diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/CMakeLists.txt | 27 | ||||
-rw-r--r-- | src/indicator-printer-state-notifier.c | 36 | ||||
-rw-r--r-- | src/indicator-printers-service.c | 561 | ||||
-rw-r--r-- | src/indicator-printers-service.h | 52 | ||||
-rw-r--r-- | src/main.c | 47 |
6 files changed, 548 insertions, 179 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 24451e4..a55e47a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ endif () include (GNUInstallDirs) find_package (PkgConfig REQUIRED) include (FindPkgConfig) -pkg_check_modules (SERVICE REQUIRED gtk+-3.0>=3.0 ayatana-indicator3-0.4>=0.2 dbusmenu-glib-0.4>=0.2 dbusmenu-gtk3-0.4) +pkg_check_modules (SERVICE REQUIRED glib-2.0>=2.36 gio-2.0>=2.36 gio-unix-2.0>=2.36 libayatana-common) find_program (CUPS_CONFIG cups-config REQUIRED) execute_process (COMMAND ${CUPS_CONFIG} --cflags OUTPUT_VARIABLE CUPS_CFLAGS) execute_process (COMMAND ${CUPS_CONFIG} --libs OUTPUT_VARIABLE CUPS_LIBS) @@ -61,7 +61,7 @@ if (ENABLE_TESTS) add_subdirectory (test) if (ENABLE_COVERAGE) find_package (CoverageReport) - ENABLE_COVERAGE_REPORT (TARGETS "ayatana-printersmenu" "ayatana-indicator-printers-service" TESTS "mock-cups-notifier" FILTER /usr/include ${CMAKE_BINARY_DIR}/*) + ENABLE_COVERAGE_REPORT (TARGETS "ayatanaindicatorprinterssservice" "ayatana-indicator-printers-service" TESTS "mock-cups-notifier" FILTER /usr/include ${CMAKE_BINARY_DIR}/*) endif () endif () diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 98af322..44e276a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,31 +1,22 @@ -# libayatana-printersmenu.so -add_library (ayatana-printersmenu SHARED - indicator-printers.c - indicator-printers.h - indicator-menu-item.c - indicator-menu-item.h - dbus-names.h) -target_include_directories (ayatana-printersmenu PUBLIC ${SERVICE_INCLUDE_DIRS}) -target_compile_definitions (ayatana-printersmenu PUBLIC GETTEXT_PACKAGE="${GETTEXT_PACKAGE}" PACKAGE_NAME="${PACKAGE}") -install (TARGETS ayatana-printersmenu DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/ayatana-indicators3/7/") - # cups-notifier.h # cups-notifier.c include (GdbusCodegen) add_gdbus_codegen_with_namespace (CUPS_NOTIFIER cups-notifier org.cups.cupsd Cups "${CMAKE_CURRENT_SOURCE_DIR}/org.cups.cupsd.Notifier.xml") -# ayatana-indicator-printers-service -add_executable (ayatana-indicator-printers-service +# libayatanaindicatorprinterssservice.a +add_library (ayatanaindicatorprinterssservice STATIC + indicator-printers-service.h indicator-printers-service.c - indicator-printers-menu.c - indicator-printers-menu.h indicator-printer-state-notifier.c indicator-printer-state-notifier.h spawn-printer-settings.c spawn-printer-settings.h dbus-names.h ${CUPS_NOTIFIER}) -target_include_directories (ayatana-indicator-printers-service PUBLIC ${SERVICE_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) -target_link_libraries (ayatana-indicator-printers-service ${SERVICE_LIBRARIES}) -target_compile_definitions (ayatana-indicator-printers-service PUBLIC GETTEXT_PACKAGE="${GETTEXT_PACKAGE}" LOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}") +target_include_directories (ayatanaindicatorprinterssservice PUBLIC ${SERVICE_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) +target_compile_definitions (ayatanaindicatorprinterssservice PUBLIC GETTEXT_PACKAGE="${GETTEXT_PACKAGE}" LOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}") + +# ayatana-indicator-printers-service +add_executable (ayatana-indicator-printers-service main.c) +target_link_libraries (ayatana-indicator-printers-service ayatanaindicatorprinterssservice ${SERVICE_LIBRARIES}) install (TARGETS ayatana-indicator-printers-service RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}) diff --git a/src/indicator-printer-state-notifier.c b/src/indicator-printer-state-notifier.c index 7a587a0..12f77c6 100644 --- a/src/indicator-printer-state-notifier.c +++ b/src/indicator-printer-state-notifier.c @@ -1,7 +1,9 @@ /* * Copyright 2012 Canonical Ltd. + * Copyright 2022 Robert Tari * * Authors: Lars Uebernickel <lars.uebernickel@canonical.com> + * Robert Tari <robert@tari.in> * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -17,9 +19,8 @@ */ #include "indicator-printer-state-notifier.h" - +#include <ayatana/common/utils.h> #include <glib/gi18n.h> -#include <gtk/gtk.h> #include <cups/cups.h> #include <string.h> #include <stdarg.h> @@ -27,9 +28,6 @@ #include "cups-notifier.h" #include "spawn-printer-settings.h" - -#define RESPONSE_SHOW_SYSTEM_SETTINGS 1 - struct _IndicatorPrinterStateNotifierPrivate { CupsNotifier *cups_notifier; @@ -95,12 +93,9 @@ show_alert_box (const gchar *printer, const gchar *reason, int njobs) { - GtkWidget *dialog; - GtkWidget *image; gchar *primary_text; gchar *secondary_text; - image = gtk_image_new_from_icon_name ("printer", GTK_ICON_SIZE_DIALOG); primary_text = g_strdup_printf (reason, printer); secondary_text = g_strdup_printf (ngettext( @@ -108,31 +103,14 @@ show_alert_box (const gchar *printer, "You have %d jobs queued to print on this printer.", njobs), njobs); - dialog = g_object_new (GTK_TYPE_MESSAGE_DIALOG, - "title", _("Printing Problem"), - "icon-name", "printer", - "image", image, - "text", primary_text, - "secondary-text", secondary_text, - "urgency-hint", TRUE, - "focus-on-map", FALSE, - "window-position", GTK_WIN_POS_CENTER, - "skip-taskbar-hint", FALSE, - "deletable", FALSE, - NULL); - + gchar *sText = g_strdup_printf("<b>%s</b>\n\n%s", primary_text, secondary_text); g_free (primary_text); g_free (secondary_text); - gtk_dialog_add_buttons(GTK_DIALOG (dialog), _("_Settingsā¦"), RESPONSE_SHOW_SYSTEM_SETTINGS, _("_OK"), GTK_RESPONSE_OK, NULL); - gtk_dialog_set_default_response (GTK_DIALOG (dialog), - GTK_RESPONSE_OK); - gtk_widget_show_all (dialog); - - if (gtk_dialog_run (GTK_DIALOG (dialog)) == RESPONSE_SHOW_SYSTEM_SETTINGS) - spawn_printer_settings (); + ayatana_common_utils_zenity_warning ("printer", _("Printing Problem"), sText); + g_free (sText); - gtk_widget_destroy (dialog); + spawn_printer_settings (); } diff --git a/src/indicator-printers-service.c b/src/indicator-printers-service.c index e2a32f7..042580f 100644 --- a/src/indicator-printers-service.c +++ b/src/indicator-printers-service.c @@ -18,189 +18,490 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <libayatana-indicator/indicator-service.h> -#include <libdbusmenu-glib/dbusmenu-glib.h> -#include <gtk/gtk.h> #include <cups/cups.h> #include "dbus-names.h" #include <glib/gi18n-lib.h> +#include <gio/gio.h> +#include "indicator-printers-service.h" #include "cups-notifier.h" -#include "indicator-printers-menu.h" #include "indicator-printer-state-notifier.h" +#include "spawn-printer-settings.h" #define NOTIFY_LEASE_DURATION (24 * 60 * 60) +static guint m_nSignal = 0; + +enum +{ + SECTION_HEADER = (1<<0), + SECTION_PRINTERS = (1<<2) +}; + +enum +{ + PROFILE_PHONE, + PROFILE_DESKTOP, + N_PROFILES +}; + +static const char *const lMenuNames[N_PROFILES] = +{ + "phone", + "desktop" +}; + +struct ProfileMenuInfo +{ + GMenu *pMenu; + GMenu *pSubmenu; + guint nExportId; +}; + +struct _IndicatorPrintersServicePrivate +{ + GCancellable *pCancellable; + IndicatorPrinterStateNotifier *pStateNotifier; + CupsNotifier *pCupsNotifier; + guint nOwnId; + guint nActionsId; + GDBusConnection *pConnection; + gboolean bMenusBuilt; + int nSubscriptionId; + struct ProfileMenuInfo lMenus[N_PROFILES]; + GSimpleActionGroup *pActionGroup; + GSimpleAction *pHeaderAction; + GSimpleAction *pPrinterAction; + GMenu *pPrintersSection; + gboolean bVisible; +}; + +typedef IndicatorPrintersServicePrivate priv_t; + +G_DEFINE_TYPE_WITH_PRIVATE (IndicatorPrintersService, indicator_printers_service, G_TYPE_OBJECT) + +static void rebuildNow (IndicatorPrintersService *self, guint nSections); + +static void unexport (IndicatorPrintersService *self) +{ + if (self->pPrivate->nSubscriptionId > 0) + { + ipp_t *pRequest = ippNewRequest (IPP_CANCEL_SUBSCRIPTION); + ippAddString (pRequest, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "/"); + ippAddInteger (pRequest, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-subscription-id", self->pPrivate->nSubscriptionId); + ipp_t *pResponse = cupsDoRequest (CUPS_HTTP_DEFAULT, pRequest, "/"); + + if (!pResponse || cupsLastError () != IPP_OK) + { + g_warning ("Error subscribing to CUPS notifications: %s\n", cupsLastErrorString ()); + + return; + } + + ippDelete (pResponse); + } + + // Unexport the menus + for (int i = 0; i < N_PROFILES; ++i) + { + guint *pId = &self->pPrivate->lMenus[i].nExportId; + + if (*pId) + { + g_dbus_connection_unexport_menu_model (self->pPrivate->pConnection, *pId); + *pId = 0; + } + } + + // Unexport the actions + if (self->pPrivate->nActionsId) + { + g_dbus_connection_unexport_action_group (self->pPrivate->pConnection, self->pPrivate->nActionsId); + self->pPrivate->nActionsId = 0; + } +} + +static void onPrinterStateChanged (CupsNotifier *pNotifier, const gchar *sText, const gchar *sPrinterUri, const gchar *sPrinterName, guint nPrinterState, const gchar *sPrinterStateReasons, gboolean bPrinterIsAcceptingJobs, IndicatorPrintersService *self) +{ + rebuildNow(self, SECTION_PRINTERS | SECTION_HEADER); +} + +static void onJobChanged (CupsNotifier *pNotifier, const gchar *sText, const gchar *sPrinterUri, const gchar *sPrinterName, guint nPrinterState, const gchar *sPrinterStateReasons, gboolean bPrinterIsAcceptingJobs, guint nJobId, guint nJobState, const gchar *sJobStateReasons, const gchar *sJobName, guint nJobImpressionsCompleted, IndicatorPrintersService *self) +{ + rebuildNow(self, SECTION_PRINTERS | SECTION_HEADER); +} + +static void onDispose (GObject *pObject) +{ + IndicatorPrintersService *self = INDICATOR_PRINTERS_SERVICE (pObject); + + unexport (self); + + if (self->pPrivate->pCancellable != NULL) + { + g_cancellable_cancel (self->pPrivate->pCancellable); + g_clear_object (&self->pPrivate->pCancellable); + } + + if (self->pPrivate->pCupsNotifier) + { + g_object_disconnect (self->pPrivate->pCupsNotifier, "any-signal", onJobChanged, self, "any-signal", onPrinterStateChanged, self, NULL); + g_clear_object (&self->pPrivate->pCupsNotifier); + } + + g_object_unref (self->pPrivate->pStateNotifier); + g_clear_object (&self->pPrivate->pPrinterAction); + g_clear_object (&self->pPrivate->pHeaderAction); + g_clear_object (&self->pPrivate->pActionGroup); + g_clear_object (&self->pPrivate->pConnection); + + G_OBJECT_CLASS (indicator_printers_service_parent_class)->dispose (pObject); +} + +static void indicator_printers_service_class_init (IndicatorPrintersServiceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->dispose = onDispose; + m_nSignal = g_signal_new ("name-lost", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (IndicatorPrintersServiceClass, pNameLost), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static int createSubscription () +{ + int nId = 0; + + ipp_t *pRequest = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION); + ippAddString (pRequest, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "/"); + ippAddString (pRequest, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, "notify-events", NULL, "all"); + ippAddString (pRequest, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, "notify-recipient-uri", NULL, "dbus://"); + ippAddInteger (pRequest, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-lease-duration", NOTIFY_LEASE_DURATION); + ipp_t *pResponse = cupsDoRequest (CUPS_HTTP_DEFAULT, pRequest, "/"); + + if (!pResponse || cupsLastError () != IPP_OK) + { + g_warning ("Error subscribing to CUPS notifications: %s\n", cupsLastErrorString ()); -static int -create_subscription () -{ - ipp_t *req; - ipp_t *resp; - ipp_attribute_t *attr; - int id = 0; - - req = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION); - ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD, - "notify-events", NULL, "all"); - ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, - "notify-recipient-uri", NULL, "dbus://"); - ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, - "notify-lease-duration", NOTIFY_LEASE_DURATION); - - resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/"); - if (!resp || cupsLastError() != IPP_OK) { - g_warning ("Error subscribing to CUPS notifications: %s\n", - cupsLastErrorString ()); return 0; } - attr = ippFindAttribute (resp, "notify-subscription-id", IPP_TAG_INTEGER); - if (attr) - id = ippGetInteger (attr, 0); + ipp_attribute_t *pAttribute = ippFindAttribute (pResponse, "notify-subscription-id", IPP_TAG_INTEGER); + + if (pAttribute) + { + nId = ippGetInteger (pAttribute, 0); + } else - g_warning ("ipp-create-printer-subscription response doesn't contain " - "subscription id.\n"); + { + g_warning ("ipp-create-printer-subscription response doesn't contain subscription id.\n"); + } - ippDelete (resp); - return id; + ippDelete (pResponse); + + return nId; } +static gboolean renewSubscriptionTimeout (gpointer pData) +{ + int *nSubscriptionId = pData; + gboolean bRenewed = TRUE; + ipp_t *pRequest = ippNewRequest (IPP_RENEW_SUBSCRIPTION); + ippAddInteger (pRequest, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "notify-subscription-id", *nSubscriptionId); + ippAddString (pRequest, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, "/"); + ippAddString (pRequest, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, "notify-recipient-uri", NULL, "dbus://"); + ippAddInteger (pRequest, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, "notify-lease-duration", NOTIFY_LEASE_DURATION); + ipp_t *pResponse = cupsDoRequest (CUPS_HTTP_DEFAULT, pRequest, "/"); + + if (!pResponse || cupsLastError () != IPP_OK) + { + g_warning ("Error renewing CUPS subscription %d: %s\n", *nSubscriptionId, cupsLastErrorString ()); + bRenewed = FALSE; + } + else + { + ippDelete (pResponse); + } + + if (*nSubscriptionId <= 0 || !bRenewed) + { + *nSubscriptionId = createSubscription (); + } + + return TRUE; +} -static gboolean -renew_subscription (int id) +static GVariant *createHeaderState (IndicatorPrintersService *self) { - ipp_t *req; - ipp_t *resp; + GVariantBuilder b; - req = ippNewRequest (IPP_RENEW_SUBSCRIPTION); - ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER, - "notify-subscription-id", id); - ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI, - "notify-recipient-uri", NULL, "dbus://"); - ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER, - "notify-lease-duration", NOTIFY_LEASE_DURATION); + g_variant_builder_init (&b, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&b, "{sv}", "title", g_variant_new_string (_("Printers"))); + g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (TRUE)); - resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/"); - if (!resp || cupsLastError() != IPP_OK) { - g_warning ("Error renewing CUPS subscription %d: %s\n", - id, cupsLastErrorString ()); - return FALSE; + if (self->pPrivate->bVisible) + { + GIcon *pIcon = g_themed_icon_new_with_default_fallbacks ("printer-symbolic"); + g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_string (_("Printers"))); + + if (pIcon) + { + GVariant *pSerialized = g_icon_serialize (pIcon); + + if (pSerialized != NULL) + { + g_variant_builder_add (&b, "{sv}", "icon", pSerialized); + g_variant_unref (pSerialized); + } + + g_object_unref (pIcon); + } } - ippDelete (resp); - return TRUE; + return g_variant_builder_end (&b); } +static void onPrinterItemActivated (GSimpleAction *pAction, GVariant *pVariant, gpointer pData) +{ + const gchar *sPrinter = g_variant_get_string(pVariant, NULL); + spawn_printer_settings_with_args ("--show-jobs %s", sPrinter); +} -static gboolean -renew_subscription_timeout (gpointer userdata) +static void initActions (IndicatorPrintersService *self) { - int *subscription_id = userdata; + self->pPrivate->pActionGroup = g_simple_action_group_new (); - if (*subscription_id <= 0 || !renew_subscription (*subscription_id)) - *subscription_id = create_subscription (); + GSimpleAction *pAction = g_simple_action_new_stateful ("_header", NULL, createHeaderState (self)); + g_action_map_add_action (G_ACTION_MAP (self->pPrivate->pActionGroup), G_ACTION (pAction)); + self->pPrivate->pHeaderAction = pAction; - return TRUE; + pAction = g_simple_action_new("printer", G_VARIANT_TYPE_STRING); + g_action_map_add_action (G_ACTION_MAP (self->pPrivate->pActionGroup), G_ACTION (pAction)); + self->pPrivate->pPrinterAction = pAction; + g_signal_connect(pAction, "activate", G_CALLBACK(onPrinterItemActivated), self); + + rebuildNow (self, SECTION_HEADER); } +static GMenuModel *createPrintersSection (IndicatorPrintersService *self) +{ + self->pPrivate->pPrintersSection = g_menu_new (); + self->pPrivate->bVisible = FALSE; + cups_dest_t *lDests; + gint nDests = cupsGetDests (&lDests); + + for (gint i = 0; i < nDests; i++) + { + const gchar *sOption = cupsGetOption ("printer-state", lDests[i].num_options, lDests[i].options); + + if (sOption != NULL) + { + cups_job_t *lJobs; + gint nJobs = cupsGetJobs (&lJobs, lDests[i].name, 1, CUPS_WHICHJOBS_ACTIVE); + cupsFreeJobs (nJobs, lJobs); + + if (nJobs < 0) + { + g_warning ("printer '%s' does not exist\n", lDests[i].name); + } + else if (nJobs != 0) + { + GMenuItem *pItem = g_menu_item_new (lDests[i].name, NULL); + g_menu_item_set_attribute (pItem, "x-ayatana-type", "s", "org.ayatana.indicator.basic"); + g_menu_item_set_action_and_target_value(pItem, "indicator.printer", g_variant_new_string (lDests[i].name)); + GIcon *pIcon = g_themed_icon_new_with_default_fallbacks ("printer"); + GVariant *pSerialized = g_icon_serialize(pIcon); + + if (pSerialized != NULL) + { + g_menu_item_set_attribute_value(pItem, G_MENU_ATTRIBUTE_ICON, pSerialized); + g_variant_unref(pSerialized); + } + + g_object_unref(pIcon); + + gint nState = atoi (sOption); + + switch (nState) + { + case IPP_PRINTER_STOPPED: + { + g_menu_item_set_attribute (pItem, "x-ayatana-secondary-text", "s", _("Paused")); + + break; + } + case IPP_PRINTER_PROCESSING: + { + g_menu_item_set_attribute (pItem, "x-ayatana-secondary-count", "i", nJobs); + + break; + } + } + + g_menu_append_item(self->pPrivate->pPrintersSection, pItem); + g_object_unref(pItem); + self->pPrivate->bVisible = TRUE; + } + } + } + + cupsFreeDests (nDests, lDests); + + return G_MENU_MODEL (self->pPrivate->pPrintersSection); +} -void -cancel_subscription (int id) +static void createMenu (IndicatorPrintersService *self, int nProfile) { - ipp_t *req; - ipp_t *resp; + g_assert (0 <= nProfile && nProfile < N_PROFILES); + g_assert (self->pPrivate->lMenus[nProfile].pMenu == NULL); - if (id <= 0) - return; + GMenuModel *lSections[16]; + guint nSection = 0; - req = ippNewRequest (IPP_CANCEL_SUBSCRIPTION); - ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI, - "printer-uri", NULL, "/"); - ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER, - "notify-subscription-id", id); + // Build the sections + switch (nProfile) + { + case PROFILE_PHONE: + case PROFILE_DESKTOP: + { + lSections[nSection++] = createPrintersSection (self); - resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/"); - if (!resp || cupsLastError() != IPP_OK) { - g_warning ("Error subscribing to CUPS notifications: %s\n", - cupsLastErrorString ()); - return; + break; + } + + break; } - ippDelete (resp); + // Add sections to the submenu + GMenu *pSubmenu = g_menu_new (); + + for (guint i = 0; i < nSection; ++i) + { + g_menu_append_section (pSubmenu, NULL, lSections[i]); + g_object_unref (lSections[i]); + } + + // Add submenu to the header + GMenuItem *pHeader = g_menu_item_new (NULL, "indicator._header"); + g_menu_item_set_attribute (pHeader, "x-ayatana-type", "s", "org.ayatana.indicator.root"); + g_menu_item_set_submenu (pHeader, G_MENU_MODEL (pSubmenu)); + g_object_unref (pSubmenu); + + // Add header to the menu + GMenu *pMenu = g_menu_new (); + g_menu_append_item (pMenu, pHeader); + g_object_unref (pHeader); + + self->pPrivate->lMenus[nProfile].pMenu = pMenu; + self->pPrivate->lMenus[nProfile].pSubmenu = pSubmenu; } -static void -name_lost (GDBusConnection *connection, - const gchar *name, - gpointer user_data) +static void onBusAcquired (GDBusConnection *pConnection, const gchar *sName, gpointer pSelf) { - int subscription_id = GPOINTER_TO_INT (user_data); + g_debug ("bus acquired: %s", sName); + + IndicatorPrintersService *self = INDICATOR_PRINTERS_SERVICE (pSelf); + guint nId; + GError *pError = NULL; + GString *pPath = g_string_new (NULL); + self->pPrivate->pConnection = (GDBusConnection*)g_object_ref (G_OBJECT (pConnection)); + + // Export the actions + if ((nId = g_dbus_connection_export_action_group (pConnection, INDICATOR_PRINTERS_DBUS_OBJECT_PATH, G_ACTION_GROUP (self->pPrivate->pActionGroup), &pError))) + { + self->pPrivate->nActionsId = nId; + } + else + { + g_warning ("cannot export action group: %s", pError->message); + g_clear_error (&pError); + } - cancel_subscription (subscription_id); - gtk_main_quit (); + // Export the menus + for (gint nProfile = 0; nProfile < N_PROFILES; ++nProfile) + { + struct ProfileMenuInfo *pInfo = &self->pPrivate->lMenus[nProfile]; + + g_string_printf (pPath, "%s/%s", INDICATOR_PRINTERS_DBUS_OBJECT_PATH, lMenuNames[nProfile]); + + if ((nId = g_dbus_connection_export_menu_model (pConnection, pPath->str, G_MENU_MODEL (pInfo->pMenu), &pError))) + { + pInfo->nExportId = nId; + } + else + { + g_warning ("cannot export %s menu: %s", pPath->str, pError->message); + g_clear_error (&pError); + } + } + + g_string_free (pPath, TRUE); } -int main (int argc, char *argv[]) +static void onNameLost (GDBusConnection *pConnection, const gchar *sName, gpointer pSelf) { - /* Init i18n */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); + IndicatorPrintersService *self = INDICATOR_PRINTERS_SERVICE (pSelf); - DbusmenuServer *menuserver; - CupsNotifier *cups_notifier; - IndicatorPrintersMenu *menu; - IndicatorPrinterStateNotifier *state_notifier; - GError *error = NULL; - int subscription_id; + g_debug ("%s %s name lost %s", G_STRLOC, G_STRFUNC, sName); - gtk_init (&argc, &argv); + unexport (self); +} - subscription_id = create_subscription (); - g_timeout_add_seconds (NOTIFY_LEASE_DURATION - 60, - renew_subscription_timeout, - &subscription_id); +static void indicator_printers_service_init (IndicatorPrintersService *self) +{ + self->pPrivate = indicator_printers_service_get_instance_private (self); + self->pPrivate->pCancellable = g_cancellable_new (); + + GError *pError = NULL; + self->pPrivate->nSubscriptionId = createSubscription (); + g_timeout_add_seconds (NOTIFY_LEASE_DURATION - 60, renewSubscriptionTimeout, &self->pPrivate->nSubscriptionId); + self->pPrivate->pCupsNotifier = cups_notifier_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL, CUPS_DBUS_PATH, NULL, &pError); + + if (pError) + { + g_error ("Error creating cups notify handler: %s", pError->message); + g_error_free (pError); + } - g_bus_own_name (G_BUS_TYPE_SESSION, - INDICATOR_PRINTERS_DBUS_NAME, - G_BUS_NAME_OWNER_FLAGS_NONE, - NULL, NULL, name_lost, - GINT_TO_POINTER (subscription_id), NULL); + g_object_connect (self->pPrivate->pCupsNotifier, "signal::job-created", onJobChanged, self, "signal::job-state", onJobChanged, self, "signal::job-completed", onJobChanged, self, "signal::printer-state-changed", onPrinterStateChanged, self, NULL); + self->pPrivate->pStateNotifier = g_object_new (INDICATOR_TYPE_PRINTER_STATE_NOTIFIER, "cups-notifier", self->pPrivate->pCupsNotifier, NULL); + initActions (self); - cups_notifier = cups_notifier_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - 0, - NULL, - CUPS_DBUS_PATH, - NULL, - &error); - if (error) { - g_warning ("Error creating cups notify handler: %s", error->message); - g_error_free (error); - return 1; + for (gint nProfile = 0; nProfile < N_PROFILES; ++nProfile) + { + createMenu (self, nProfile); } - menu = g_object_new (INDICATOR_TYPE_PRINTERS_MENU, - "cups-notifier", cups_notifier, - NULL); + self->pPrivate->bMenusBuilt = TRUE; + self->pPrivate->nOwnId = g_bus_own_name (G_BUS_TYPE_SESSION, INDICATOR_PRINTERS_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, onBusAcquired, NULL, onNameLost, self, NULL); +} + +IndicatorPrintersService *indicator_printers_service_new () +{ + GObject *pObject = g_object_new (INDICATOR_TYPE_PRINTERS_SERVICE, NULL); + + return INDICATOR_PRINTERS_SERVICE (pObject); +} + +static void rebuildSection (GMenu *pMenu, int nPos, GMenuModel *pSection) +{ + g_menu_remove (pMenu, nPos); + g_menu_insert_section (pMenu, nPos, NULL, pSection); + g_object_unref (pSection); +} - menuserver = dbusmenu_server_new (INDICATOR_PRINTERS_DBUS_OBJECT_PATH); - dbusmenu_server_set_root (menuserver, - indicator_printers_menu_get_root (menu)); +static void rebuildNow (IndicatorPrintersService *self, guint nSections) +{ + struct ProfileMenuInfo *pinfo = &self->pPrivate->lMenus[PROFILE_DESKTOP]; - state_notifier = g_object_new (INDICATOR_TYPE_PRINTER_STATE_NOTIFIER, - "cups-notifier", cups_notifier, - NULL); + if (nSections & SECTION_HEADER) + { + g_simple_action_set_state (self->pPrivate->pHeaderAction, createHeaderState (self)); + } - gtk_main (); + if (!self->pPrivate->bMenusBuilt) + { + return; + } - g_object_unref (menu); - g_object_unref (menuserver); - g_object_unref (state_notifier); - g_object_unref (cups_notifier); - return 0; + if (nSections & SECTION_PRINTERS) + { + rebuildSection (pinfo->pSubmenu, 0, createPrintersSection (self)); + } } diff --git a/src/indicator-printers-service.h b/src/indicator-printers-service.h new file mode 100644 index 0000000..0b093c1 --- /dev/null +++ b/src/indicator-printers-service.h @@ -0,0 +1,52 @@ +/* + * Copyright 2022 Robert Tari + * + * Authors: Robert Tari <robert@tari.in> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_PRINTERS_SERVICE_H__ +#define __INDICATOR_PRINTERS_SERVICE_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define INDICATOR_PRINTERS_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_PRINTERS_SERVICE, IndicatorPrintersService)) +#define INDICATOR_TYPE_PRINTERS_SERVICE (indicator_printers_service_get_type ()) +#define INDICATOR_IS_PRINTERS_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_PRINTERS_SERVICE)) + +typedef struct _IndicatorPrintersService IndicatorPrintersService; +typedef struct _IndicatorPrintersServiceClass IndicatorPrintersServiceClass; +typedef struct _IndicatorPrintersServicePrivate IndicatorPrintersServicePrivate; + +struct _IndicatorPrintersService +{ + GObject parent; + IndicatorPrintersServicePrivate *pPrivate; +}; + +struct _IndicatorPrintersServiceClass +{ + GObjectClass parent_class; + void (*pNameLost) (IndicatorPrintersService *self); +}; + +GType indicator_printers_service_get_type (void); +IndicatorPrintersService *indicator_printers_service_new (); + +G_END_DECLS + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..6cc5103 --- /dev/null +++ b/src/main.c @@ -0,0 +1,47 @@ +/* + * Copyright 2022 Robert Tari + * + * Authors: + * Robert Tari <robert@tari.in> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <locale.h> +#include <glib.h> +#include <glib/gi18n.h> +#include "indicator-printers-service.h" + +static void onNameLost (gpointer pInstance G_GNUC_UNUSED, gpointer pLoop) +{ + g_message("Exiting: service couldn't acquire or lost ownership of busname"); + g_main_loop_quit ((GMainLoop*) pLoop); +} + +int main (int argc G_GNUC_UNUSED, char **argv G_GNUC_UNUSED) +{ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + IndicatorPrintersService *pService = indicator_printers_service_new (NULL); + GMainLoop *pLoop = g_main_loop_new (NULL, FALSE); + g_signal_connect (pService, "name-lost", G_CALLBACK (onNameLost), pLoop); + g_main_loop_run (pLoop); + + g_main_loop_unref (pLoop); + g_clear_object (&pService); + + return 0; +} |