diff options
author | Ted Gould <ted@canonical.com> | 2009-07-26 23:47:27 -0500 |
---|---|---|
committer | Ted Gould <ted@canonical.com> | 2009-07-26 23:47:27 -0500 |
commit | 9b7303cd469357b2d17cb87313d83d47ffc16470 (patch) | |
tree | 3248af69a8fb6f5791ff48aa1cd30d24146ec556 | |
parent | f5cec7c3e6dc602772642bd390ec632450709145 (diff) | |
parent | 0417e7049a32ceb45e19705992dfccef5d858893 (diff) | |
download | ayatana-indicator-session-9b7303cd469357b2d17cb87313d83d47ffc16470.tar.gz ayatana-indicator-session-9b7303cd469357b2d17cb87313d83d47ffc16470.tar.bz2 ayatana-indicator-session-9b7303cd469357b2d17cb87313d83d47ffc16470.zip |
Merging in the branch to port everything to libindicator and fleshing thigns out.
28 files changed, 2435 insertions, 38 deletions
@@ -14,3 +14,17 @@ debian/indicator-messages.postrm.debhelper debian/indicator-messages.substvars debian/stamp-autotools-files debian/stamp-makefile-build +libstatus-users-session.la +libstatus_users_session_la-indicator-fusa.lo +libstatus_users_session_la-indicator-sus.lo +indicator-session-service +indicator-status-service +indicator-users-service +indicator-session.service +indicator-status.service +indicator-users.service +status-service-client.h +status-service-server.h +gtk-logout-helper +.deps +.libs diff --git a/Makefile.am b/Makefile.am index f0f2247..7d58c6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,7 @@ SUBDIRS = \ - src + src \ + data + +DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall @@ -1,6 +1,6 @@ #!/bin/sh -PKG_NAME="indicator-fusa" +PKG_NAME="indicator-sus" which gnome-autogen.sh || { echo "You need gnome-common from GNOME SVN" diff --git a/configure.ac b/configure.ac index ec22ca2..1e65958 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ -AC_INIT(src/indicator-fusa.c) +AC_INIT(src/indicator-sus.c) AC_PREREQ(2.53) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(indicator-fusa, 0.1) +AM_INIT_AUTOMAKE(indicator-sus, 0.1dev) AM_MAINTAINER_MODE @@ -22,12 +22,67 @@ AC_CONFIG_MACRO_DIR([m4]) ########################### GTK_REQUIRED_VERSION=2.12 -PANEL_REQUIRED_VERSION=2.0.0 +INDICATOR_REQUIRED_VERSION=0.2.0 +DBUSMENUGTK_REQUIRED_VERSION=0.0.0 -PKG_CHECK_MODULES(APPLET, gtk+-2.0 >= $GTK_REQUIRED_VERSION) +PKG_CHECK_MODULES(APPLET, gtk+-2.0 >= $GTK_REQUIRED_VERSION + indicator >= $INDICATOR_REQUIRED_VERSION + dbusmenu-gtk >= $DBUSMENUGTK_REQUIRED_VERSION) AC_SUBST(APPLET_CFLAGS) AC_SUBST(APPLET_LIBS) +DBUSMENUGLIB_REQUIRED_VERSION=0.0.0 + +PKG_CHECK_MODULES(STATUSSERVICE, dbusmenu-glib >= $DBUSMENUGLIB_REQUIRED_VERSION) + +AC_SUBST(STATUSERVICE_CFLAGS) +AC_SUBST(STATUSERVICE_LIBS) + +PKG_CHECK_MODULES(USERSSERVICE, dbusmenu-glib >= $DBUSMENUGLIB_REQUIRED_VERSION) + +AC_SUBST(USERSERVICE_CFLAGS) +AC_SUBST(USERSERVICE_LIBS) + +PKG_CHECK_MODULES(SESSIONSERVICE, dbusmenu-glib >= $DBUSMENUGLIB_REQUIRED_VERSION) + +AC_SUBST(SESSIONERVICE_CFLAGS) +AC_SUBST(SESSIONERVICE_LIBS) + +PKG_CHECK_MODULES(GTKLOGOUTHELPER, gtk+-2.0 >= $GTK_REQUIRED_VERSION + polkit-gnome) + +AC_SUBST(GTKLOGOUTHELPER_CFLAGS) +AC_SUBST(GTKLOGOUTHELPER_LIBS) + +########################### +# Check to see if we're local +########################### + +with_localinstall="no" +AC_ARG_ENABLE(localinstall, AS_HELP_STRING([--enable-localinstall], [install all of the files localy instead of system directories (for distcheck)]), with_localinstall=$enableval, with_localinstall=no) + +########################### +# Indicator Info +########################### + +if test "x$with_localinstall" = "xyes"; then + INDICATORDIR="${libdir}/indicators/2/" +else + INDICATORDIR=`$PKG_CONFIG --variable=indicatordir indicator` +fi +AC_SUBST(INDICATORDIR) + +########################### +# DBus Service Info +########################### + +if test "x$with_localinstall" = "xyes"; then + DBUSSERVICEDIR="${datadir}/dbus-1/services/" +else + DBUSSERVICEDIR=`$PKG_CONFIG --variable=session_bus_services_dir dbus-1` +fi +AC_SUBST(DBUSSERVICEDIR) + ########################### # Files ########################### @@ -35,6 +90,8 @@ AC_SUBST(APPLET_LIBS) AC_OUTPUT([ Makefile src/Makefile +src/gtk-dialog/Makefile +data/Makefile ]) ########################### @@ -43,7 +100,7 @@ src/Makefile AC_MSG_NOTICE([ -FUSA Indicator Configuration: +SUS Indicator Configuration: Prefix: $prefix ]) diff --git a/data/Makefile.am b/data/Makefile.am new file mode 100644 index 0000000..e003b0e --- /dev/null +++ b/data/Makefile.am @@ -0,0 +1,16 @@ + +dbus_servicesdir = $(DBUSSERVICEDIR) +service_in_files = indicator-status.service.in \ + indicator-users.service.in \ + indicator-session.service.in +dbus_services_DATA = $(service_in_files:.service.in=.service) + +%.service: %.service.in + sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + +#$(dbus_services_DATA): $(service_in_files) Makefile +# sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@ + +EXTRA_DIST = $(service_in_files) + +CLEANFILES = $(dbus_services_DATA) diff --git a/data/indicator-session.service.in b/data/indicator-session.service.in new file mode 100644 index 0000000..50d8635 --- /dev/null +++ b/data/indicator-session.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.ayatana.indicator.session +Exec=@libexecdir@/indicator-session-service diff --git a/data/indicator-status.service.in b/data/indicator-status.service.in new file mode 100644 index 0000000..e920bf2 --- /dev/null +++ b/data/indicator-status.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.ayatana.indicator.status +Exec=@libexecdir@/indicator-status-service diff --git a/data/indicator-users.service.in b/data/indicator-users.service.in new file mode 100644 index 0000000..2dc9062 --- /dev/null +++ b/data/indicator-users.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.ayatana.indicator.users +Exec=@libexecdir@/indicator-users-service diff --git a/src/Makefile.am b/src/Makefile.am index c29d179..ab79f0f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,74 @@ +SUBDIRS = gtk-dialog -fusalibdir = $(libdir)/indicators/1 -fusalib_LTLIBRARIES = libfusa.la -libfusa_la_SOURCES = indicator-fusa.c -libfusa_la_CFLAGS = $(APPLET_CFLAGS) -libfusa_la_LIBADD = $(APPLET_LIBS) +libexec_PROGRAMS = indicator-status-service indicator-users-service indicator-session-service + +################### +# Indicator Stuff +################### + +status_users_sessionlibdir = $(INDICATORDIR) +status_users_sessionlib_LTLIBRARIES = libstatus-users-session.la +libstatus_users_session_la_SOURCES = \ + indicator-sus.c \ + dbus-shared-names.h \ + status-service-client.h +libstatus_users_session_la_CFLAGS = $(APPLET_CFLAGS) -Wall -Werror +libstatus_users_session_la_LIBADD = $(APPLET_LIBS) +libstatus_users_session_la_LDFLAGS = -module -avoid-version + +################ +# Status Stuff +################ + +indicator_status_service_SOURCES = \ + status-service.c \ + status-service-dbus.h \ + status-service-dbus.c \ + status-service-server.h \ + status-provider.h \ + status-provider.c \ + status-provider-pidgin.h \ + status-provider-pidgin.c +indicator_status_service_CFLAGS = $(STATUSSERVICE_CFLAGS) +indicator_status_service_LDADD = $(STATUSSERVICE_LIBS) + +status-service-client.h: status-service.xml + dbus-binding-tool \ + --prefix=_status_service_client \ + --mode=glib-client \ + --output=status-service-client.h \ + $(srcdir)/status-service.xml + +status-service-server.h: status-service.xml + dbus-binding-tool \ + --prefix=_status_service_server \ + --mode=glib-server \ + --output=status-service-server.h \ + $(srcdir)/status-service.xml + +############### +# Users Stuff +############### + +indicator_users_service_SOURCES = users-service.c +indicator_users_service_CFLAGS = $(USERSSERVICE_CFLAGS) +indicator_users_service_LDADD = $(USERSSERVICE_LIBS) + +################# +# Session Stuff +################# + +indicator_session_service_SOURCES = session-service.c +indicator_session_service_CFLAGS = $(SESSIONSERVICE_CFLAGS) -DLIBEXECDIR=\"$(libexecdir)\" +indicator_session_service_LDADD = $(SESSIONSERVICE_LIBS) + +############### +# Other Stuff +############### + +BUILT_SOURCES = \ + status-service-client.h \ + status-service-server.h + +EXTRA_DIST = \ + status-service.xml diff --git a/src/dbus-shared-names.h b/src/dbus-shared-names.h new file mode 100644 index 0000000..bd2f8ab --- /dev/null +++ b/src/dbus-shared-names.h @@ -0,0 +1,14 @@ + +#ifndef __DBUS_SHARED_NAMES_H__ +#define __DBUS_SHARED_NAMES_H__ 1 + +#define INDICATOR_STATUS_DBUS_NAME "org.ayatana.indicator.status" +#define INDICATOR_STATUS_DBUS_OBJECT "/org/ayatana/indicator/status" + +#define INDICATOR_USERS_DBUS_NAME "org.ayatana.indicator.users" +#define INDICATOR_USERS_DBUS_OBJECT "/org/ayatana/indicator/users" + +#define INDICATOR_SESSION_DBUS_NAME "org.ayatana.indicator.session" +#define INDICATOR_SESSION_DBUS_OBJECT "/org/ayatana/indicator/session" + +#endif /* __DBUS_SHARED_NAMES_H__ */ diff --git a/src/gtk-dialog/Makefile.am b/src/gtk-dialog/Makefile.am new file mode 100644 index 0000000..9aa0097 --- /dev/null +++ b/src/gtk-dialog/Makefile.am @@ -0,0 +1,13 @@ + +libexec_PROGRAMS = gtk-logout-helper + +gtk_logout_helper_SOURCES = \ + gtk-logout-helper.c \ + ck-pk-helper.c \ + ck-pk-helper.h \ + logout-dialog.c \ + logout-dialog.h + +gtk_logout_helper_CFLAGS = $(GTKLOGOUTHELPER_CFLAGS) -Wall -Werror +gtk_logout_helper_LDADD = $(GTKLOGOUTHELPER_LIBS) + diff --git a/src/gtk-dialog/ck-pk-helper.c b/src/gtk-dialog/ck-pk-helper.c new file mode 100644 index 0000000..9278905 --- /dev/null +++ b/src/gtk-dialog/ck-pk-helper.c @@ -0,0 +1,214 @@ + +#include <unistd.h> +#include <glib.h> +#include <dbus/dbus-glib.h> +#include <polkit-gnome/polkit-gnome.h> + +#include "logout-dialog.h" +#include "ck-pk-helper.h" + +static gboolean +ck_multiple_users (void) +{ + DBusGConnection * sbus = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); + g_return_val_if_fail(sbus != NULL, TRUE); /* worst case */ + DBusGProxy * proxy = dbus_g_proxy_new_for_name(sbus, "org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager"); + + if (proxy == NULL) { + return TRUE; + } + + gboolean result; + GPtrArray * seats = NULL; + + result = dbus_g_proxy_call(proxy, "GetSeats", NULL, G_TYPE_INVALID, + dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &seats, G_TYPE_INVALID); + + if (!result) { + g_warning("Unable to get the seats for ConsoleKit"); + g_object_unref(proxy); + return TRUE; + } + + gchar * this_session_id = NULL; + + result = dbus_g_proxy_call(proxy, "GetCurrentSession", NULL, G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &this_session_id, G_TYPE_INVALID); + + g_object_unref(proxy); + + if (!result) { + g_warning("Unable to get current session from ConsoleKit"); + return TRUE; + } + + proxy = dbus_g_proxy_new_for_name(sbus, "org.freedesktop.ConsoleKit", + this_session_id, "org.freedesktop.ConsoleKit.Session"); + + if (proxy == NULL) { + return TRUE; + } + + guint this_session_uid; + + result = dbus_g_proxy_call(proxy, "GetUnixUser", NULL, G_TYPE_INVALID, + G_TYPE_UINT, &this_session_uid, G_TYPE_INVALID); + + if (!result) { + g_warning("Unable to get UID from ConsoleKit"); + return TRUE; + } + + guint seat; + gboolean multiple_users = FALSE; + for (seat = 0; seat < seats->len; seat++) { + gchar * seat_id = g_ptr_array_index(seats, seat); + DBusGProxy * seat_proxy = dbus_g_proxy_new_for_name(sbus, "org.freedesktop.ConsoleKit", + seat_id, "org.freedesktop.ConsoleKit.Seat"); + g_free(seat_id); + + if (seat_proxy == NULL) { + continue; + } + + GPtrArray * sessions = NULL; + + gboolean result = dbus_g_proxy_call(seat_proxy, + "GetSessions", NULL, G_TYPE_INVALID, + dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions, G_TYPE_INVALID); + + g_object_unref(seat_proxy); + if (!result) { + continue; + } + + guint session; + for (session = 0; session < sessions->len; session++) { + gchar * session_id = g_ptr_array_index(sessions, session); + if (g_strcmp0(this_session_id, session_id) == 0) { + continue; + } + DBusGProxy * session_proxy = dbus_g_proxy_new_for_name(sbus, "org.freedesktop.ConsoleKit", + session_id, "org.freedesktop.ConsoleKit.Session"); + g_free(session_id); + + if (session_proxy == NULL) { + continue; + } + + guint session_uid; + result = dbus_g_proxy_call(session_proxy, "GetUnixUser", NULL, G_TYPE_INVALID, + G_TYPE_UINT, &session_uid, G_TYPE_INVALID); + g_object_unref(session_proxy); + + if (!result) { + continue; + } + + if (session_uid != this_session_uid) { + multiple_users = TRUE; + break; + } + } + + g_ptr_array_free(sessions, TRUE); + + if (multiple_users) { + break; + } + } + + g_ptr_array_free(seats, TRUE); + g_object_unref(proxy); + g_free(this_session_id); + + return multiple_users; +} + +gboolean +pk_require_auth (LogoutDialogAction action) { + if (action == LOGOUT_DIALOG_LOGOUT) { + return FALSE; + } + + gchar * pk_action; + if (ck_multiple_users()) { + if (action == LOGOUT_DIALOG_RESTART) { + pk_action = "org.freedesktop.consolekit.system.restart-multiple-users"; + } else { + pk_action = "org.freedesktop.consolekit.system.stop-multiple-users"; + } + } else { + if (action == LOGOUT_DIALOG_RESTART) { + pk_action = "org.freedesktop.consolekit.system.restart"; + } else { + pk_action = "org.freedesktop.consolekit.system.stop"; + } + } + + PolKitResult polres; + if (pk_can_do_action(pk_action, &polres)) { + if (polres == POLKIT_RESULT_YES) { + return FALSE; + } + return TRUE; + } + return FALSE; +} + +gboolean +pk_can_do_action (const gchar *action_id, PolKitResult * pol_result) +{ + PolKitGnomeContext *gnome_context; + PolKitAction *action; + PolKitCaller *caller; + DBusError dbus_error; + PolKitError *error; + PolKitResult result; + + gnome_context = polkit_gnome_context_get (NULL); + + if (gnome_context == NULL) { + return FALSE; + } + + if (gnome_context->pk_tracker == NULL) { + return FALSE; + } + + dbus_error_init (&dbus_error); + caller = polkit_tracker_get_caller_from_pid (gnome_context->pk_tracker, + getpid (), + &dbus_error); + dbus_error_free (&dbus_error); + + if (caller == NULL) { + return FALSE; + } + + action = polkit_action_new (); + if (!polkit_action_set_action_id (action, action_id)) { + polkit_action_unref (action); + polkit_caller_unref (caller); + return FALSE; + } + + result = POLKIT_RESULT_UNKNOWN; + error = NULL; + result = polkit_context_is_caller_authorized (gnome_context->pk_context, + action, caller, FALSE, + &error); + if (polkit_error_is_set (error)) { + polkit_error_free (error); + } + polkit_action_unref (action); + polkit_caller_unref (caller); + + if (pol_result != NULL) { + *pol_result = result; + } + + return result != POLKIT_RESULT_NO && result != POLKIT_RESULT_UNKNOWN; +} diff --git a/src/gtk-dialog/ck-pk-helper.h b/src/gtk-dialog/ck-pk-helper.h new file mode 100644 index 0000000..fb5936e --- /dev/null +++ b/src/gtk-dialog/ck-pk-helper.h @@ -0,0 +1,10 @@ + +#ifndef __CK_PK_HELPER_H__ +#define __CK_PK_HELPER_H__ 1 + +#include <polkit-gnome/polkit-gnome.h> + +gboolean pk_require_auth (LogoutDialogAction action); +gboolean pk_can_do_action (const gchar *action_id, PolKitResult * pol_result); + +#endif /* __CK_PK_HELPER__ */ diff --git a/src/gtk-dialog/gtk-logout-helper.c b/src/gtk-dialog/gtk-logout-helper.c new file mode 100644 index 0000000..75ab63f --- /dev/null +++ b/src/gtk-dialog/gtk-logout-helper.c @@ -0,0 +1,134 @@ + +#include <glib.h> +#include <gtk/gtk.h> +#include <dbus/dbus-glib.h> +#include "logout-dialog.h" +#include "ck-pk-helper.h" + +static void +session_action (LogoutDialogAction action) +{ + DBusGConnection * sbus; + DBusGProxy * sm_proxy; + GError * error = NULL; + gboolean res = FALSE; + + sbus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + if (sbus == NULL) { + g_warning("Unable to get DBus session bus."); + return; + } + sm_proxy = dbus_g_proxy_new_for_name_owner (sbus, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + "org.gnome.SessionManager", + &error); + if (sm_proxy == NULL) { + g_warning("Unable to get DBus proxy to SessionManager interface: %s", error->message); + g_error_free(error); + return; + } + + g_clear_error (&error); + + if (action == LOGOUT_DIALOG_LOGOUT) { + res = dbus_g_proxy_call_with_timeout (sm_proxy, "Logout", INT_MAX, &error, + G_TYPE_UINT, 1, G_TYPE_INVALID, G_TYPE_INVALID); + } else if (action == LOGOUT_DIALOG_SHUTDOWN) { + res = dbus_g_proxy_call_with_timeout (sm_proxy, "RequestShutdown", INT_MAX, &error, + G_TYPE_INVALID, G_TYPE_INVALID); + } else if (action == LOGOUT_DIALOG_RESTART) { + res = dbus_g_proxy_call_with_timeout (sm_proxy, "RequestReboot", INT_MAX, &error, + G_TYPE_INVALID, G_TYPE_INVALID); + } else { + g_warning ("Unknown session action"); + } + + if (!res) { + if (error != NULL) { + g_warning ("SessionManager action failed: %s", error->message); + } else { + g_warning ("SessionManager action failed: unknown error"); + } + } + + g_object_unref(sm_proxy); + + if (error != NULL) { + g_error_free(error); + } + + return; +} + +static LogoutDialogAction type = LOGOUT_DIALOG_LOGOUT; + +static gboolean +option_logout (const gchar * arg, const gchar * value, gpointer data, GError * error) +{ + type = LOGOUT_DIALOG_LOGOUT; + return TRUE; +} + +static gboolean +option_shutdown (const gchar * arg, const gchar * value, gpointer data, GError * error) +{ + type = LOGOUT_DIALOG_SHUTDOWN; + return TRUE; +} + +static gboolean +option_restart (const gchar * arg, const gchar * value, gpointer data, GError * error) +{ + type = LOGOUT_DIALOG_RESTART; + return TRUE; +} + +static GOptionEntry options[] = { + {"logout", 'l', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_logout, "Log out of the current session", NULL}, + {"shutdown", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_shutdown, "Shutdown the entire system", NULL}, + {"restart", 'r', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_restart, "Restart the system", NULL}, + + {NULL} +}; + +int +main (int argc, char * argv[]) +{ + gtk_init(&argc, &argv); + + GError * error = NULL; + GOptionContext * context = g_option_context_new(" - logout of the current session"); + g_option_context_add_main_entries(context, options, "gtk-logout-helper"); + g_option_context_add_group(context, gtk_get_option_group(TRUE)); + g_option_context_set_help_enabled(context, TRUE); + + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_debug("Option parsing failed: %s", error->message); + g_error_free(error); + return 1; + } + + GtkWidget * dialog = NULL; + if (!pk_require_auth(type)) { + dialog = logout_dialog_new(type); + } + + if (dialog != NULL) { + GtkResponseType response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_hide(dialog); + + if (response == GTK_RESPONSE_HELP) { + type = LOGOUT_DIALOG_RESTART; + response = GTK_RESPONSE_OK; + } + + if (response != GTK_RESPONSE_OK) { + return 0; + } + } + + session_action(type); + + return 0; +} diff --git a/src/gtk-dialog/logout-dialog.c b/src/gtk-dialog/logout-dialog.c new file mode 100644 index 0000000..cd49b00 --- /dev/null +++ b/src/gtk-dialog/logout-dialog.c @@ -0,0 +1,367 @@ +/* + * libgksuui -- Gtk+ widget and convenience functions for requesting passwords + * Copyright (C) 2004 Gustavo Noronha Silva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <math.h> + +#include <gtk/gtk.h> +#include <gdk/gdkx.h> +#include <glib/gi18n.h> +#include <X11/XKBlib.h> + +#include "logout-dialog.h" +#include "ck-pk-helper.h" + +enum { + PROP_ZERO, + PROP_ACTION +}; + + +static void +logout_dialog_class_init (LogoutDialogClass *klass); + +static void +logout_dialog_init (LogoutDialog *logout_dialog); + +static void +set_property (GObject * object, guint param_id, const GValue * value, GParamSpec *pspec); + +static void +get_property (GObject * object, guint param_id, GValue * value, GParamSpec *pspec); + +static gboolean +timer_cb (gpointer data); + +static void +show_cb (GtkWidget * widget, gpointer data); + +static void +check_restart (LogoutDialog * dialog); + +static gchar* +get_plural_string (LogoutDialog * dialog); + +static const gchar * title_strings[LOGOUT_DIALOG_ACTION_CNT] = { + /* LOGOUT_DIALOG_LOGOUT, */ NC_("title", "Log Out"), + /* LOGOUT_DIALOG_RESTART, */ NC_("title", "Restart"), + /* LOGOUT_DIALOG_SHUTDOWN, */ NC_("title", "Shut Down") +}; + +static const gchar * button_strings[LOGOUT_DIALOG_ACTION_CNT] = { + /* LOGOUT_DIALOG_LOGOUT, */ NC_("button", "Log Out"), + /* LOGOUT_DIALOG_RESTART, */ NC_("button", "Restart"), + /* LOGOUT_DIALOG_SHUTDOWN, */ NC_("button", "Shut Down") +}; + +static const gchar * restart_auth = N_("Restart..."); + +static const gchar * body_logout_update = N_("You recently installed updates which will only take effect after a restart. Restart to apply software updates."); + +static const gchar * icon_strings[LOGOUT_DIALOG_ACTION_CNT] = { + /* LOGOUT_DIALOG_LOGOUT, */ "system-log-out", + /* LOGOUT_DIALOG_RESTART, */ "system-restart", + /* LOGOUT_DIALOG_SHUTDOWN, */ "system-shutdown" +}; + +GType +logout_dialog_get_type (void) +{ + static GType type = 0; + + if (type == 0) + { + static const GTypeInfo info = + { + sizeof (LogoutDialogClass), /* size of class */ + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) logout_dialog_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LogoutDialog), /* size of object */ + 0, /* n_preallocs */ + (GInstanceInitFunc) logout_dialog_init /* instance_init */ + }; + type = g_type_register_static (gtk_dialog_get_type (), + "LogoutDialogType", + &info, 0); + } + + return type; +} + +static gchar* +get_plural_string (LogoutDialog * dialog) +{ + static gchar *plural_string = ""; + + switch (dialog->action) + { + case LOGOUT_DIALOG_LOGOUT: + plural_string = ngettext("You will be logged out in %d second.", + "You will be logged out in %d seconds.", + dialog->timeout); + break; + case LOGOUT_DIALOG_RESTART: + plural_string = ngettext("The computer will restart in %d second.", + "The computer will restart in %d seconds.", + dialog->timeout); + break; + case LOGOUT_DIALOG_SHUTDOWN: + plural_string = ngettext("The computer will be shut down in %d second.", + "The computer will be shut down in %d seconds.", + dialog->timeout); + break; + default: + break; + } + + return plural_string; +} + +static void +logout_dialog_class_init (LogoutDialogClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + + g_object_class_install_property(gobject_class, PROP_ACTION, + g_param_spec_int("action", NULL, NULL, + LOGOUT_DIALOG_LOGOUT, LOGOUT_DIALOG_SHUTDOWN, + LOGOUT_DIALOG_LOGOUT, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + + return; +} + +static void +set_property (GObject * object, guint param_id, const GValue * value, GParamSpec *pspec) +{ + g_return_if_fail(param_id == PROP_ACTION); + + LogoutDialog * dialog = LOGOUT_DIALOG(object); + dialog->action = (LogoutDialogAction)g_value_get_int(value); + + gtk_image_set_from_icon_name(GTK_IMAGE(dialog->image), icon_strings[dialog->action], GTK_ICON_SIZE_DIALOG); + gtk_window_set_title (GTK_WINDOW(dialog), _(title_strings[dialog->action])); + gtk_widget_hide(dialog->message); + gtk_button_set_label(GTK_BUTTON(dialog->ok_button), _(button_strings[dialog->action])); + + gchar * timeouttxt = g_strdup_printf(get_plural_string(dialog), dialog->timeout); + gtk_label_set_text(GTK_LABEL(dialog->timeout_text), timeouttxt); + g_free(timeouttxt); + + check_restart(dialog); + + return; +} + +static void +get_property (GObject * object, guint param_id, GValue * value, GParamSpec *pspec) +{ + g_return_if_fail(param_id == PROP_ACTION); + g_value_set_int(value, LOGOUT_DIALOG(object)->action); +} + +static gboolean +timer_cb (gpointer data) +{ + LogoutDialog * dialog = LOGOUT_DIALOG(data); + + if (dialog->timeout == 0) { + gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + dialog->timerfunc = 0; + return FALSE; + } else { + dialog->timeout--; + + gchar * timeouttxt = g_strdup_printf(get_plural_string(dialog), dialog->timeout); + gtk_label_set_text(GTK_LABEL(dialog->timeout_text), timeouttxt); + g_free(timeouttxt); + } + + return TRUE; +} + +static void +show_cb (GtkWidget * widget, gpointer data) +{ + LogoutDialog * dialog = LOGOUT_DIALOG(widget); + + if (dialog->timerfunc != 0) { + g_source_remove(dialog->timerfunc); + dialog->timerfunc = 0; + } + + dialog->timerfunc = g_timeout_add_seconds(1, timer_cb, dialog); + return; +} + +static void +check_restart (LogoutDialog * dialog) +{ + if (dialog->action != LOGOUT_DIALOG_LOGOUT) { + return; + } + + if (g_file_test("/var/run/reboot-required", G_FILE_TEST_EXISTS)) { + if (pk_can_do_action("org.freedesktop.consolekit.system.restart", NULL) || + pk_can_do_action("org.freedesktop.consolekit.system.restart-multiple-users", NULL)) { + + gtk_label_set_text(GTK_LABEL(dialog->message), _(body_logout_update)); + gtk_widget_show(dialog->message); + if (pk_require_auth(LOGOUT_DIALOG_RESTART)) { + gtk_button_set_label(GTK_BUTTON(dialog->restart_button), _(restart_auth)); + } else { + gtk_button_set_label(GTK_BUTTON(dialog->restart_button), _(button_strings[LOGOUT_DIALOG_RESTART])); + } + gtk_widget_show(dialog->restart_button); + } + } + + return; +} + +static gboolean +focus_out_cb (GtkWidget *widget, GdkEventFocus *event, gpointer user_data) +{ + gtk_window_present (GTK_WINDOW(widget)); + return TRUE; +} + +static void +logout_dialog_init (LogoutDialog *logout_dialog) +{ + GtkDialog *dialog; + gint border_width = 6; + + logout_dialog->timeout = 60; + logout_dialog->timerfunc = 0; + + /* dialog window */ + dialog = GTK_DIALOG(logout_dialog); + + /* make sure that our window will always have the focus */ + g_signal_connect (G_OBJECT(dialog), "focus-out-event", + G_CALLBACK(focus_out_cb), NULL); + + logout_dialog->main_vbox = dialog->vbox; + + gtk_window_set_title (GTK_WINDOW(logout_dialog), ""); + gtk_dialog_set_has_separator (GTK_DIALOG(logout_dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER(logout_dialog), border_width); + gtk_box_set_spacing (GTK_BOX(logout_dialog->main_vbox), 12); + gtk_window_set_resizable (GTK_WINDOW(logout_dialog), FALSE); + + gtk_window_stick(GTK_WINDOW(logout_dialog)); + gtk_window_set_keep_above(GTK_WINDOW(logout_dialog), TRUE); + gtk_widget_realize(GTK_WIDGET(logout_dialog)); + gdk_window_set_functions(GTK_WIDGET(logout_dialog)->window, GDK_FUNC_CLOSE); + + /* center window */ + gtk_window_set_position (GTK_WINDOW(logout_dialog), GTK_WIN_POS_CENTER); + + /* the action buttons */ + /* the cancel button */ + logout_dialog->restart_button = gtk_dialog_add_button (dialog, + GTK_STOCK_HELP, + GTK_RESPONSE_HELP); + gtk_button_set_label(GTK_BUTTON(logout_dialog->restart_button), _(button_strings[LOGOUT_DIALOG_RESTART])); + gtk_widget_hide(logout_dialog->restart_button); + + /* the cancel button */ + logout_dialog->cancel_button = gtk_dialog_add_button (dialog, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL); + /* the ok button */ + logout_dialog->ok_button = gtk_dialog_add_button (dialog, + GTK_STOCK_OK, + GTK_RESPONSE_OK); + gtk_widget_grab_default (logout_dialog->ok_button); + + /* Title */ + gtk_window_set_title (GTK_WINDOW(logout_dialog), _(title_strings[logout_dialog->action])); + + /* hbox */ + logout_dialog->hbox = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER(logout_dialog->hbox), 6); + gtk_box_pack_start (GTK_BOX(logout_dialog->main_vbox), + logout_dialog->hbox, FALSE, FALSE, 0); + gtk_widget_show (logout_dialog->hbox); + + /* image */ + logout_dialog->image = + gtk_image_new_from_icon_name (icon_strings[logout_dialog->action], + GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC(logout_dialog->image), 0.5, 0); + gtk_box_pack_start (GTK_BOX(logout_dialog->hbox), logout_dialog->image, + FALSE, FALSE, 0); + gtk_widget_show (logout_dialog->image); + + /* vbox for text */ + logout_dialog->vbox_text = gtk_vbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(logout_dialog->hbox), logout_dialog->vbox_text, TRUE, TRUE, 0); + gtk_widget_show(logout_dialog->vbox_text); + + /* Message */ + logout_dialog->message = gtk_label_new(""); + gtk_label_set_line_wrap(GTK_LABEL(logout_dialog->message), TRUE); + gtk_label_set_single_line_mode(GTK_LABEL(logout_dialog->message), FALSE); + gtk_label_set_selectable(GTK_LABEL(logout_dialog->message), TRUE); + gtk_misc_set_alignment (GTK_MISC(logout_dialog->message), 0.0, 0.0); + gtk_box_pack_start(GTK_BOX(logout_dialog->vbox_text), logout_dialog->message, TRUE, TRUE, 0); + gtk_widget_show(logout_dialog->message); + + /* timeout */ + logout_dialog->timeout_text = gtk_label_new(""); + gtk_label_set_line_wrap(GTK_LABEL(logout_dialog->timeout_text), TRUE); + gtk_label_set_single_line_mode(GTK_LABEL(logout_dialog->timeout_text), FALSE); + gtk_label_set_selectable(GTK_LABEL(logout_dialog->timeout_text), FALSE); + gtk_misc_set_alignment (GTK_MISC(logout_dialog->timeout_text), 0.0, 0.5); + gtk_box_pack_start(GTK_BOX(logout_dialog->vbox_text), logout_dialog->timeout_text, TRUE, TRUE, 0); + gtk_widget_show(logout_dialog->timeout_text); + + g_signal_connect(G_OBJECT(logout_dialog), "show", G_CALLBACK(show_cb), logout_dialog); + + return; +} + +/** + * logout_dialog_new: + * + * Creates a new #LogoutDialog. + * + * Returns: the new #LogoutDialog + */ +GtkWidget* +logout_dialog_new (LogoutDialogAction action) +{ + LogoutDialog * dialog = g_object_new (LOGOUT_TYPE_DIALOG, "action", action, NULL); + return GTK_WIDGET(dialog); +} + +LogoutDialogAction +logout_dialog_get_action (LogoutDialog * dialog) +{ + return dialog->action; +} + diff --git a/src/gtk-dialog/logout-dialog.h b/src/gtk-dialog/logout-dialog.h new file mode 100644 index 0000000..b0e19b7 --- /dev/null +++ b/src/gtk-dialog/logout-dialog.h @@ -0,0 +1,99 @@ +/* + * libgksuui -- Gtk+ widget and convenience functions for requesting passwords + * Copyright (C) 2004 Gustavo Noronha Silva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __LOGOUT_DIALOG_H__ +#define __LOGOUT_DIALOG_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define LOGOUT_TYPE_DIALOG (logout_dialog_get_type ()) +#define LOGOUT_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), LOGOUT_TYPE_DIALOG, LogoutDialog)) +#define LOGOUT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), LOGOUT_TYPE_DIALOG, LogoutDialogClass)) +#define LOGOUT_IS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), LOGOUT_TYPE_DIALOG)) +#define LOGOUT_IS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LOGOUT_TYPE_CONTEXT)) +#define LOGOUT_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LOGOUT_TYPE_DIALOG, LogoutDialogClass)) + +typedef struct _LogoutDialogClass LogoutDialogClass; +typedef struct _LogoutDialog LogoutDialog; +typedef enum _LogoutDialogAction LogoutDialogAction; + +enum _LogoutDialogAction { + LOGOUT_DIALOG_LOGOUT, + LOGOUT_DIALOG_RESTART, + LOGOUT_DIALOG_SHUTDOWN, + LOGOUT_DIALOG_ACTION_CNT +}; + +struct _LogoutDialogClass +{ + GtkDialogClass parent_class; +}; + +/** + * LogoutDialog: + * @dialog: parent widget + * @main_vbox: GtkDialog's vbox + * @hbox: box to separate the image of the right-side widgets + * @image: the authorization image, left-side widget + * @entry_vbox: right-side widgets container + * @label: message describing what is required from the user, + * right-side widget + * @entry: place to type the password in, right-side widget + * @ok_button: OK button of the dialog + * @cancel_button: Cancel button of the dialog + * + * Convenience widget based on #GtkDialog to request a password. + */ +struct _LogoutDialog +{ + GtkDialog dialog; + + GtkWidget *main_vbox; + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *ok_button; + GtkWidget *cancel_button; + GtkWidget *restart_button; + GtkWidget *vbox_text; + GtkWidget *message; + GtkWidget *timeout_text; + + LogoutDialogAction action; + + /* private */ + gchar * timeout_result; + guint timeout; + guint timerfunc; +}; + +GType +logout_dialog_get_type (void); + +GtkWidget* +logout_dialog_new (LogoutDialogAction action); + +LogoutDialogAction +logout_dialog_get_action (LogoutDialog * widget); + +G_END_DECLS + +#endif diff --git a/src/indicator-fusa.c b/src/indicator-fusa.c deleted file mode 100644 index 21c0817..0000000 --- a/src/indicator-fusa.c +++ /dev/null @@ -1,26 +0,0 @@ - -#include <gtk/gtk.h> - -GtkWidget * -get_menu_item (void) -{ - GtkWidget * mainmenu = gtk_menu_item_new(); - - GtkWidget * hbox = gtk_hbox_new(FALSE, 3); - - GtkWidget * label = gtk_label_new("Ted Gould"); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 3); - gtk_widget_show(label); - - GtkWidget * icon = gtk_image_new_from_icon_name("gnome-logout", - GTK_ICON_SIZE_MENU); - gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0); - gtk_widget_show(icon); - - gtk_container_add(GTK_CONTAINER(mainmenu), hbox); - gtk_widget_show(hbox); - - gtk_widget_show(mainmenu); - return mainmenu; -} - diff --git a/src/indicator-sus.c b/src/indicator-sus.c new file mode 100644 index 0000000..18b6c44 --- /dev/null +++ b/src/indicator-sus.c @@ -0,0 +1,378 @@ + +#include <gtk/gtk.h> +#include <libdbusmenu-gtk/client.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> + +#include <libindicator/indicator.h> +INDICATOR_SET_VERSION +INDICATOR_SET_NAME("users-status-session") + +#include "dbus-shared-names.h" +#include "status-service-client.h" + +static DbusmenuGtkClient * status_client = NULL; +static DbusmenuGtkClient * users_client = NULL; +static DbusmenuGtkClient * session_client = NULL; + +static GtkMenu * main_menu = NULL; + +static GtkWidget * status_separator = NULL; +static GtkWidget * users_separator = NULL; +#define SEPARATOR_SHOWN(sep) (sep != NULL && GTK_WIDGET_VISIBLE(sep)) +static GtkWidget * loading_item = NULL; + +static DBusGConnection * connection = NULL; +static DBusGProxy * proxy = NULL; + +typedef enum { + STATUS_SECTION, + USERS_SECTION, + SESSION_SECTION, + END_OF_SECTIONS +} section_t; + +static void child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint position, gpointer section); +static guint status_menu_pos_offset (void); +static guint users_menu_pos_offset (void); +static guint session_menu_pos_offset (void); + +GtkLabel * +get_label (void) +{ + GtkLabel * returnval = GTK_LABEL(gtk_label_new("Ted Gould")); + gtk_widget_show(GTK_WIDGET(returnval)); + return returnval; +} + +GtkImage * +get_icon (void) +{ + return NULL; +} + +static void +child_added (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint position, gpointer section) +{ + DbusmenuGtkClient * client = NULL; + gchar * errorstr = NULL; + guint (*posfunc) (void) = NULL; + + switch (GPOINTER_TO_UINT(section)) { + case STATUS_SECTION: + client = status_client; + errorstr = "Status"; + posfunc = status_menu_pos_offset; + break; + case USERS_SECTION: + client = users_client; + errorstr = "Users"; + posfunc = users_menu_pos_offset; + break; + case SESSION_SECTION: + client = session_client; + errorstr = "Session"; + posfunc = session_menu_pos_offset; + break; + default: + g_warning("Child Added called with an unknown position function!"); + return; + } + + position += posfunc(); + g_debug("SUS: Adding child: %d", position); + GtkMenuItem * widget = dbusmenu_gtkclient_menuitem_get(client, child); + + if (widget == NULL) { + g_warning("Had a menu item added to the %s menu, but yet it didn't have a GtkWidget with it. Can't add that to a menu now can we? You need to figure this @#$# out!", errorstr); + return; + } + + gtk_menu_insert(main_menu, GTK_WIDGET(widget), position); + gtk_widget_show(GTK_WIDGET(widget)); + + gtk_widget_hide(loading_item); + + return; +} + +static void +child_moved (DbusmenuMenuitem * parent, DbusmenuMenuitem * child, guint newpos, guint oldpos, guint (*posfunc) (void)) +{ + + +} + + +/* Status Menu */ +static guint +status_menu_pos_offset (void) +{ + return 0; +} + +static void +status_menu_added (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint position, gpointer user_data) +{ + gtk_widget_show(GTK_WIDGET(status_separator)); + return; +} + +static void +status_menu_removed (DbusmenuMenuitem * root, DbusmenuMenuitem * child, gpointer user_data) +{ + if (g_list_length(dbusmenu_menuitem_get_children(root)) == 0) { + gtk_widget_hide(GTK_WIDGET(status_separator)); + } + + return; +} + +static void +status_menu_root_changed(DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, GtkMenu * main) +{ + if (newroot == NULL) { + gtk_widget_hide(GTK_WIDGET(status_separator)); + return; + } + + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(child_added), GUINT_TO_POINTER(STATUS_SECTION)); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(status_menu_added), NULL); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(status_menu_removed), NULL); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(child_moved), GUINT_TO_POINTER(STATUS_SECTION)); + + GList * child = NULL; + guint count = 0; + for (child = dbusmenu_menuitem_get_children(newroot); child != NULL; child = g_list_next(child), count++) { + child_added(newroot, DBUSMENU_MENUITEM(child->data), count, GUINT_TO_POINTER(STATUS_SECTION)); + } + + if (count > 0) { + gtk_widget_show(GTK_WIDGET(status_separator)); + } + + return; +} + +static gboolean +build_status_menu (gpointer userdata) +{ + g_debug("Building Status Menu"); + guint returnval = 0; + GError * error = NULL; + + if (proxy == NULL) { + /* If we don't have DBus, let's stay in the idle loop */ + return TRUE; + } + + if (!org_freedesktop_DBus_start_service_by_name (proxy, INDICATOR_STATUS_DBUS_NAME, 0, &returnval, &error)) { + g_error("Unable to send message to DBus to start service: %s", error != NULL ? error->message : "(NULL error)" ); + g_error_free(error); + return FALSE; + } + + if (returnval != DBUS_START_REPLY_SUCCESS && returnval != DBUS_START_REPLY_ALREADY_RUNNING) { + g_error("Return value isn't indicative of success: %d", returnval); + return FALSE; + } + + status_client = dbusmenu_gtkclient_new(INDICATOR_STATUS_DBUS_NAME, INDICATOR_STATUS_DBUS_OBJECT); + g_signal_connect(G_OBJECT(status_client), DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED, G_CALLBACK(status_menu_root_changed), main_menu); + + status_separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(main_menu), status_separator); + gtk_widget_hide(status_separator); /* Should be default, I'm just being explicit. $(%*#$ hide already! */ + + return FALSE; +} + +/* Users menu */ + +static guint +users_menu_pos_offset (void) +{ + guint position = 0; + if (SEPARATOR_SHOWN(status_separator)) { + GList * location = g_list_find(GTK_MENU_SHELL(main_menu)->children, status_separator); + position = g_list_position(GTK_MENU_SHELL(main_menu)->children, location) + 1; + } + + return position; +} + +static void +users_menu_added (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint position, gpointer user_data) +{ + gtk_widget_show(GTK_WIDGET(users_separator)); + return; +} + +static void +users_menu_removed (DbusmenuMenuitem * root, DbusmenuMenuitem * child, gpointer user_data) +{ + if (g_list_length(dbusmenu_menuitem_get_children(root)) == 0) { + gtk_widget_hide(GTK_WIDGET(users_separator)); + } + + return; +} + +static void +users_menu_root_changed(DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, GtkMenu * main) +{ + if (newroot == NULL) { + gtk_widget_hide(GTK_WIDGET(users_separator)); + return; + } + + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(child_added), GUINT_TO_POINTER(USERS_SECTION)); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(users_menu_added), NULL); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(users_menu_removed), NULL); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(child_moved), GUINT_TO_POINTER(USERS_SECTION)); + + GList * child = NULL; + guint count = 0; + for (child = dbusmenu_menuitem_get_children(newroot); child != NULL; child = g_list_next(child), count++) { + child_added(newroot, DBUSMENU_MENUITEM(child->data), count, GUINT_TO_POINTER(USERS_SECTION)); + } + + if (count > 0) { + gtk_widget_show(GTK_WIDGET(users_separator)); + } + + return; +} + +static gboolean +build_users_menu (gpointer userdata) +{ + g_debug("Building Users Menu"); + guint returnval = 0; + GError * error = NULL; + + if (proxy == NULL) { + /* If we don't have DBus, let's stay in the idle loop */ + return TRUE; + } + + if (!org_freedesktop_DBus_start_service_by_name (proxy, INDICATOR_USERS_DBUS_NAME, 0, &returnval, &error)) { + g_error("Unable to send message to DBus to start service: %s", error != NULL ? error->message : "(NULL error)" ); + g_error_free(error); + return FALSE; + } + + if (returnval != DBUS_START_REPLY_SUCCESS && returnval != DBUS_START_REPLY_ALREADY_RUNNING) { + g_error("Return value isn't indicative of success: %d", returnval); + return FALSE; + } + + users_client = dbusmenu_gtkclient_new(INDICATOR_USERS_DBUS_NAME, INDICATOR_USERS_DBUS_OBJECT); + g_signal_connect(G_OBJECT(users_client), DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED, G_CALLBACK(users_menu_root_changed), main_menu); + + users_separator = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(main_menu), users_separator); + gtk_widget_hide(users_separator); /* Should be default, I'm just being explicit. $(%*#$ hide already! */ + + return FALSE; +} + +/* Session Menu Stuff */ + +static guint +session_menu_pos_offset (void) +{ + guint position = 0; + if (SEPARATOR_SHOWN(users_separator)) { + GList * location = g_list_find(GTK_MENU_SHELL(main_menu)->children, users_separator); + position = g_list_position(GTK_MENU_SHELL(main_menu)->children, location) + 1; + } else if (SEPARATOR_SHOWN(status_separator)) { + GList * location = g_list_find(GTK_MENU_SHELL(main_menu)->children, status_separator); + position = g_list_position(GTK_MENU_SHELL(main_menu)->children, location) + 1; + } + + return position; +} + +static void +session_menu_removed (DbusmenuMenuitem * root, DbusmenuMenuitem * child, gpointer user_data) +{ + return; +} + +static void +session_menu_root_changed(DbusmenuGtkClient * client, DbusmenuMenuitem * newroot, GtkMenu * main) +{ + if (newroot == NULL) { + /* We're assuming this'll crash the least so it doesn't + hide a separator. May be a bad choice. */ + return; + } + + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(child_added), GUINT_TO_POINTER(SESSION_SECTION)); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(session_menu_removed), NULL); + g_signal_connect(G_OBJECT(newroot), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(child_moved), GUINT_TO_POINTER(SESSION_SECTION)); + + GList * child = NULL; + guint count = 0; + for (child = dbusmenu_menuitem_get_children(newroot); child != NULL; child = g_list_next(child), count++) { + child_added(newroot, DBUSMENU_MENUITEM(child->data), count, GUINT_TO_POINTER(SESSION_SECTION)); + } + + return; +} + +static gboolean +build_session_menu (gpointer userdata) +{ + g_debug("Building Session Menu"); + guint returnval = 0; + GError * error = NULL; + + if (proxy == NULL) { + /* If we don't have DBus, let's stay in the idle loop */ + return TRUE; + } + + if (!org_freedesktop_DBus_start_service_by_name (proxy, INDICATOR_SESSION_DBUS_NAME, 0, &returnval, &error)) { + g_error("Unable to send message to DBus to start service: %s", error != NULL ? error->message : "(NULL error)" ); + g_error_free(error); + return FALSE; + } + + if (returnval != DBUS_START_REPLY_SUCCESS && returnval != DBUS_START_REPLY_ALREADY_RUNNING) { + g_error("Return value isn't indicative of success: %d", returnval); + return FALSE; + } + + session_client = dbusmenu_gtkclient_new(INDICATOR_SESSION_DBUS_NAME, INDICATOR_SESSION_DBUS_OBJECT); + g_signal_connect(G_OBJECT(session_client), DBUSMENU_GTKCLIENT_SIGNAL_ROOT_CHANGED, G_CALLBACK(session_menu_root_changed), main_menu); + + return FALSE; +} + +/* Base menu stuff */ + +GtkMenu * +get_menu (void) +{ + connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + if (proxy == NULL) { + g_warning("Unable to get proxy for DBus itself. Seriously."); + } + + g_idle_add(build_status_menu, NULL); + g_idle_add(build_users_menu, NULL); + g_idle_add(build_session_menu, NULL); + + main_menu = GTK_MENU(gtk_menu_new()); + loading_item = gtk_menu_item_new_with_label("Loading..."); + gtk_menu_shell_append(GTK_MENU_SHELL(main_menu), loading_item); + gtk_widget_show(GTK_WIDGET(loading_item)); + + return main_menu; +} + + diff --git a/src/session-service.c b/src/session-service.c new file mode 100644 index 0000000..235d0bc --- /dev/null +++ b/src/session-service.c @@ -0,0 +1,269 @@ + +#include <glib/gi18n.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> + +#include <libdbusmenu-glib/server.h> +#include <libdbusmenu-glib/menuitem.h> + +#include "dbus-shared-names.h" + +#define DKP_ADDRESS "org.freedesktop.DeviceKit.Power" +#define DKP_OBJECT "/org/freedesktop/DeviceKit/Power" +#define DKP_INTERFACE "org.freedesktop.DeviceKit.Power" + +static DbusmenuMenuitem * root_menuitem = NULL; +static GMainLoop * mainloop = NULL; +static DBusGProxy * dkp_main_proxy = NULL; +static DBusGProxy * dkp_prop_proxy = NULL; + +static DBusGProxyCall * suspend_call = NULL; +static DBusGProxyCall * hibernate_call = NULL; + +static DbusmenuMenuitem * hibernate_mi = NULL; +static DbusmenuMenuitem * suspend_mi = NULL; + +/* Let's put this machine to sleep, with some info on how + it should sleep. */ +static void +sleep (DbusmenuMenuitem * mi, gpointer userdata) +{ + gchar * type = (gchar *)userdata; + + if (dkp_main_proxy == NULL) { + g_warning("Can not %s as no DeviceKit Power Proxy", type); + } + + dbus_g_proxy_call_no_reply(dkp_main_proxy, + type, + G_TYPE_INVALID, + G_TYPE_INVALID); + + return; +} + +/* A response to getting the suspend property */ +static void +suspend_prop_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata) +{ + suspend_call = NULL; + + GValue candoit = {0}; + GError * error = NULL; + dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_VALUE, &candoit, G_TYPE_INVALID); + if (error != NULL) { + g_warning("Unable to check suspend: %s", error->message); + g_error_free(error); + return; + } + g_debug("Got Suspend: %s", g_value_get_boolean(&candoit) ? "true" : "false"); + + if (suspend_mi != NULL) { + dbusmenu_menuitem_property_set(suspend_mi, "visible", g_value_get_boolean(&candoit) ? "true" : "false"); + } + + return; +} + +/* Response to getting the hibernate property */ +static void +hibernate_prop_cb (DBusGProxy * proxy, DBusGProxyCall * call, gpointer userdata) +{ + hibernate_call = NULL; + + GValue candoit = {0}; + GError * error = NULL; + dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_VALUE, &candoit, G_TYPE_INVALID); + if (error != NULL) { + g_warning("Unable to check hibernate: %s", error->message); + g_error_free(error); + return; + } + g_debug("Got Hibernate: %s", g_value_get_boolean(&candoit) ? "true" : "false"); + + if (suspend_mi != NULL) { + dbusmenu_menuitem_property_set(hibernate_mi, "visible", g_value_get_boolean(&candoit) ? "true" : "false"); + } + + return; +} + +/* A signal that we need to recheck to ensure we can still + hibernate and/or suspend */ +static void +dpk_changed_cb (DBusGProxy * proxy, gpointer user_data) +{ + /* Start Async call to see if we can hibernate */ + if (suspend_call == NULL) { + suspend_call = dbus_g_proxy_begin_call(dkp_prop_proxy, + "Get", + suspend_prop_cb, + NULL, + NULL, + G_TYPE_STRING, + DKP_INTERFACE, + G_TYPE_STRING, + "can-suspend", + G_TYPE_INVALID, + G_TYPE_VALUE, + G_TYPE_INVALID); + } + + /* Start Async call to see if we can suspend */ + if (hibernate_call == NULL) { + hibernate_call = dbus_g_proxy_begin_call(dkp_prop_proxy, + "Get", + hibernate_prop_cb, + NULL, + NULL, + G_TYPE_STRING, + DKP_INTERFACE, + G_TYPE_STRING, + "can-hibernate", + G_TYPE_INVALID, + G_TYPE_VALUE, + G_TYPE_INVALID); + } + + return; +} + +/* This function goes through and sets up what we need for + DKp checking. We're even setting up the calls for the props + we need */ +static void +setup_dkp (void) { + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); + g_return_if_fail(bus != NULL); + + if (dkp_main_proxy == NULL) { + dkp_main_proxy = dbus_g_proxy_new_for_name(bus, + DKP_ADDRESS, + DKP_OBJECT, + DKP_INTERFACE); + } + g_return_if_fail(dkp_main_proxy != NULL); + + if (dkp_prop_proxy == NULL) { + dkp_prop_proxy = dbus_g_proxy_new_for_name(bus, + DKP_ADDRESS, + DKP_OBJECT, + DBUS_INTERFACE_PROPERTIES); + } + g_return_if_fail(dkp_prop_proxy != NULL); + + /* Connect to changed signal */ + dbus_g_proxy_add_signal(dkp_main_proxy, + "Changed", + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal(dkp_main_proxy, + "Changed", + G_CALLBACK(dpk_changed_cb), + NULL, + NULL); + + /* Force an original "changed" event */ + dpk_changed_cb(dkp_main_proxy, NULL); + + return; +} + +/* This is the function to show a dialog on actions that + can destroy data. Currently it just calls the GTK version + but it seems that in the future it should figure out + what's going on and something better. */ +static void +show_dialog (DbusmenuMenuitem * mi, gchar * type) +{ + gchar * helper = g_build_filename(LIBEXECDIR, "gtk-logout-helper", NULL); + gchar * dialog_line = g_strdup_printf("%s --%s", helper, type); + g_free(helper); + + g_debug("Showing dialog '%s'", dialog_line); + + GError * error = NULL; + if (!g_spawn_command_line_async(dialog_line, &error)) { + g_warning("Unable to show dialog: %s", error->message); + g_error_free(error); + } + + g_free(dialog_line); + + return; +} + +/* This function creates all of the menuitems that the service + provides in the UI. It also connects them to the callbacks. */ +static void +create_items (DbusmenuMenuitem * root) { + DbusmenuMenuitem * mi = NULL; + + mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mi, "label", _("Log Out")); + dbusmenu_menuitem_child_append(root, mi); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(show_dialog), "logout"); + + suspend_mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(suspend_mi, "visible", "false"); + dbusmenu_menuitem_property_set(suspend_mi, "label", _("Suspend")); + dbusmenu_menuitem_child_append(root, suspend_mi); + g_signal_connect(G_OBJECT(suspend_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(sleep), "Suspend"); + + hibernate_mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(hibernate_mi, "visible", "false"); + dbusmenu_menuitem_property_set(hibernate_mi, "label", _("Hibernate")); + dbusmenu_menuitem_child_append(root, hibernate_mi); + g_signal_connect(G_OBJECT(hibernate_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(sleep), "Hibernate"); + + mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mi, "label", _("Restart")); + dbusmenu_menuitem_child_append(root, mi); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(show_dialog), "restart"); + + mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mi, "label", _("Shutdown")); + dbusmenu_menuitem_child_append(root, mi); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(show_dialog), "shutdown"); + + return; +} + +/* Main, is well, main. It brings everything up and throws + us into the mainloop of no return. */ +int +main (int argc, char ** argv) +{ + g_type_init(); + + DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + GError * error = NULL; + guint nameret = 0; + + if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_SESSION_DBUS_NAME, 0, &nameret, &error)) { + g_error("Unable to call to request name"); + return 1; + } + + if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + g_error("Unable to get name"); + return 1; + } + + root_menuitem = dbusmenu_menuitem_new(); + g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem)); + + create_items(root_menuitem); + setup_dkp(); + + DbusmenuServer * server = dbusmenu_server_new(INDICATOR_SESSION_DBUS_OBJECT); + dbusmenu_server_set_root(server, root_menuitem); + + mainloop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(mainloop); + + return 0; +} + diff --git a/src/status-provider-pidgin.c b/src/status-provider-pidgin.c new file mode 100644 index 0000000..529f457 --- /dev/null +++ b/src/status-provider-pidgin.c @@ -0,0 +1,158 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "status-provider.h" +#include "status-provider-pidgin.h" + +#include <dbus/dbus-glib.h> + +typedef enum { + PG_STATUS_UNKNOWN, + PG_STATUS_OFFLINE, + PG_STATUS_AVAILABLE, + PG_STATUS_UNAVAILABLE, + PG_STATUS_INVISIBLE, + PG_STATUS_AWAY, + PG_STATUS_EXTENDEND_AWAY, + PG_STATUS_MOBILE, + PG_STATUS_TUNE +} pg_status_t; + +static const StatusProviderStatus pg_to_sp_map[] = { + /* PG_STATUS_UNKNOWN, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* PG_STATUS_OFFLINE, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* PG_STATUS_AVAILABLE, */ STATUS_PROVIDER_STATUS_ONLINE, + /* PG_STATUS_UNAVAILABLE, */ STATUS_PROVIDER_STATUS_DND, + /* PG_STATUS_INVISIBLE, */ STATUS_PROVIDER_STATUS_INVISIBLE, + /* PG_STATUS_AWAY, */ STATUS_PROVIDER_STATUS_AWAY, + /* PG_STATUS_EXTENDEND_AWAY, */ STATUS_PROVIDER_STATUS_AWAY, + /* PG_STATUS_MOBILE, */ STATUS_PROVIDER_STATUS_OFFLINE, + /* PG_STATUS_TUNE */ STATUS_PROVIDER_STATUS_OFFLINE +}; + +static const pg_status_t sp_to_pg_map[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ PG_STATUS_AVAILABLE, + /* STATUS_PROVIDER_STATUS_AWAY, */ PG_STATUS_AWAY, + /* STATUS_PROVIDER_STATUS_DND */ PG_STATUS_UNAVAILABLE, + /* STATUS_PROVIDER_STATUS_INVISIBLE*/ PG_STATUS_INVISIBLE, + /* STATUS_PROVIDER_STATUS_OFFLINE */ PG_STATUS_OFFLINE +}; + +typedef struct _StatusProviderPidginPrivate StatusProviderPidginPrivate; +struct _StatusProviderPidginPrivate { + DBusGProxy * proxy; + pg_status_t pg_status; +}; + +#define STATUS_PROVIDER_PIDGIN_GET_PRIVATE(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginPrivate)) + +/* Prototypes */ +/* GObject stuff */ +static void status_provider_pidgin_class_init (StatusProviderPidginClass *klass); +static void status_provider_pidgin_init (StatusProviderPidgin *self); +static void status_provider_pidgin_dispose (GObject *object); +static void status_provider_pidgin_finalize (GObject *object); +/* Internal Funcs */ +static void set_status (StatusProvider * sp, StatusProviderStatus status); +static StatusProviderStatus get_status (StatusProvider * sp); + +G_DEFINE_TYPE (StatusProviderPidgin, status_provider_pidgin, STATUS_PROVIDER_TYPE); + +static void +status_provider_pidgin_class_init (StatusProviderPidginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (StatusProviderPidginPrivate)); + + object_class->dispose = status_provider_pidgin_dispose; + object_class->finalize = status_provider_pidgin_finalize; + + StatusProviderClass * spclass = STATUS_PROVIDER_CLASS(klass); + + spclass->set_status = set_status; + spclass->get_status = get_status; + + return; +} + +static void +status_provider_pidgin_init (StatusProviderPidgin *self) +{ + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(self); + + priv->proxy = NULL; + priv->pg_status = PG_STATUS_OFFLINE; + + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(bus != NULL); /* Can't do anymore DBus stuff without this, + all non-DBus stuff should be done */ + + GError * error = NULL; + priv->proxy = dbus_g_proxy_new_for_name_owner (bus, + "im.pidgin.purple.PurpleService", + "/im/pidgin/purple/PurpleObject", + "im.pidgin.purple.PurpleInterface", + &error); + if (error != NULL) { + g_debug("Unable to get Pidgin proxy: %s", error->message); + g_error_free(error); + } + + return; +} + +static void +status_provider_pidgin_dispose (GObject *object) +{ + + G_OBJECT_CLASS (status_provider_pidgin_parent_class)->dispose (object); + return; +} + +static void +status_provider_pidgin_finalize (GObject *object) +{ + + G_OBJECT_CLASS (status_provider_pidgin_parent_class)->finalize (object); + return; +} + +/** + status_provider_pidgin_new: + + Creates a new #StatusProviderPidgin object. No parameters or anything + like that. Just a convience function. + + Return value: A new instance of #StatusProviderPidgin +*/ +StatusProvider * +status_provider_pidgin_new (void) +{ + return STATUS_PROVIDER(g_object_new(STATUS_PROVIDER_PIDGIN_TYPE, NULL)); +} + +/* Takes the status provided generically for Status providers + and turns it into a Pidgin status and sends it to Pidgin. */ +static void +set_status (StatusProvider * sp, StatusProviderStatus status) +{ + g_debug("\tSetting Pidgin Status: %d", status); + g_return_if_fail(IS_STATUS_PROVIDER_PIDGIN(sp)); + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(sp); + pg_status_t pg_status = sp_to_pg_map[status]; + priv->pg_status = pg_status; + return; +} + +/* Takes the cached Pidgin status and makes it into the generic + Status provider status */ +static StatusProviderStatus +get_status (StatusProvider * sp) +{ + g_return_val_if_fail(IS_STATUS_PROVIDER_PIDGIN(sp), STATUS_PROVIDER_STATUS_OFFLINE); + StatusProviderPidginPrivate * priv = STATUS_PROVIDER_PIDGIN_GET_PRIVATE(sp); + return pg_to_sp_map[priv->pg_status]; +} diff --git a/src/status-provider-pidgin.h b/src/status-provider-pidgin.h new file mode 100644 index 0000000..85077e8 --- /dev/null +++ b/src/status-provider-pidgin.h @@ -0,0 +1,34 @@ +#ifndef __STATUS_PROVIDER_PIDGIN_H__ +#define __STATUS_PROVIDER_PIDGIN_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "status-provider.h" + +G_BEGIN_DECLS + +#define STATUS_PROVIDER_PIDGIN_TYPE (status_provider_pidgin_get_type ()) +#define STATUS_PROVIDER_PIDGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidgin)) +#define STATUS_PROVIDER_PIDGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginClass)) +#define IS_STATUS_PROVIDER_PIDGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_PIDGIN_TYPE)) +#define IS_STATUS_PROVIDER_PIDGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_PIDGIN_TYPE)) +#define STATUS_PROVIDER_PIDGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_PIDGIN_TYPE, StatusProviderPidginClass)) + + +typedef struct _StatusProviderPidginClass StatusProviderPidginClass; +struct _StatusProviderPidginClass { + StatusProviderClass parent_class; +}; + +typedef struct _StatusProviderPidgin StatusProviderPidgin; +struct _StatusProviderPidgin { + StatusProvider parent; +}; + +GType status_provider_pidgin_get_type (void); +StatusProvider * status_provider_pidgin_new (void); + +G_END_DECLS + +#endif diff --git a/src/status-provider.c b/src/status-provider.c new file mode 100644 index 0000000..97f1798 --- /dev/null +++ b/src/status-provider.c @@ -0,0 +1,86 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "status-provider.h" + +/* Signals */ +enum { + STATUS_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* GObject Boilerplate */ +static void status_provider_class_init (StatusProviderClass *klass); +static void status_provider_init (StatusProvider *self); + +G_DEFINE_TYPE (StatusProvider, status_provider, G_TYPE_OBJECT); + +static void +status_provider_class_init (StatusProviderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + klass->status_changed = NULL; + + klass->set_status = NULL; + klass->get_status = NULL; + + /** + StatusProvider::status-changed: + @arg0: The #StatusProvider object. + @arg1: The new status #StatusProviderStatus + + Should be emitted by subclasses everytime that the status + changes externally to us. + */ + signals[STATUS_CHANGED] = g_signal_new(STATUS_PROVIDER_SIGNAL_STATUS_CHANGED, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(StatusProviderClass, status_changed), + NULL, NULL, + g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + + return; +} + +static void +status_provider_init (StatusProvider *self) +{ + + return; +} + +void +status_provider_set_status (StatusProvider * sp, StatusProviderStatus status) +{ + g_return_if_fail(IS_STATUS_PROVIDER(sp)); + + StatusProviderClass * class = STATUS_PROVIDER_GET_CLASS(sp); + g_return_if_fail(class != NULL); + g_return_if_fail(class->set_status != NULL); + + return class->set_status(sp, status); +} + +StatusProviderStatus +status_provider_get_status (StatusProvider * sp) +{ + g_return_val_if_fail(IS_STATUS_PROVIDER(sp), STATUS_PROVIDER_STATUS_OFFLINE); + + StatusProviderClass * class = STATUS_PROVIDER_GET_CLASS(sp); + g_return_val_if_fail(class->get_status != NULL, STATUS_PROVIDER_STATUS_OFFLINE); + + return class->get_status(sp); +} + +void +status_provider_emit_status_changed (StatusProvider * sp, StatusProviderStatus newstatus) +{ + g_return_if_fail(IS_STATUS_PROVIDER(sp)); + g_signal_emit(sp, signals[STATUS_CHANGED], 0, newstatus, TRUE); + return; +} diff --git a/src/status-provider.h b/src/status-provider.h new file mode 100644 index 0000000..87a2ea3 --- /dev/null +++ b/src/status-provider.h @@ -0,0 +1,56 @@ +#ifndef __STATUS_PROVIDER_H__ +#define __STATUS_PROVIDER_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define STATUS_PROVIDER_TYPE (status_provider_get_type ()) +#define STATUS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_PROVIDER_TYPE, StatusProvider)) +#define STATUS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_PROVIDER_TYPE, StatusProviderClass)) +#define IS_STATUS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_PROVIDER_TYPE)) +#define IS_STATUS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_PROVIDER_TYPE)) +#define STATUS_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_PROVIDER_TYPE, StatusProviderClass)) + +typedef enum +{ + STATUS_PROVIDER_STATUS_ONLINE, + STATUS_PROVIDER_STATUS_AWAY, + STATUS_PROVIDER_STATUS_DND, + STATUS_PROVIDER_STATUS_INVISIBLE, + STATUS_PROVIDER_STATUS_OFFLINE, + /* Leave as last */ + STATUS_PROVIDER_STATUS_LAST +} +StatusProviderStatus; + +#define STATUS_PROVIDER_SIGNAL_STATUS_CHANGED "status-changed" + +typedef struct _StatusProvider StatusProvider; +struct _StatusProvider { + GObject parent; +}; + +typedef struct _StatusProviderClass StatusProviderClass; +struct _StatusProviderClass { + GObjectClass parent_class; + + /* Signals */ + void (*status_changed) (StatusProviderStatus newstatus); + + /* Virtual Functions */ + void (*set_status) (StatusProvider * sp, StatusProviderStatus newstatus); + StatusProviderStatus (*get_status) (StatusProvider * sp); +}; + +GType status_provider_get_type (void); + +void status_provider_set_status (StatusProvider * sp, StatusProviderStatus status); +StatusProviderStatus status_provider_get_status (StatusProvider * sp); + +void status_provider_emit_status_changed (StatusProvider * sp, StatusProviderStatus newstatus); + +G_END_DECLS + +#endif diff --git a/src/status-service-dbus.c b/src/status-service-dbus.c new file mode 100644 index 0000000..9ffc6aa --- /dev/null +++ b/src/status-service-dbus.c @@ -0,0 +1,72 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "status-service-dbus.h" + +static void status_service_dbus_class_init (StatusServiceDbusClass *klass); +static void status_service_dbus_init (StatusServiceDbus *self); +static void status_service_dbus_dispose (GObject *object); +static void status_service_dbus_finalize (GObject *object); +static void _status_service_server_watch (void); +static void _status_service_server_status_icons (void); +static void _status_service_server_pretty_user_name (void); + +#include "status-service-server.h" + +G_DEFINE_TYPE (StatusServiceDbus, status_service_dbus, G_TYPE_OBJECT); + +static void +status_service_dbus_class_init (StatusServiceDbusClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = status_service_dbus_dispose; + object_class->finalize = status_service_dbus_finalize; + + return; +} + +static void +status_service_dbus_init (StatusServiceDbus *self) +{ + + return; +} + +static void +status_service_dbus_dispose (GObject *object) +{ + + G_OBJECT_CLASS (status_service_dbus_parent_class)->dispose (object); + return; +} + +static void +status_service_dbus_finalize (GObject *object) +{ + + G_OBJECT_CLASS (status_service_dbus_parent_class)->finalize (object); + return; +} + +static void +_status_service_server_watch (void) +{ + + return; +} + +static void +_status_service_server_status_icons (void) +{ + + return; +} + +static void +_status_service_server_pretty_user_name (void) +{ + + return; +} diff --git a/src/status-service-dbus.h b/src/status-service-dbus.h new file mode 100644 index 0000000..1805dc5 --- /dev/null +++ b/src/status-service-dbus.h @@ -0,0 +1,31 @@ +#ifndef __STATUS_SERVICE_DBUS_H__ +#define __STATUS_SERVICE_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define STATUS_SERVICE_DBUS_TYPE (status_service_dbus_get_type ()) +#define STATUS_SERVICE_DBUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), STATUS_SERVICE_DBUS_TYPE, StatusServiceDbus)) +#define STATUS_SERVICE_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), STATUS_SERVICE_DBUS_TYPE, StatusServiceDbusClass)) +#define IS_STATUS_SERVICE_DBUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), STATUS_SERVICE_DBUS_TYPE)) +#define IS_STATUS_SERVICE_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), STATUS_SERVICE_DBUS_TYPE)) +#define STATUS_SERVICE_DBUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), STATUS_SERVICE_DBUS_TYPE, StatusServiceDbusClass)) + +typedef struct _StatusServiceDbus StatusServiceDbus; +typedef struct _StatusServiceDbusClass StatusServiceDbusClass; + +struct _StatusServiceDbusClass { + GObjectClass parent_class; +}; + +struct _StatusServiceDbus { + GObject parent; +}; + +GType status_service_dbus_get_type (void); + +G_END_DECLS + +#endif diff --git a/src/status-service.c b/src/status-service.c new file mode 100644 index 0000000..dbb597c --- /dev/null +++ b/src/status-service.c @@ -0,0 +1,155 @@ + +#include <glib/gi18n.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> + +#include <libdbusmenu-glib/server.h> +#include <libdbusmenu-glib/menuitem.h> + +#include "dbus-shared-names.h" + +#include "status-service-dbus.h" + +#include "status-provider.h" +#include "status-provider-pidgin.h" + +typedef StatusProvider * (*newfunc) (void); +#define STATUS_PROVIDER_CNT 1 +static newfunc status_provider_newfuncs[STATUS_PROVIDER_CNT] = { + status_provider_pidgin_new +}; +static StatusProvider * status_providers[STATUS_PROVIDER_CNT] = { 0 }; + +static const gchar * status_strings [STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ N_("Available"), + /* STATUS_PROVIDER_STATUS_AWAY, */ N_("Away"), + /* STATUS_PROVIDER_STATUS_DND */ N_("Busy"), + /* STATUS_PROVIDER_STATUS_INVISIBLE */ N_("Invisible"), + /* STATUS_PROVIDER_STATUS_OFFLINE, */ N_("Offline") +}; + +static const gchar * status_icons[STATUS_PROVIDER_STATUS_LAST] = { + /* STATUS_PROVIDER_STATUS_ONLINE, */ "user-online", + /* STATUS_PROVIDER_STATUS_AWAY, */ "user-away", + /* STATUS_PROVIDER_STATUS_DND, */ "user-busy", + /* STATUS_PROVIDER_STATUS_INVISIBLE, */ "user-invisible", + /* STATUS_PROVIDER_STATUS_OFFLINE */ "user-offline" +}; + + +static DbusmenuMenuitem * root_menuitem = NULL; +static GMainLoop * mainloop = NULL; + +/* A fun little function to actually lock the screen. If, + that's what you want, let's do it! */ +static void +lock_screen (DbusmenuMenuitem * mi, gpointer data) +{ + g_debug("Lock Screen"); + + DBusGConnection * session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(session_bus != NULL); + + DBusGProxy * proxy = dbus_g_proxy_new_for_name_owner(session_bus, + "org.gnome.ScreenSaver", + "/", + "org.gnome.ScreenSaver", + NULL); + g_return_if_fail(proxy != NULL); + + dbus_g_proxy_call_no_reply(proxy, + "Lock", + G_TYPE_INVALID, + G_TYPE_INVALID); + + g_object_unref(proxy); + + return; +} + +static void +status_menu_click (DbusmenuMenuitem * mi, gpointer data) +{ + StatusProviderStatus status = (StatusProviderStatus)GPOINTER_TO_INT(data); + g_debug("Setting status: %d", status); + int i; + for (i = 0; i < STATUS_PROVIDER_CNT; i++) { + status_provider_set_status(status_providers[i], status); + } + + return; +} + +static gboolean +build_providers (gpointer data) +{ + int i; + for (i = 0; i < STATUS_PROVIDER_CNT; i++) { + status_providers[i] = status_provider_newfuncs[i](); + } + + return FALSE; +} + +static gboolean +build_menu (gpointer data) +{ + DbusmenuMenuitem * root = DBUSMENU_MENUITEM(data); + g_return_val_if_fail(root != NULL, FALSE); + + StatusProviderStatus i; + for (i = STATUS_PROVIDER_STATUS_ONLINE; i < STATUS_PROVIDER_STATUS_LAST; i++) { + DbusmenuMenuitem * mi = dbusmenu_menuitem_new(); + + dbusmenu_menuitem_property_set(mi, "label", _(status_strings[i])); + dbusmenu_menuitem_property_set(mi, "icon", status_icons[i]); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(status_menu_click), GINT_TO_POINTER(i)); + + dbusmenu_menuitem_child_append(root, mi); + + g_debug("Built %s", status_strings[i]); + } + + DbusmenuMenuitem * mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mi, "label", _("Lock Screen")); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(lock_screen), GINT_TO_POINTER(i)); + dbusmenu_menuitem_child_append(root, mi); + + return FALSE; +} + +int +main (int argc, char ** argv) +{ + g_type_init(); + + DBusGConnection * connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + DBusGProxy * bus_proxy = dbus_g_proxy_new_for_name(connection, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + GError * error = NULL; + guint nameret = 0; + + if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_STATUS_DBUS_NAME, 0, &nameret, &error)) { + g_error("Unable to call to request name"); + return 1; + } + + if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + g_error("Unable to get name"); + return 1; + } + + g_idle_add(build_providers, NULL); + + root_menuitem = dbusmenu_menuitem_new(); + DbusmenuServer * server = dbusmenu_server_new(INDICATOR_STATUS_DBUS_OBJECT); + dbusmenu_server_set_root(server, root_menuitem); + + g_idle_add(build_menu, root_menuitem); + + mainloop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(mainloop); + + return 0; +} + diff --git a/src/status-service.xml b/src/status-service.xml new file mode 100644 index 0000000..df10859 --- /dev/null +++ b/src/status-service.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<node name="/"> + <interface name="com.ubuntu.indicator.status"> + +<!-- Methods --> + <method name="Watch"> + <annotation name="org.freedesktop.DBus.GLib.Async" value="true" /> + </method> + <method name="StatusIcons"> + <arg type="a(s)" name="icons" direction="out" /> + </method> + <method name="PrettyUserName"> + <arg type="s" name="name" direction="out" /> + </method> + +<!-- Signals --> + <signal name="UserChanged"> + <arg type="s" name="name" direction="out" /> + </signal> + <signal name="StatusIconsChanged"> + <arg type="a(s)" name="icons" direction="out" /> + </signal> + + </interface> +</node> diff --git a/src/users-service.c b/src/users-service.c new file mode 100644 index 0000000..731bc4d --- /dev/null +++ b/src/users-service.c @@ -0,0 +1,141 @@ + +#include <glib/gi18n.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-bindings.h> + +#include <libdbusmenu-glib/server.h> +#include <libdbusmenu-glib/menuitem.h> + +#include "dbus-shared-names.h" + +#define GUEST_SESSION_LAUNCHER "/usr/share/gdm/guest-session/guest-session-launch" + +static DBusGConnection * session_bus = NULL; +static DBusGConnection * system_bus = NULL; +static DBusGProxy * bus_proxy = NULL; +static DBusGProxy * gdm_proxy = NULL; +static DbusmenuMenuitem * root_menuitem = NULL; +static GMainLoop * mainloop = NULL; + +static gboolean +check_guest_session (void) +{ + if (geteuid() < 500) { + /* System users shouldn't have guest account shown. Mosly + this would be the case of the guest user itself. */ + return FALSE; + } + if (!g_file_test(GUEST_SESSION_LAUNCHER, G_FILE_TEST_IS_EXECUTABLE)) { + /* It doesn't appear that the Guest session stuff is + installed. So let's not use it then! */ + return FALSE; + } + + return TRUE; +} + +static void +activate_guest_session (DbusmenuMenuitem * mi, gpointer user_data) +{ + GError * error = NULL; + if (!g_spawn_command_line_async(GUEST_SESSION_LAUNCHER, &error)) { + g_warning("Unable to start guest session: %s", error->message); + g_error_free(error); + } + + return; +} + +static gboolean +check_new_session (void) +{ + if (system_bus == NULL) { + system_bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); + } + + if (system_bus == NULL) { + return FALSE; + } + + if (gdm_proxy == NULL) { + gdm_proxy = dbus_g_proxy_new_for_name(system_bus, + "org.gnome.DisplayManager", + "/org/gnome/DisplayManager/LocalDisplayFactory", + "org.gnome.DisplayManager.LocalDisplayFactory"); + } + + if (gdm_proxy == NULL) { + return FALSE; + } + + return TRUE; +} + +static void +activate_new_session (DbusmenuMenuitem * mi, gpointer user_data) +{ + GError * error = NULL; + if (!g_spawn_command_line_async("gdmflexiserver --startnew", &error)) { + g_warning("Unable to start guest session: %s", error->message); + g_error_free(error); + } + + return; +} + +static void +create_items (DbusmenuMenuitem * root) { + DbusmenuMenuitem * mi = NULL; + + if (check_guest_session()) { + mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mi, "label", _("Guest Session")); + dbusmenu_menuitem_child_append(root, mi); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_guest_session), NULL); + } + + if (check_new_session()) { + mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mi, "label", _("New Session...")); + dbusmenu_menuitem_child_append(root, mi); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_new_session), NULL); + } + + return; +} + +int +main (int argc, char ** argv) +{ + g_type_init(); + + session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + bus_proxy = dbus_g_proxy_new_for_name(session_bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + GError * error = NULL; + guint nameret = 0; + + if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_USERS_DBUS_NAME, 0, &nameret, &error)) { + g_error("Unable to call to request name"); + return 1; + } + + if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + g_error("Unable to get name"); + return 1; + } + + root_menuitem = dbusmenu_menuitem_new(); + g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem)); + + create_items(root_menuitem); + + DbusmenuServer * server = dbusmenu_server_new(INDICATOR_USERS_DBUS_OBJECT); + dbusmenu_server_set_root(server, root_menuitem); + + mainloop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(mainloop); + + return 0; +} + |