From 6ce1a3a0fc714595ccc238b56b0ec5a8d22ef598 Mon Sep 17 00:00:00 2001 From: kaushik1216_2 Date: Sat, 9 Aug 2025 16:38:52 +0530 Subject: [PATCH] control-center: Support IPP and Printer Applications for CUPS transition Adapt the Printers panel to the new all-IPP printing architecture as CUPS transitions away from PPD files and classic printer drivers. Most modern printers are driverless IPP printers. For legacy and specialty printers, Printer Applications act as software emulations of driverless IPP printers and handle all required conversions internally. This replaces the former printer driver model. To allow a smooth transition, this change keeps support for both the existing and new architectures while CUPS 2.x supports them. Once GNOME Control Center is updated, CUPS can be upgraded independently to 3.x. New functionality includes: - Display IPP print destinations in the main view, including driverless network printers, IPP-over-USB printers, Printer Applications, and shared remote CUPS queues. - Adapt action menus for IPP destinations, including direct access to the destination's web administration interface. - Extend the Add Printer workflow to discover printers via installed Printer Applications. - Allow applying Printer Applications as drivers to discovered printers. - remove duplicate entries from printers main panel --- panels/printers/cc-printers-panel.c | 856 +++++++++++++++++++++++++-- panels/printers/pp-cups.c | 143 ++++- panels/printers/pp-cups.h | 5 + panels/printers/pp-printer-entry.blp | 12 +- panels/printers/pp-printer-entry.c | 149 ++++- panels/printers/pp-printer-entry.h | 4 + 6 files changed, 1117 insertions(+), 52 deletions(-) diff --git a/panels/printers/cc-printers-panel.c b/panels/printers/cc-printers-panel.c index f4a03a6859..3941891221 100644 --- a/panels/printers/cc-printers-panel.c +++ b/panels/printers/cc-printers-panel.c @@ -24,11 +24,13 @@ #include "cc-printers-resources.h" #include "pp-printer.h" -#include +#include +#include +#include #include #include #include -#include +#include #include #include @@ -57,6 +59,19 @@ #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) #define HAVE_CUPS_1_6 1 +#define OBJ_ATTR_SIZE 1024 +#define AVAHI_IF_UNSPEC -1 +#define AVAHI_PROTO_INET 0 +#define AVAHI_PROTO_INET6 1 +#define AVAHI_PROTO_UNSPEC -1 +#define SYSTEMD1_OBJ "/org/freedesktop/systemd1" +#define SYSTEMD1_BUS "org.freedesktop.systemd1" +#define SYSTEMD1_MANAGER_IFACE "org.freedesktop.systemd1.Manager" +#define SYSTEMD1_SERVICE_IFACE "org.freedesktop.systemd1.Service" +#define AVAHI_BUS "org.freedesktop.Avahi" +#define AVAHI_SERVER_IFACE "org.freedesktop.Avahi.Server" +#define AVAHI_SERVICE_BROWSER_IFACE "org.freedesktop.Avahi.ServiceBrowser" +#define AVAHI_SERVICE_RESOLVER_IFACE "org.freedesktop.Avahi.ServiceResolver" #endif #ifndef HAVE_CUPS_1_6 @@ -65,6 +80,61 @@ #define ippGetString(attr, element, language) attr->values[element].string.text #endif +enum +{ + SYSTEM_OBJECT, + PRINTER_OBJECT, + SCANNER_OBJECT, + PRINTER_QUEUE + +} obj_type; + +typedef struct +{ + ipp_t *response; + gchar *buff; + int buff_size; + cups_dest_t *service; +} add_attribute_data; + +typedef struct +{ + char *avahi_service_browser_path; + guint avahi_service_browser_subscription_id; + guint avahi_service_type_browser_subscription_id; + guint unsubscribe_general_subscription_id; + guint done, done_1, done_2, done_3, done_4; + GDBusConnection *dbus_connection; + GCancellable *avahi_cancellable; + GList *system_objects; + GMainLoop *loop; + gpointer user_data; + char *service_type; +} Avahi; + +typedef struct +{ + GList *services; + gchar *location; + gchar *address; + gchar *hostname; + gchar *name; + gchar *resource_path; + gchar *type; + gchar *domain; + gchar *object_type; + gchar *admin_url; + gchar *uri; + gchar *objAttr; + gchar *printer_type_hex; + gint64 printer_type, + printer_state; + gboolean got_printer_state, + got_printer_type; + int port; + int family; + gpointer user_data; +} AvahiData; struct _CcPrintersPanel { CcPanel parent_instance; @@ -80,8 +150,10 @@ struct _CcPrintersPanel GtkEditable *search_entry; AdwToastOverlay *toast_overlay; AdwToast *toast; + GHashTable *printer_groups; PpCups *cups; + Avahi *printer_device_backend; cups_dest_t *dests; int num_dests; @@ -140,6 +212,7 @@ static void free_dests (CcPrintersPanel *self); static void set_current_page (GObject *source_object, GAsyncResult *result, gpointer user_data); +static gboolean is_local_ip (const char *ip); static void execute_action (CcPrintersPanel *self, @@ -297,6 +370,42 @@ cc_printers_panel_dispose (GObject *object) detach_from_cups_notifier (CC_PRINTERS_PANEL (object)); + Avahi *printer_device_backend = self->printer_device_backend; + + for (int i = 0; i < 4; i++) + { + if (printer_device_backend[i].avahi_service_browser_subscription_id > 0) + { + g_dbus_connection_signal_unsubscribe (printer_device_backend[i].dbus_connection, + printer_device_backend[i].avahi_service_browser_subscription_id); + printer_device_backend[i].avahi_service_browser_subscription_id = 0; + } + + if (printer_device_backend[i].avahi_service_type_browser_subscription_id > 0) + { + g_dbus_connection_signal_unsubscribe (printer_device_backend[i].dbus_connection, + printer_device_backend[i].avahi_service_type_browser_subscription_id); + printer_device_backend[i].avahi_service_type_browser_subscription_id = 0; + } + + if (printer_device_backend[i].avahi_service_browser_path) + { + g_dbus_connection_call (printer_device_backend[i].dbus_connection, + AVAHI_BUS, + printer_device_backend[i].avahi_service_browser_path, + AVAHI_SERVICE_BROWSER_IFACE, + "Free", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + NULL, + NULL); + g_clear_pointer (&printer_device_backend[i].avahi_service_browser_path, g_free); + } + } + if (self->deleted_printer_name != NULL) { g_autoptr(PpPrinter) printer = pp_printer_new (self->deleted_printer_name); @@ -709,43 +818,270 @@ on_printer_changed (CcPrintersPanel *self) actualize_printers_list (self); } +static gint +compare_printer_entries (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const gchar *uri_a = (const gchar *) g_object_get_data (G_OBJECT (PP_PRINTER_ENTRY ((gpointer) a)), "printer-uri"); + const gchar *uri_b = (const gchar *) g_object_get_data (G_OBJECT (PP_PRINTER_ENTRY ((gpointer) b)), "printer-uri"); + + if (uri_a == NULL) + uri_a = ""; + if (uri_b == NULL) + uri_b = ""; + + return g_strcmp0 (uri_a, uri_b); +} + static void -add_printer_entry (CcPrintersPanel *self, - cups_dest_t printer) +sort_printers_in_group (GtkWidget *group_box, const gchar *group_name) { - PpPrinterEntry *printer_entry; - GSList *widgets, *l; + GList *children = NULL; + GtkWidget *child; - printer_entry = pp_printer_entry_new (printer, self->is_authorized); + child = gtk_widget_get_first_child (group_box); + while (child) + { + if (PP_IS_PRINTER_ENTRY (child)) + { + children = g_list_prepend (children, child); + } + child = gtk_widget_get_next_sibling (child); + } + + if (g_list_length (children) > 1) + { + GList *l; + for (l = children; l != NULL; l = l->next) + { + g_object_ref (l->data); + gtk_box_remove (GTK_BOX (group_box), GTK_WIDGET (l->data)); + } + + children = g_list_sort_with_data (children, compare_printer_entries, (gpointer) group_name); + + for (l = children; l != NULL; l = l->next) + { + gtk_box_append (GTK_BOX (group_box), GTK_WIDGET (l->data)); + g_object_unref (l->data); + } + } + + g_list_free (children); +} + +gboolean +is_local_ip (const char *ip) +{ + GInetAddress *addr = g_inet_address_new_from_string (ip); + if (!addr) + { + g_printerr ("Invalid IP address: %s\n", ip); + return FALSE; + } + + if (g_inet_address_get_is_loopback (addr)) + { + g_object_unref (addr); + return TRUE; + } + + if (g_inet_address_get_is_link_local (addr)) + { + g_object_unref (addr); + return TRUE; + } + + GResolver *resolver = g_resolver_get_default (); + GList *addresses = g_resolver_lookup_by_name (resolver, g_get_host_name (), NULL, NULL); + + gboolean is_local = FALSE; + for (GList *l = addresses; l != NULL; l = l->next) + { + GInetAddress *local = G_INET_ADDRESS (l->data); + if (g_inet_address_equal (addr, local)) + { + is_local = TRUE; + break; + } + } + + g_list_free_full (addresses, g_object_unref); + g_object_unref (addr); + + return is_local; +} + +static void +add_printer_entry (CcPrintersPanel *self, cups_dest_t printer) +{ + PpPrinterEntry *printer_entry; + GSList *widgets, *l; + GtkWidget *group_box = NULL, *group_container = NULL; + const gchar *printer_uri = NULL; + gchar *group_key_copy = NULL; + gboolean isprinterapp = TRUE; + const gchar *group_name = NULL; + const gchar *ip_address = NULL; + const gchar *printer_type_hex = NULL; + const gchar *hostname = NULL; + const gchar *port = NULL; + + for (int i = 0; i < printer.num_options; i++) + { + if (g_strcmp0 (printer.options[i].name, "address") == 0) + ip_address = printer.options[i].value; + + if (g_strcmp0 (printer.options[i].name, "printer-type-hex") == 0) + printer_type_hex = printer.options[i].value; + + if (g_strcmp0 (printer.options[i].name, "printer-more-info") == 0) + printer_uri = printer.options[i].value; + + if (g_strcmp0 (printer.options[i].name, "hostname") == 0) + hostname = printer.options[i].value; + + if (g_strcmp0 (printer.options[i].name, "port") == 0) + port = printer.options[i].value; + } + + if (ip_address != NULL && printer_type_hex != NULL && + is_local_ip (ip_address) && + printer_type_hex[0] == '0' && + strncmp (printer_type_hex, "0x", 2) == 0) + { + return; + } + if (!printer_uri || *printer_uri == '\0') + { + printer_uri = printer.name; + isprinterapp = FALSE; + } + + gchar *group_key = NULL; + if (hostname && port) + group_key = g_strdup_printf ("%s:%s", hostname, port); + else + group_key = g_strdup (printer.name); + + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init (&iter, self->printer_groups); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + if (g_strcmp0 (group_key, key) == 0) + { + group_container = GTK_WIDGET (value); + group_key_copy = g_strdup (key); + break; + } + } + + if (!group_container || !isprinterapp) + { + + group_key_copy = group_key; + group_container = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2); + + const gchar *group_title = printer.name; + if (isprinterapp) + { + for (int i = 0; i < printer.num_options; i++) + { + if (g_strcmp0 (printer.options[i].name, "printer-info") == 0) + { + group_title = printer.options[i].value; + break; + } + } + } + + GtkWidget *group_label = gtk_label_new (group_title); + gtk_widget_set_halign (group_label, GTK_ALIGN_START); + group_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_append (GTK_BOX (group_container), group_label); + gtk_widget_set_visible (group_label, FALSE); + gtk_box_append (GTK_BOX (group_container), group_box); + + g_object_set_data_full (G_OBJECT (group_container), + "group-title", + g_strdup (group_title), + g_free); + + gtk_list_box_insert (self->content, group_container, -1); + g_hash_table_insert (self->printer_groups, g_strdup (group_key_copy), group_container); + + group_name = group_title; + } + else + { + GtkWidget *child = gtk_widget_get_first_child (group_container); + while ((child = gtk_widget_get_next_sibling (child))) + { + if (GTK_IS_BOX (child)) + { + group_box = child; + break; + } + } + + group_name = (const gchar *) g_object_get_data (G_OBJECT (group_container), "group-title"); + } + + printer_entry = pp_printer_entry_new (printer, self->is_authorized); widgets = pp_printer_entry_get_size_group_widgets (printer_entry); for (l = widgets; l != NULL; l = l->next) gtk_size_group_add_widget (self->size_group, GTK_WIDGET (l->data)); g_slist_free (widgets); - g_signal_connect_object (printer_entry, - "printer-changed", + g_object_set_data_full (G_OBJECT (printer_entry), "printer-uri", g_strdup (printer_uri), g_free); + + g_signal_connect_object (printer_entry, "printer-changed", G_CALLBACK (on_printer_changed), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (printer_entry, - "printer-delete", + self, G_CONNECT_SWAPPED); + g_signal_connect_object (printer_entry, "printer-delete", G_CALLBACK (on_printer_deleted), - self, - G_CONNECT_SWAPPED); - g_signal_connect_object (printer_entry, - "printer-renamed", + self, G_CONNECT_SWAPPED); + g_signal_connect_object (printer_entry, "printer-renamed", G_CALLBACK (on_printer_renamed), - self, - G_CONNECT_SWAPPED); + self, G_CONNECT_SWAPPED); g_object_bind_property (self, "compact", printer_entry, "compact", G_BINDING_SYNC_CREATE); - gtk_list_box_insert (self->content, GTK_WIDGET (printer_entry), -1); + gtk_box_append (GTK_BOX (group_box), GTK_WIDGET (printer_entry)); + g_hash_table_insert (self->printer_entries, + g_strdup (printer.name), + printer_entry); + + if (group_name) + sort_printers_in_group (group_box, group_name); + + { + int count = 0; + GtkWidget *first_child = NULL; + GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (group_box)); + + while (child) + { + if (PP_IS_PRINTER_ENTRY (child)) + { + gtk_widget_remove_css_class (child, "first-in-group"); + if (count == 0) + first_child = child; + count++; + } + child = gtk_widget_get_next_sibling (child); + } + + if (count > 1 && first_child) + { + gtk_widget_add_css_class (first_child, "first-in-group"); + } + } - g_hash_table_insert (self->printer_entries, g_strdup (printer.name), printer_entry); + g_free (group_key); } static void @@ -770,6 +1106,9 @@ static gboolean remove_nonexisting_entry (CcPrintersPanel *self, PpPrinterEntry *entry) { + if (pp_printer_entry_get_web_interface (entry) != NULL) + return FALSE; + gboolean exists = FALSE; gint i; @@ -788,6 +1127,25 @@ remove_nonexisting_entry (CcPrintersPanel *self, return !exists; } +static void +add_ipp_device_cb (AvahiData *data, + cups_dest_t *dest) +{ + CcPrintersPanel *self = (CcPrintersPanel *) data->user_data; + gpointer item; + + g_message ("%d\n", data->user_data == NULL); + + gtk_stack_set_visible_child_name (self->main_stack, "printers-list"); + + item = g_hash_table_lookup (self->printer_entries, dest->name); + + if (item == NULL) + add_printer_entry (self, *dest); + + update_sensitivity (data->user_data); +} + static void actualize_printers_list_cb (GObject *source_object, GAsyncResult *result, @@ -876,7 +1234,6 @@ actualize_printers_list_cb (GObject *source_object, GtkAdjustment *adjustment; GtkWidget *printer_entry; - /* Scroll the view to show the newly added printer-entry. */ adjustment = gtk_scrolled_window_get_vadjustment (self->scrolled_window); printer_entry = GTK_WIDGET (g_hash_table_lookup (self->printer_entries, @@ -901,6 +1258,418 @@ actualize_printers_list (CcPrintersPanel *self) self); } +static int +compare_services (gconstpointer data1, + gconstpointer data2) +{ + AvahiData *data_1 = (AvahiData *) data1; + AvahiData *data_2 = (AvahiData *) data2; + + return g_strcmp0 (data_1->name, data_2->name); +} + +static void +add_option (cups_dest_t *dest, + gchar *attr_name, + gchar *attr_val) +{ + dest->options[dest->num_options].name = g_strdup (attr_name); + dest->options[dest->num_options].value = g_strdup (attr_val); + dest->num_options++; + + return; +} + +static gboolean +avahi_txt_get_key_value_pair (const gchar *entry, + gchar **key, + gchar **value) +{ + const gchar *equal_sign; + + *key = NULL; + *value = NULL; + + if (entry != NULL) + { + equal_sign = strstr (entry, "="); + + if (equal_sign != NULL) + { + *key = g_strndup (entry, equal_sign - entry); + *value = g_strdup (equal_sign + 1); + + return TRUE; + } + } + + return FALSE; +} + +static void +add_device (AvahiData *data) +{ + cups_dest_t *dest = g_new0 (cups_dest_t, 1); + dest->options = g_new0 (cups_option_t, 20); + dest->name = g_strdup (data->name); + add_option (dest, "domain", data->domain); + add_option (dest, "type", data->type); + add_option (dest, "address", data->address); + add_option (dest, "device-uri", data->uri); + add_option (dest, "printer-type-hex", data->printer_type_hex); + + if (data->admin_url != NULL) + add_option (dest, "printer-more-info", data->admin_url); + else + add_option (dest, "printer-more-info", g_strdup_printf ("http://%s:%d", data->hostname, data->port)); + + add_option (dest, "printer-location", data->location); + add_option (dest, "hostname", data->hostname); + add_option (dest, "port", g_strdup_printf ("%d", data->port)); + add_option (dest, "OBJ_TYPE", data->object_type); + + add_ipp_device_cb (data, dest); + return; +} + +static void +avahi_service_resolver_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + AvahiData *data; + Avahi *backend; + const char *name; + const char *hostname; + const char *type; + const char *domain; + const char *address; + char *key; + char *value; + char *tmp; + char *endptr; + GVariant *txt, + *child, + *output; + guint32 flags; + guint16 port; + GError *error = NULL; + GList *iter; + gsize length; + int interface; + int protocol; + int aprotocol; + int i; + + backend = user_data; + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + + if (output) + { + + g_variant_get (output, "(ii&s&s&s&si&sq@aayu)", + &interface, + &protocol, + &name, + &type, + &domain, + &hostname, + &aprotocol, + &address, + &port, + &txt, + &flags); + + data = g_new0 (AvahiData, 1); + data->user_data = backend->user_data; + + if (g_strcmp0 (type, "_ipps-system._tcp") == 0 || + g_strcmp0 (type, "_ipp-system._tcp") == 0) + { + data->object_type = g_strdup ("SYSTEM_OBJECT"); + } + else + { + data->object_type = g_strdup ("PRINTER_OBJECT"); + } + + for (i = 0; i < g_variant_n_children (txt); i++) + { + child = g_variant_get_child_value (txt, i); + + length = g_variant_get_size (child); + if (length > 0) + { + tmp = g_strndup (g_variant_get_data (child), length); + g_variant_unref (child); + + if (!avahi_txt_get_key_value_pair (tmp, &key, &value)) + { + g_free (tmp); + continue; + } + + if (g_strcmp0 (key, "rp") == 0) + { + data->resource_path = g_strdup (value); + } + else if (g_strcmp0 (key, "note") == 0) + { + data->location = g_strdup (value); + } + else if (g_strcmp0 (key, "printer-type") == 0) + { + endptr = NULL; + data->printer_type_hex = g_strdup (value); + // data->printer_type = g_ascii_strtoull (value, &endptr, 16); + if (data->printer_type != 0 || endptr != value) + data->got_printer_type = TRUE; + } + else if (g_strcmp0 (key, "printer-state") == 0) + { + endptr = NULL; + data->printer_state = g_ascii_strtoull (value, &endptr, 10); + if (data->printer_state != 0 || endptr != value) + data->got_printer_state = TRUE; + } + else if (g_strcmp0 (key, "adminurl") == 0) + { + if (*value != '\0') + data->admin_url = g_strdup (value); + } + g_clear_pointer (&key, g_free); + g_clear_pointer (&value, g_free); + g_free (tmp); + } + else + { + g_variant_unref (child); + } + } + + data->address = g_strdup (address); + data->hostname = g_strdup (hostname); + data->port = port; + data->family = protocol; + data->name = g_strdup (name); + data->type = g_strdup (type); + data->domain = g_strdup (domain); + data->services = NULL; + + g_variant_unref (txt); + g_variant_unref (output); + + iter = g_list_find_custom (backend->system_objects, (gconstpointer) data, (GCompareFunc) compare_services); + if (iter == NULL) + { + backend->system_objects = g_list_append (backend->system_objects, data); + add_device (data); + } + else + { + g_free (data->location); + g_free (data->address); + g_free (data->hostname); + g_free (data->name); + g_free (data->resource_path); + g_free (data->type); + g_free (data->domain); + g_free (data); + } + } + else + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + char *message = g_strdup_printf ("%s", error->message); + g_free (message); + } + g_error_free (error); + } + + return; +} + +static void +avahi_service_browser_signal_handler (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + Avahi *backend; + char *name; + char *type; + char *domain; + guint flags; + int interface; + int protocol; + + backend = user_data; + + if (g_strcmp0 (signal_name, "ItemNew") == 0) + { + g_variant_get (parameters, "(ii&s&s&su)", + &interface, + &protocol, + &name, + &type, + &domain, + &flags); + + g_dbus_connection_call (backend->dbus_connection, + AVAHI_BUS, + "/", + AVAHI_SERVER_IFACE, + "ResolveService", + g_variant_new ("(iisssiu)", + interface, + protocol, + name, + type, + domain, + AVAHI_PROTO_UNSPEC, + 0), + G_VARIANT_TYPE ("(iissssisqaayu)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + backend->avahi_cancellable, + avahi_service_resolver_cb, + backend); + } + else if (g_strcmp0 (signal_name, "ItemRemove") == 0) + { + g_variant_get (parameters, "(ii&s&s&su)", + &interface, + &protocol, + &name, + &type, + &domain, + &flags); + + GList *iter = g_list_find_custom (backend->system_objects, name, (GCompareFunc) compare_services); + if (iter != NULL) + { + backend->system_objects = g_list_delete_link (backend->system_objects, iter); + g_free (iter->data); + } + } + + return; +} + +static void +avahi_service_browser_new_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + Avahi *printer_device_backend; + GError *error = NULL; + GVariant *output = NULL; + + output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + res, + &error); + printer_device_backend = user_data; + + if (output) + { + + g_variant_get (output, "(o)", &printer_device_backend->avahi_service_browser_path); + printer_device_backend->avahi_service_type_browser_subscription_id = + g_dbus_connection_signal_subscribe (printer_device_backend->dbus_connection, + NULL, + AVAHI_SERVICE_BROWSER_IFACE, + NULL, + printer_device_backend->avahi_service_browser_path, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + avahi_service_browser_signal_handler, + printer_device_backend, + NULL); + + if (printer_device_backend->avahi_service_browser_path && + printer_device_backend->avahi_service_type_browser_subscription_id > 0) + { + g_dbus_connection_signal_unsubscribe (printer_device_backend->dbus_connection, + printer_device_backend->avahi_service_browser_subscription_id); + printer_device_backend->avahi_service_browser_subscription_id = 0; + } + + g_variant_unref (output); + } + else + { + + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + + g_error_free (error); + } +} + +static void +cups_get_ipp_devices_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + Avahi *printer_device_backend; + g_autoptr (GError) error = NULL; + pp_cups_get_dests_finish (PP_CUPS (source_object), result, &error); + + CcPrintersPanel *self = (CcPrintersPanel *) user_data; + printer_device_backend = self->printer_device_backend; + for (int i = 0; i < 4; i++) + { + printer_device_backend[i].user_data = user_data; + + printer_device_backend[i].avahi_service_browser_subscription_id = + g_dbus_connection_signal_subscribe (printer_device_backend[i].dbus_connection, + NULL, + AVAHI_SERVICE_BROWSER_IFACE, + NULL, + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + avahi_service_browser_signal_handler, + &printer_device_backend[i], + NULL); + + g_dbus_connection_call (printer_device_backend[i].dbus_connection, + AVAHI_BUS, + "/", + AVAHI_SERVER_IFACE, + "ServiceBrowserNew", + g_variant_new ("(iissu)", + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + printer_device_backend[i].service_type, + "", + 0), + G_VARIANT_TYPE ("(o)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + printer_device_backend[i].avahi_cancellable, + avahi_service_browser_new_cb, + &printer_device_backend[i]); + } + return; +} + +static void +actualize_ipp_device_list (CcPrintersPanel *self) +{ + pp_cups_get_new_dests_async (self->cups, + cc_panel_get_cancellable (CC_PANEL (self)), + cups_get_ipp_devices_cb, + self); +} + static void printer_add_async_cb (GObject *source_object, GAsyncResult *res, @@ -927,6 +1696,7 @@ printer_add_async_cb (GObject *source_object, } actualize_printers_list (self); + actualize_ipp_device_list (self); } static void @@ -1015,6 +1785,7 @@ static void on_permission_changed (CcPrintersPanel *self) { actualize_printers_list (self); + actualize_ipp_device_list (self); update_sensitivity (self); } @@ -1162,21 +1933,25 @@ filter_function (GtkListBoxRow *row, static gint sort_function (GtkListBoxRow *row1, GtkListBoxRow *row2, - gpointer user_data) + gpointer user_data) { - PpPrinterEntry *entry1 = PP_PRINTER_ENTRY (row1); - PpPrinterEntry *entry2 = PP_PRINTER_ENTRY (row2); - if (pp_printer_entry_get_name (entry1) != NULL) + GtkWidget *child1 = gtk_list_box_row_get_child (row1); + GtkWidget *child2 = gtk_list_box_row_get_child (row2); + + const gchar *name1 = (const gchar *) g_object_get_data (G_OBJECT (child1), "group-title"); + const gchar *name2 = (const gchar *) g_object_get_data (G_OBJECT (child2), "group-title"); + + if (name1 != NULL) { - if (pp_printer_entry_get_name (entry2) != NULL) - return g_ascii_strcasecmp (pp_printer_entry_get_name (entry1), pp_printer_entry_get_name (entry2)); + if (name2 != NULL) + return g_ascii_strcasecmp (name1, name2); else return 1; } else { - if (pp_printer_entry_get_name (entry2) != NULL) + if (name2 != NULL) return -1; else return 0; @@ -1230,8 +2005,8 @@ cc_printers_panel_class_init (CcPrintersPanelClass *klass) static void cc_printers_panel_init (CcPrintersPanel *self) { - g_autoptr(GtkCssProvider) provider = NULL; - + g_autoptr (GtkCssProvider) provider = NULL; + self->printer_groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); gtk_widget_init_template (GTK_WIDGET (self)); g_resources_register (cc_printers_get_resource ()); @@ -1278,8 +2053,8 @@ cc_printers_panel_init (CcPrintersPanel *self) G_CONNECT_SWAPPED | G_CONNECT_AFTER); /* Add unlock button */ - self->permission = (GPermission *)polkit_permission_new_sync ( - "org.opensuse.cupspkhelper.mechanism.all-edit", NULL, NULL, NULL); + self->permission = (GPermission *) polkit_permission_new_sync ( + "org.opensuse.cupspkhelper.mechanism.all-edit", NULL, NULL, NULL); if (self->permission != NULL) { g_signal_connect_object (self->permission, @@ -1299,7 +2074,22 @@ Please check your installation"); self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + self->printer_device_backend = g_new0 (Avahi, 4); + for (int x = 0; x < 4; x++) + { + self->printer_device_backend[x].system_objects = NULL; + self->printer_device_backend[x].avahi_cancellable = g_cancellable_new (); + self->printer_device_backend[x].dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, self->printer_device_backend[x].avahi_cancellable, NULL); + self->printer_device_backend[x].loop = g_main_loop_new (NULL, FALSE); + } + self->printer_device_backend[0].service_type = "_ipps-system._tcp"; + self->printer_device_backend[1].service_type = "_ipp-system._tcp"; + self->printer_device_backend[2].service_type = "_ipps._tcp"; + self->printer_device_backend[3].service_type = "_ipp._tcp"; + + actualize_ipp_device_list (self); actualize_printers_list (self); + attach_to_cups_notifier (self); get_all_ppds_async (cc_panel_get_cancellable (CC_PANEL (self)), diff --git a/panels/printers/pp-cups.c b/panels/printers/pp-cups.c index 9942579371..0017f71f71 100644 --- a/panels/printers/pp-cups.c +++ b/panels/printers/pp-cups.c @@ -18,9 +18,9 @@ * Author: Marek Kasik */ -#include "config.h" - #include "pp-cups.h" +#include "config.h" +#include #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) #define HAVE_CUPS_1_6 1 @@ -31,12 +31,56 @@ #define ippGetStatusCode(ipp) ipp->request.status.status_code #endif +typedef struct +{ + char *avahi_service_browser_path; + guint avahi_service_browser_subscription_id; + guint avahi_service_browser_subscription_id_ind; + guint unsubscribe_general_subscription_id; + guint done, done_1, done_2, done_3, done_4; + GDBusConnection *dbus_connection; + GCancellable *avahi_cancellable; + GList *system_objects; + GMainLoop *loop; + gpointer user_data; + char *service_type; +} Avahi; + +typedef struct +{ + GList *services; + gchar *location; + gchar *address; + gchar *hostname; + gchar *name; + gchar *resource_path; + gchar *type; + gchar *domain; + gchar *object_type; + gchar *admin_url; + gchar *uri; + gchar *objAttr; + gint64 printer_type, + printer_state; + gboolean got_printer_state, + got_printer_type; + int port; + int family; + gpointer user_data; +} AvahiData; + +typedef struct +{ + cups_dest_t *dests; + int num_of_dests; +} EnumData; struct _PpCups { GObject parent_instance; }; G_DEFINE_TYPE (PpCups, pp_cups, G_TYPE_OBJECT); +static Avahi *cupsGetIPPDevices (gpointer user_data); static void pp_cups_class_init (PpCupsClass *klass) @@ -54,12 +98,39 @@ pp_cups_new () return g_object_new (PP_TYPE_CUPS, NULL); } +// static void +// pp_cups_dests_free (PpCupsDests *dests) +// { +// cupsFreeDests (dests->num_of_dests, dests->dests); +// } + +static int +add_dest_cb (void *user_data, unsigned flags, cups_dest_t *dest) +{ + EnumData *data = (EnumData *) user_data; + + if (flags & CUPS_DEST_FLAGS_REMOVED) + return 1; // skip removed printers + + // Append dest to our list + data->num_of_dests = cupsCopyDest (dest, data->num_of_dests, &data->dests); + + return 1; // continue enumeration +} + static void pp_cups_dests_free (PpCupsDests *dests) { - cupsFreeDests (dests->num_of_dests, dests->dests); + if (dests) + { + if (dests->dests) + cupsFreeDests (dests->num_of_dests, dests->dests); + g_free (dests); + } } + + static void _pp_cups_get_dests_thread (GTask *task, gpointer *object, @@ -67,18 +138,78 @@ _pp_cups_get_dests_thread (GTask *task, GCancellable *cancellable) { PpCupsDests *dests; + EnumData data = { NULL, 0 }; + + // enumerate with 1s timeout + cupsEnumDests (CUPS_DEST_FLAGS_NONE, + 1000, // timeout (ms) + NULL, CUPS_PRINTER_LOCAL, CUPS_PRINTER_DISCOVERED, // no monitor, match-all + (cups_dest_cb_t) add_dest_cb, + &data); dests = g_new0 (PpCupsDests, 1); - dests->num_of_dests = cupsGetDests (&dests->dests); + dests->num_of_dests = data.num_of_dests; + dests->dests = data.dests; + + if (g_task_set_return_on_cancel (task, FALSE)) + g_task_return_pointer (task, dests, (GDestroyNotify) pp_cups_dests_free); + else + pp_cups_dests_free (dests); +} + +static void +_pp_cups_get_new_dests_thread (GTask *task, + gpointer *object, + gpointer task_data, + GCancellable *cancellable) +{ + Avahi *backends; + + backends = cupsGetIPPDevices (NULL); if (g_task_set_return_on_cancel (task, FALSE)) { - g_task_return_pointer (task, dests, (GDestroyNotify) pp_cups_dests_free); + g_task_return_pointer (task, backends, (GDestroyNotify) pp_cups_dests_free); } else { - pp_cups_dests_free (dests); + // pp_cups_dests_free (backends); + g_message ("Cancelled discovery for ipp devices\n"); + } +} + +Avahi * +cupsGetIPPDevices (gpointer user_data) +{ + Avahi *printer_device_backend; + + printer_device_backend = g_new0 (Avahi, 4); + for (int x = 0; x < 4; x++) + { + printer_device_backend[x].system_objects = NULL; + printer_device_backend[x].avahi_cancellable = g_cancellable_new (); + printer_device_backend[x].dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, printer_device_backend[x].avahi_cancellable, NULL); + printer_device_backend[x].loop = g_main_loop_new (NULL, FALSE); } + printer_device_backend[0].service_type = "_ipps-system._tcp"; + printer_device_backend[1].service_type = "_ipp-system._tcp"; + printer_device_backend[2].service_type = "_ipps._tcp"; + printer_device_backend[3].service_type = "_ipp._tcp"; + + return printer_device_backend; +} + +void +pp_cups_get_new_dests_async (PpCups *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr (GTask) task = NULL; + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_return_on_cancel (task, TRUE); + g_task_run_in_thread (task, (GTaskThreadFunc) _pp_cups_get_new_dests_thread); } void diff --git a/panels/printers/pp-cups.h b/panels/printers/pp-cups.h index 04063bb1c0..1d44ef72b8 100644 --- a/panels/printers/pp-cups.h +++ b/panels/printers/pp-cups.h @@ -41,6 +41,11 @@ void pp_cups_get_dests_async (PpCups *cups, GAsyncReadyCallback callback, gpointer user_data); +void pp_cups_get_new_dests_async (PpCups *cups, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + PpCupsDests *pp_cups_get_dests_finish (PpCups *cups, GAsyncResult *result, GError **error); diff --git a/panels/printers/pp-printer-entry.blp b/panels/printers/pp-printer-entry.blp index 6f11cd5a5a..6e69df1b3d 100644 --- a/panels/printers/pp-printer-entry.blp +++ b/panels/printers/pp-printer-entry.blp @@ -106,7 +106,7 @@ template $PpPrinterEntry: ListBoxRow { } } - Label { + Label printer_status_label { label: _("Status"); xalign: 1; mnemonic-widget: printer_status; @@ -218,20 +218,29 @@ template $PpPrinterEntry: ListBoxRow { menu printer_menu { section { + item { + label: _("Web Interface"); + action: "printer.webinterface"; + hidden-when: "action-disabled"; + } + item { label: _("Printing Options"); action: "printer.options"; + hidden-when: "action-disabled"; } item { label: _("Printer Details"); action: "printer.details"; + hidden-when: "action-disabled"; } item { /* Translators: Set this printer as default */ label: _("Use Printer by Default"); action: "printer.default"; + hidden-when: "action-disabled"; } item { @@ -244,6 +253,7 @@ menu printer_menu { item { label: _("Remove Printer"); action: "printer.remove"; + hidden-when: "action-disabled"; } } } diff --git a/panels/printers/pp-printer-entry.c b/panels/printers/pp-printer-entry.c index fbaac9a35a..66ffa3ad01 100644 --- a/panels/printers/pp-printer-entry.c +++ b/panels/printers/pp-printer-entry.c @@ -49,10 +49,12 @@ struct _PpPrinterEntry gboolean is_accepting_jobs; gchar *printer_make_and_model; gchar *printer_location; + gchar *web_interface; gchar *printer_hostname; gboolean is_authorized; gint printer_state; InkLevelData *inklevel; + gboolean is_system_printer; /* Maintenance commands */ PpMaintenanceCommand *clean_command; @@ -61,6 +63,7 @@ struct _PpPrinterEntry /* Widgets */ GtkOrientable *header_box; GtkLabel *printer_status; + GtkLabel *printer_status_label; GtkLabel *printer_name_label; GtkLabel *printer_model_label; GtkLabel *printer_model; @@ -71,6 +74,9 @@ struct _PpPrinterEntry GtkDrawingArea *supply_drawing_area; GtkWidget *show_jobs_dialog_button; GtkBox *printer_error; + GtkButton *printer_detail_btn; + GtkButton *printer_options_dialog_btn; + GtkLinkButton *web_interface_btn; GtkLabel *error_status; gboolean is_default; @@ -127,10 +133,17 @@ ink_level_data_free (InkLevelData *data) static void pp_printer_entry_init (PpPrinterEntry *self) { + GtkCssProvider *provider; + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.clean-heads", FALSE); gtk_widget_init_template (GTK_WIDGET (self)); self->inklevel = ink_level_data_new (); + + provider = gtk_css_provider_new (); + gtk_css_provider_load_from_string (provider, ".system-printer { font-size: clamp(1em, 0.08em + 0.7vw, 1.15em); } .first-in-group { border-bottom: 1px solid #c0c0c0; }"); + gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (self)), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + g_object_unref (provider); } typedef struct { @@ -366,6 +379,22 @@ on_printer_rename_cb (GObject *source_object, g_signal_emit_by_name (self, "printer-renamed", pp_printer_get_name (PP_PRINTER (source_object))); } +static void +on_click_web_interface (PpPrinterEntry *self) +{ + GtkUriLauncher *launcher; + launcher = gtk_uri_launcher_new (self->web_interface); + gtk_uri_launcher_launch (launcher, NULL, NULL, NULL, NULL); + g_object_unref (launcher); +} + +static void +on_click_web_interface_action (GtkWidget *widget, const char *action_name, GVariant *parameter) +{ + PpPrinterEntry *self = PP_PRINTER_ENTRY (widget); + on_click_web_interface (self); +} + static void show_printer_details_response_cb (PpPrinterEntry *self, PpDetailsDialog *dialog) @@ -534,18 +563,37 @@ get_jobs_cb (GObject *source_object, return; } - if (jobs->len == 0) + if (self->is_system_printer) { - /* Translators: This is the label of the button that opens the Jobs Dialog. */ - button_label = g_strdup (_("No Active Jobs")); + if (jobs->len == 0) + { + gtk_widget_set_visible (self->show_jobs_dialog_button, FALSE); + } + else + { + /* Translators: This is the label of the button that opens the Jobs Dialog. */ + button_label = g_strdup_printf (ngettext ("%u Job", "%u Jobs", jobs->len), jobs->len); + gtk_widget_set_visible (self->show_jobs_dialog_button, TRUE); + } } else { - /* Translators: This is the label of the button that opens the Jobs Dialog. */ - button_label = g_strdup_printf (ngettext ("%u Job", "%u Jobs", jobs->len), jobs->len); + if (jobs->len == 0) + { + /* Translators: This is the label of the button that opens the Jobs Dialog. */ + button_label = g_strdup (_("No Active Jobs")); + } + else + { + /* Translators: This is the label of the button that opens the Jobs Dialog. */ + button_label = g_strdup_printf (ngettext ("%u Job", "%u Jobs", jobs->len), jobs->len); + } + + gtk_widget_set_visible (self->show_jobs_dialog_button, TRUE); } - gtk_button_set_label (GTK_BUTTON (self->show_jobs_dialog_button), button_label); + if (button_label != NULL) + gtk_button_set_label (GTK_BUTTON (self->show_jobs_dialog_button), button_label); gtk_widget_set_sensitive (self->show_jobs_dialog_button, jobs->len > 0); if (self->pp_jobs_dialog != NULL) @@ -641,6 +689,7 @@ pp_printer_entry_get_size_group_widgets (PpPrinterEntry *self) { GSList *widgets = NULL; + widgets = g_slist_prepend (widgets, self->printer_status_label); widgets = g_slist_prepend (widgets, self->printer_location_label); widgets = g_slist_prepend (widgets, self->printer_model_label); widgets = g_slist_prepend (widgets, self->printer_inklevel_label); @@ -682,6 +731,20 @@ pp_printer_entry_get_name (PpPrinterEntry *self) return self->printer_name; } +const gchar * +pp_printer_entry_get_web_interface (PpPrinterEntry *self) +{ + g_return_val_if_fail (PP_IS_PRINTER_ENTRY (self), NULL); + return self->web_interface; +} + +const gchar * +pp_printer_entry_get_hostname (PpPrinterEntry *self) +{ + g_return_val_if_fail (PP_IS_PRINTER_ENTRY (self), NULL); + return self->printer_hostname; +} + const gchar * pp_printer_entry_get_location (PpPrinterEntry *self) { @@ -697,11 +760,15 @@ pp_printer_entry_update (PpPrinterEntry *self, cups_ptype_t printer_type = 0; gboolean is_accepting_jobs = TRUE; gboolean ink_supply_is_empty; + gboolean sanitize_name = FALSE; g_autofree gchar *instance = NULL; const gchar *printer_uri = NULL; + gchar *web_interface = NULL; + const gchar *dev_type = NULL; const gchar *device_uri = NULL; const gchar *location = NULL; const gchar *printer_make_and_model = NULL; + const gchar *printer_commands = NULL; const gchar *reason = NULL; gchar **printer_reasons = NULL; g_autofree gchar *status = NULL; @@ -777,8 +844,23 @@ pp_printer_entry_update (PpPrinterEntry *self, for (i = 0; i < printer.num_options; i++) { + if (g_strcmp0 (printer.options[i].name, "device-uri") == 0) device_uri = printer.options[i].value; + else if (g_strcmp0 (printer.options[i].name, "printer-more-info") == 0) + { + web_interface = printer.options[i].value, self->web_interface = web_interface; + } + else if (g_strcmp0 (printer.options[i].name, "OBJ_TYPE") == 0) + { + dev_type = printer.options[i].value; + if (g_strcmp0 (dev_type, "SYSTEM_OBJECT") == 0) + self->is_system_printer = TRUE; + } + else if (g_strcmp0 (printer.options[i].name, "sanitize-name") == 0) + sanitize_name = TRUE; + else if (g_strcmp0 (printer.options[i].name, "printer-commands") == 0) + printer_commands = printer.options[i].value; else if (g_strcmp0 (printer.options[i].name, "printer-uri-supported") == 0) printer_uri = printer.options[i].value; else if (g_strcmp0 (printer.options[i].name, "printer-type") == 0) @@ -865,7 +947,8 @@ pp_printer_entry_update (PpPrinterEntry *self, } if ((self->printer_state == PRINTER_STOPPED || !is_accepting_jobs) && - status != NULL && status[0] != '\0') + status != NULL && status[0] != '\0' && + !(self->web_interface != NULL && *self->web_interface != '\0' && self->is_system_printer)) { gtk_label_set_label (self->error_status, status); gtk_widget_set_visible (GTK_WIDGET (self->printer_error), TRUE); @@ -909,11 +992,32 @@ pp_printer_entry_update (PpPrinterEntry *self, g_free (self->printer_hostname); self->printer_hostname = printer_get_hostname (printer_type, device_uri, printer_uri); - gtk_label_set_text (self->printer_status, printer_status); + if (self->web_interface != NULL && *self->web_interface != '\0' && self->is_system_printer) + { + gtk_widget_set_visible (GTK_WIDGET (self->printer_status_label), FALSE); + gtk_widget_set_visible (GTK_WIDGET (self->printer_status), FALSE); + gtk_label_set_text (self->printer_status, ""); + } + else + { + gtk_widget_set_visible (GTK_WIDGET (self->printer_status_label), TRUE); + gtk_widget_set_visible (GTK_WIDGET (self->printer_status), TRUE); + gtk_label_set_text (self->printer_status, printer_status); + } gtk_label_set_text (self->printer_name_label, instance); self->is_default = printer.is_default; g_object_notify (G_OBJECT (self), "default"); + if (dev_type == NULL || sanitize_name == TRUE) + self->printer_make_and_model = sanitize_printer_model (printer_make_and_model); + else + { + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.options", FALSE); + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.details", FALSE); + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.remove", FALSE); + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.default", FALSE); + } + self->printer_make_and_model = sanitize_printer_model (printer_make_and_model); if (self->printer_make_and_model == NULL || self->printer_make_and_model[0] == '\0') @@ -926,24 +1030,42 @@ pp_printer_entry_update (PpPrinterEntry *self, gtk_label_set_text (self->printer_model, self->printer_make_and_model); } - if (location != NULL && location[0] == '\0') + if ((location != NULL && location[0] == '\0') || (self->web_interface != NULL && *self->web_interface != '\0' && self->is_system_printer)) { gtk_widget_set_visible (GTK_WIDGET (self->printer_location_label), FALSE); gtk_widget_set_visible (GTK_WIDGET (self->printer_location_address_label), FALSE); + gtk_label_set_text (self->printer_location_address_label, ""); } else { + gtk_widget_set_visible (GTK_WIDGET (self->printer_location_label), TRUE); + gtk_widget_set_visible (GTK_WIDGET (self->printer_location_address_label), TRUE); gtk_label_set_text (self->printer_location_address_label, location); } + if (web_interface != NULL || device_uri == NULL) + { + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.options", FALSE); + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.details", FALSE); + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.remove", FALSE); + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.default", FALSE); + } + + if (printer_commands != NULL) + { + gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.webinterface", FALSE); + } + ink_supply_is_empty = supply_level_is_empty (self); gtk_widget_set_visible (GTK_WIDGET (self->printer_inklevel_label), !ink_supply_is_empty); gtk_widget_set_visible (GTK_WIDGET (self->supply_frame), !ink_supply_is_empty); - pp_printer_entry_update_jobs_count (self); + if (self->is_system_printer) + gtk_widget_add_css_class (GTK_WIDGET (self), "system-printer"); + else + gtk_widget_remove_css_class (GTK_WIDGET (self), "system-printer"); - gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.default", self->is_authorized); - gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.remove", self->is_authorized); + pp_printer_entry_update_jobs_count (self); } static void @@ -1036,6 +1158,7 @@ pp_printer_entry_class_init (PpPrinterEntryClass *klass) gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, header_box); gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_name_label); gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_status); + gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_status_label); gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_model_label); gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_model); gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_location_label); @@ -1104,4 +1227,6 @@ pp_printer_entry_class_init (PpPrinterEntryClass *klass) (GtkWidgetActionActivateFunc) printer_clean_heads_cb); gtk_widget_class_install_action (widget_class, "printer.remove", NULL, (GtkWidgetActionActivateFunc) printer_remove_cb); + gtk_widget_class_install_action (widget_class, "printer.webinterface", NULL, + on_click_web_interface_action); } diff --git a/panels/printers/pp-printer-entry.h b/panels/printers/pp-printer-entry.h index 1c6b3dced4..a4225e6e00 100644 --- a/panels/printers/pp-printer-entry.h +++ b/panels/printers/pp-printer-entry.h @@ -30,6 +30,10 @@ PpPrinterEntry *pp_printer_entry_new (cups_dest_t printer, const gchar *pp_printer_entry_get_name (PpPrinterEntry *self); +const gchar *pp_printer_entry_get_hostname (PpPrinterEntry *self); + +const gchar *pp_printer_entry_get_web_interface (PpPrinterEntry *self); + const gchar *pp_printer_entry_get_location (PpPrinterEntry *self); void pp_printer_entry_update_jobs_count (PpPrinterEntry *self); -- GitLab