/* GTK - The GIMP Toolkit * gtkprintbackendpapi.c: Default implementation of GtkPrintBackend * for printing to papi * Copyright (C) 2003, Red Hat, Inc. * Copyright (C) 2009, Sun Microsystems, Inc. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "gtk.h" #include "gtkprintbackendpapi.h" #include "gtkprinterpapi.h" #include "gtkprinter-private.h" typedef struct _GtkPrintBackendPapiClass GtkPrintBackendPapiClass; #define GTK_PRINT_BACKEND_PAPI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_PAPI, GtkPrintBackendPapiClass)) #define GTK_IS_PRINT_BACKEND_PAPI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_PAPI)) #define GTK_PRINT_BACKEND_PAPI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_PAPI, GtkPrintBackendPapiClass)) #define _PAPI_MAX_CHUNK_SIZE 8192 static GType print_backend_papi_type = 0; struct _GtkPrintBackendPapiClass { GtkPrintBackendClass parent_class; }; struct _GtkPrintBackendPapi { GtkPrintBackend parent_instance; char *default_printer; }; typedef struct { GtkPrinter *printer; } _PrinterStatus; static GObjectClass *backend_parent_class; static void gtk_print_backend_papi_class_init (GtkPrintBackendPapiClass *class); static void gtk_print_backend_papi_init (GtkPrintBackendPapi *impl); static void gtk_print_backend_papi_finalize (GObject *object); static void gtk_print_backend_papi_dispose (GObject *object); static void papi_request_printer_list (GtkPrintBackend *print_backend); static gboolean papi_get_printer_list (GtkPrintBackendPapi *papi_backend); static void papi_printer_request_details (GtkPrinter *printer); static GtkPrintCapabilities papi_printer_get_capabilities (GtkPrinter *printer); static void papi_printer_get_settings_from_options (GtkPrinter *printer, GtkPrinterOptionSet *options, GtkPrintSettings *settings); static GtkPrinterOptionSet *papi_printer_get_options (GtkPrinter *printer, GtkPrintSettings *settings, GtkPageSetup *page_setup, GtkPrintCapabilities capabilities); static void papi_printer_prepare_for_print (GtkPrinter *printer, GtkPrintJob *print_job, GtkPrintSettings *settings, GtkPageSetup *page_setup); static cairo_surface_t * papi_printer_create_cairo_surface (GtkPrinter *printer, GtkPrintSettings *settings, gdouble width, gdouble height, GIOChannel *cache_io); static void gtk_print_backend_papi_print_stream (GtkPrintBackend *print_backend, GtkPrintJob *job, GIOChannel *data_io, GtkPrintJobCompleteFunc callback, gpointer user_data, GDestroyNotify dnotify); static gboolean papi_display_printer_status (gpointer user_data); static void papi_display_printer_status_done (gpointer user_data); static void gtk_print_backend_papi_register_type (GTypeModule *module) { const GTypeInfo print_backend_papi_info = { sizeof (GtkPrintBackendPapiClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) gtk_print_backend_papi_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GtkPrintBackendPapi), 0, /* n_preallocs */ (GInstanceInitFunc) gtk_print_backend_papi_init, }; print_backend_papi_type = g_type_module_register_type (module, GTK_TYPE_PRINT_BACKEND, "GtkPrintBackendPapi", &print_backend_papi_info, 0); } G_MODULE_EXPORT void pb_module_init (GTypeModule *module) { gtk_print_backend_papi_register_type (module); gtk_printer_papi_register_type (module); } G_MODULE_EXPORT void pb_module_exit (void) { } G_MODULE_EXPORT GtkPrintBackend * pb_module_create (void) { return gtk_print_backend_papi_new (); } /* * GtkPrintBackendPapi */ GType gtk_print_backend_papi_get_type (void) { return print_backend_papi_type; } /** * gtk_print_backend_papi_new: * * Creates a new #GtkPrintBackendPapi object. #GtkPrintBackendPapi * implements the #GtkPrintBackend interface with direct access to * the filesystem using Unix/Linux API calls * * Return value: the new #GtkPrintBackendPapi object **/ GtkPrintBackend * gtk_print_backend_papi_new (void) { return g_object_new (GTK_TYPE_PRINT_BACKEND_PAPI, NULL); } static void gtk_print_backend_papi_class_init (GtkPrintBackendPapiClass *class) { GObjectClass *gobject_class = G_OBJECT_CLASS (class); GtkPrintBackendClass *backend_class = GTK_PRINT_BACKEND_CLASS (class); backend_parent_class = g_type_class_peek_parent (class); gobject_class->finalize = gtk_print_backend_papi_finalize; gobject_class->dispose = gtk_print_backend_papi_dispose; backend_class->request_printer_list = papi_request_printer_list; backend_class->printer_request_details = papi_printer_request_details; backend_class->printer_get_capabilities = papi_printer_get_capabilities; backend_class->printer_get_options = papi_printer_get_options; backend_class->printer_get_settings_from_options = papi_printer_get_settings_from_options; backend_class->printer_prepare_for_print = papi_printer_prepare_for_print; backend_class->printer_create_cairo_surface = papi_printer_create_cairo_surface; backend_class->print_stream = gtk_print_backend_papi_print_stream; } static cairo_status_t _cairo_write (void *closure, const unsigned char *data, unsigned int length) { GIOChannel *io = (GIOChannel *)closure; gsize written; GError *error = NULL; GTK_NOTE (PRINTING, g_print ("PAPI Backend: Writting %i byte chunk to temp file\n", length)); while (length > 0) { g_io_channel_write_chars (io, (char *)data, length, &written, &error); if (error != NULL) { GTK_NOTE (PRINTING, g_print ("PAPI Backend: Error writting to temp file, %s\n", error->message)); g_error_free (error); return CAIRO_STATUS_WRITE_ERROR; } GTK_NOTE (PRINTING, g_print ("PAPI Backend: Wrote %i bytes to temp file\n", written)); data += written; length -= written; } return CAIRO_STATUS_SUCCESS; } static cairo_surface_t * papi_printer_create_cairo_surface (GtkPrinter *printer, GtkPrintSettings *settings, gdouble width, gdouble height, GIOChannel *cache_io) { cairo_surface_t *surface; surface = cairo_ps_surface_create_for_stream (_cairo_write, cache_io, width, height); cairo_surface_set_fallback_resolution (surface, 2.0 * gtk_print_settings_get_printer_lpi (settings), 2.0 * gtk_print_settings_get_printer_lpi (settings)); return surface; } typedef struct { GtkPrintBackend *backend; GtkPrintJobCompleteFunc callback; GtkPrintJob *job; gpointer user_data; GDestroyNotify dnotify; papi_service_t service; papi_stream_t stream; } _PrintStreamData; static void papi_print_cb (GtkPrintBackendPapi *print_backend, GError *error, gpointer user_data) { _PrintStreamData *ps = (_PrintStreamData *) user_data; if (ps->callback) ps->callback (ps->job, ps->user_data, error); if (ps->dnotify) ps->dnotify (ps->user_data); gtk_print_job_set_status (ps->job, error ? GTK_PRINT_STATUS_FINISHED_ABORTED : GTK_PRINT_STATUS_FINISHED); if (ps->job) g_object_unref (ps->job); g_free (ps); } static gboolean papi_write (GIOChannel *source, GIOCondition con, gpointer user_data) { gchar buf[_PAPI_MAX_CHUNK_SIZE]; gsize bytes_read; GError *error; GIOStatus status; _PrintStreamData *ps = (_PrintStreamData *) user_data; papi_job_t job = NULL; error = NULL; status = g_io_channel_read_chars (source, buf, _PAPI_MAX_CHUNK_SIZE, &bytes_read, &error); /* Keep writing to PAPI input stream while there are data */ if (status != G_IO_STATUS_ERROR) { papiJobStreamWrite (ps->service, ps->stream, buf, bytes_read); } /* Finish reading input stream data. Closing the stream and handle to service */ if (bytes_read == 0) { papiJobStreamClose (ps->service, ps->stream, &job); ps->stream = NULL; papiJobFree (job); papiServiceDestroy (ps->service); ps->service = NULL; } if (error != NULL || status == G_IO_STATUS_EOF) { papi_print_cb (GTK_PRINT_BACKEND_PAPI (ps->backend), error, user_data); if (error) g_error_free (error); if (error != NULL) { GTK_NOTE (PRINTING, g_print ("PAPI Backend: %s\n", error->message)); g_error_free (error); } return FALSE; } GTK_NOTE (PRINTING, g_print ("PAPI Backend: Writting %i byte chunk to papi pipe\n", bytes_read)); return TRUE; } static void gtk_print_backend_papi_print_stream (GtkPrintBackend *print_backend, GtkPrintJob *job, GIOChannel *data_io, GtkPrintJobCompleteFunc callback, gpointer user_data, GDestroyNotify dnotify) { GError *print_error = NULL; GtkPrinterPapi *printer; _PrintStreamData *ps; GtkPrintSettings *settings; gint argc; gint in_fd; gchar **argv = NULL; const gchar *title; char *prtnm = NULL; GtkPrintDuplex val; papi_status_t pstatus = NULL; papi_attribute_t **attrs = NULL; papi_job_ticket_t *ticket = NULL; printer = GTK_PRINTER_PAPI (gtk_print_job_get_printer (job)); settings = gtk_print_job_get_settings (job); /* FIXME - the title should be set as the job-name */ title = gtk_print_job_get_title (job); ps = g_new0 (_PrintStreamData, 1); ps->callback = callback; ps->user_data = user_data; ps->dnotify = dnotify; ps->job = g_object_ref (job); ps->service = NULL; ps->stream = NULL; /* This cannot be queried yet with the current API */ papiAttributeListAddString (&attrs, PAPI_ATTR_EXCL, "document-format", "application/postscript"); val = gtk_print_settings_get_duplex (settings) ; if (val == GTK_PRINT_DUPLEX_HORIZONTAL) papiAttributeListAddString (&attrs, PAPI_ATTR_EXCL, "Duplex", "DuplexNoTumble"); else if (val == GTK_PRINT_DUPLEX_VERTICAL) papiAttributeListAddString (&attrs, PAPI_ATTR_EXCL, "Duplex", "DuplexTumble"); if (job->num_copies > 1) { papiAttributeListAddInteger (&attrs, PAPI_ATTR_EXCL, "copies", job->num_copies); } prtnm = strdup (gtk_printer_get_name (GTK_PRINTER(printer))); if (papiServiceCreate (&(ps->service), prtnm, NULL, NULL, NULL, PAPI_ENCRYPT_NEVER, NULL) != PAPI_OK) return; pstatus = papiJobStreamOpen (ps->service, prtnm, attrs, ticket, &(ps->stream)); if (pstatus != PAPI_OK) { papiServiceDestroy (ps->service); ps->service = NULL; return; } /* Everything set up fine, so get ready to wait for input data stream */ g_io_add_watch (data_io, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP, (GIOFunc) papi_write, ps); } static void _papi_set_default_printer (GtkPrintBackendPapi *backend) { char *def_printer = NULL; char *_default_attr[] = { "printer-name", NULL }; papi_service_t service = NULL; papi_printer_t default_printer = NULL; papi_attribute_t **attrs = NULL; if (papiServiceCreate (&service, NULL, NULL, NULL, NULL, PAPI_ENCRYPT_NEVER, NULL) != PAPI_OK) return; if (papiPrinterQuery (service, "_default", _default_attr, NULL, &default_printer) == PAPI_OK) { if (default_printer != NULL) { attrs = papiPrinterGetAttributeList (default_printer); if (attrs != NULL) if (papiAttributeListGetString (attrs, NULL, "printer-name", &def_printer) == PAPI_OK) { backend->default_printer = strdup (def_printer); } } } papiPrinterFree (default_printer); papiServiceDestroy (service); } static void gtk_print_backend_papi_init (GtkPrintBackendPapi *backend) { _papi_set_default_printer (backend); } static void gtk_print_backend_papi_finalize (GObject *object) { GtkPrintBackendPapi *backend_papi; GTK_NOTE (PRINTING, g_print ("PAPI Backend: finalizing PAPI backend module\n")); backend_papi = GTK_PRINT_BACKEND_PAPI (object); g_free (backend_papi->default_printer); backend_papi->default_printer = NULL; backend_parent_class->finalize (object); } static void gtk_print_backend_papi_dispose (GObject *object) { GtkPrintBackendPapi *backend_papi; GTK_NOTE (PRINTING, g_print ("PAPI Backend: %s\n", G_STRFUNC)); backend_papi = GTK_PRINT_BACKEND_PAPI (object); backend_parent_class->dispose (object); } char ** get_all_list(papi_service_t svc) { papi_status_t status; papi_printer_t printer = NULL; char *attr[] = { "member-names", NULL }; char **names = NULL; status = papiPrinterQuery(svc, "_all", attr, NULL, &printer); if ((status == PAPI_OK) && (printer != NULL)) { papi_attribute_t **attributes = papiPrinterGetAttributeList(printer); if (attributes != NULL) { void *iter = NULL; char *member = NULL; for (status = papiAttributeListGetString(attributes, &iter, "member-names", &member); status == PAPI_OK; status = papiAttributeListGetString(attributes, &iter, NULL, &member)) list_append(&names, strdup(member)); } papiPrinterFree(printer); } return (names); } static char ** get_printers_list(papi_service_t svc) { papi_status_t status; papi_printer_t *printers = NULL; char *keys[] = { "printer-name", "printer-uri-supported", NULL }; char **names = NULL; status = papiPrintersList(svc, keys, NULL, &printers); if ((status == PAPI_OK) && (printers != NULL)) { int i; for (i = 0; printers[i] != NULL; i++) { papi_attribute_t **attributes = papiPrinterGetAttributeList(printers[i]); char *name = NULL; (void) papiAttributeListGetString(attributes, NULL, "printer-name", &name); if ((name != NULL) && (strcmp(name, "_default") != 0)) list_append(&names, strdup(name)); } papiPrinterListFree(printers); } return (names); } static void papi_request_printer_list (GtkPrintBackend *backend) { GtkPrintBackendPapi *papi_backend; papi_backend = GTK_PRINT_BACKEND_PAPI (backend); /* Get the list of printers using papi API */ papi_get_printer_list (papi_backend); } static gboolean papi_get_printer_list (GtkPrintBackendPapi *papi_backend) { int i; const char *attributes[] = /* Attributes we're interested in */ { "printer-name", "printer-uri-supported", NULL }; papi_status_t status, status2; papi_service_t service = NULL; char **printers = NULL; GtkPrinter *printer; GtkPrinterPapi *papi_printer; GList *current_printer_list; GtkPrintBackend *backend = GTK_PRINT_BACKEND (papi_backend); if ((status = papiServiceCreate (&service, NULL, NULL, NULL, NULL, PAPI_ENCRYPT_NEVER, NULL)) != PAPI_OK) return FALSE; if ((printers = get_all_list (service)) == NULL) { printers = get_printers_list (service); } if (printers == NULL) { papiServiceDestroy (service); return FALSE; } for (i = 0; printers[i] != NULL; i++) { GtkPrinter *printer; char *name = NULL, *url = NULL; papi_attribute_t **attrs = NULL; printer = gtk_print_backend_find_printer (backend, printers[i]); if (!printer) { /* skip null printer name just in case */ if (printers[i] == NULL) continue; /* skip the alias _default and _all printers */ if (strcmp(printers[i], "_default")==0 || strcmp(printers[i], "_all")==0) continue; papi_printer = gtk_printer_papi_new (printers[i], backend); printer = GTK_PRINTER (papi_printer); /* Only marked default printer to not have details so that the request_details method will be called at start up */ if (papi_backend->default_printer != NULL) if (strcmp (printers[i], papi_backend->default_printer)==0) { gtk_printer_set_is_default (printer, TRUE); } gtk_printer_set_icon_name (printer, "printer"); gtk_print_backend_add_printer (backend, printer); gtk_printer_set_is_active (printer, TRUE); /* gtk_printer_set_has_details (printer, TRUE); */ } else g_object_ref (printer); if (!gtk_printer_is_active (printer)) { gtk_printer_set_is_active (printer, TRUE); gtk_printer_set_is_new (printer, TRUE); } if (gtk_printer_is_new (printer)) { g_signal_emit_by_name (backend, "printer-added", printer); gtk_printer_set_is_new (printer, FALSE); } g_object_unref (printer); } free (printers); papiServiceDestroy (service); /* To set that the list of printers added is complete */ gtk_print_backend_set_list_done (backend); return TRUE; } static void update_printer_status (GtkPrinter *printer) { GtkPrintBackend *backend; GtkPrinterPapi *papi_printer; gboolean status_changed = FALSE; backend = gtk_printer_get_backend (printer); papi_printer = GTK_PRINTER_PAPI (printer); /* if (status_changed) */ g_signal_emit_by_name (GTK_PRINT_BACKEND (backend), "printer-status-changed", printer); } static GtkPrinterOptionSet * papi_printer_get_options (GtkPrinter *printer, GtkPrintSettings *settings, GtkPageSetup *page_setup, GtkPrintCapabilities capabilities) { GtkPrinterOptionSet *set; GtkPrinterOption *option; int i; char *print_at[] = { "now", "on-hold" }; char *n_up[] = {"1"}; /* Update the printer status before the printer options are displayed */ update_printer_status (printer); set = gtk_printer_option_set_new (); /* non-ppd related settings */ /* This maps to number-up-supported in PAPI. FIXME * number-up-default is the default value. * number-up-supported is the list of number of able to print per page */ option = gtk_printer_option_new ("gtk-n-up", "Pages Per Sheet", GTK_PRINTER_OPTION_TYPE_PICKONE); gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (n_up), n_up, n_up); gtk_printer_option_set (option, "1"); gtk_printer_option_set_add (set, option); g_object_unref (option); /* This maps to job-priority-supported and job-priority-default in PAPI - FIXME*/ /* This relates to job-sheets-supported in PAPI FIXME*/ /* This relates to job-hold-until-supported in PAPI */ option = gtk_printer_option_new ("gtk-print-time", "Print at", GTK_PRINTER_OPTION_TYPE_PICKONE); gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (print_at), print_at, print_at); gtk_printer_option_set (option, "now"); gtk_printer_option_set_add (set, option); g_object_unref (option); return set; } static void papi_printer_get_settings_from_options (GtkPrinter *printer, GtkPrinterOptionSet *options, GtkPrintSettings *settings) { GtkPrinterOption *option; option = gtk_printer_option_set_lookup (options, "gtk-n-up"); if (option) gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_NUMBER_UP, option->value); } static void papi_printer_prepare_for_print (GtkPrinter *printer, GtkPrintJob *print_job, GtkPrintSettings *settings, GtkPageSetup *page_setup) { GtkPageSet page_set; double scale; GtkPaperSize *papersize = NULL; char *ppd_paper_name; print_job->print_pages = gtk_print_settings_get_print_pages (settings); print_job->page_ranges = NULL; print_job->num_page_ranges = 0; if (print_job->print_pages == GTK_PRINT_PAGES_RANGES) print_job->page_ranges = gtk_print_settings_get_page_ranges (settings, &print_job->num_page_ranges); print_job->collate = gtk_print_settings_get_collate (settings); print_job->reverse = gtk_print_settings_get_reverse (settings); print_job->num_copies = gtk_print_settings_get_n_copies (settings); scale = gtk_print_settings_get_scale (settings); if (scale != 100.0) print_job->scale = scale/100.0; papersize = gtk_page_setup_get_paper_size (page_setup); ppd_paper_name = gtk_paper_size_get_ppd_name (papersize); page_set = gtk_print_settings_get_page_set (settings); if (page_set == GTK_PAGE_SET_EVEN) print_job->page_set = GTK_PAGE_SET_EVEN; else if (page_set == GTK_PAGE_SET_ODD) print_job->page_set = GTK_PAGE_SET_ODD; else print_job->page_set = GTK_PAGE_SET_ALL; print_job->rotate_to_orientation = TRUE; } gboolean is_local_printer (gchar *printer_uri) { if (strncmp (printer_uri, "lpsched:", 8) == 0) return TRUE; else return FALSE; } void merge_ppd_data (papi_attribute_t ***attributes, gchar *ppdfile) { get_ppd_attrs (attributes, ppdfile); } static void papi_display_printer_status_done (gpointer user_data) { GtkPrinter *printer = (GtkPrinter *) user_data; GtkPrinterPapi *papi_printer; g_signal_emit_by_name (printer, "details-acquired", TRUE); papi_printer = GTK_PRINTER_PAPI (printer); return; } #define IDLE 3 #define PROCESSING 4 #define STOPPED 5 static gboolean papi_display_printer_status (gpointer user_data) { GtkPrinter *printer = (GtkPrinter *) user_data; GtkPrinterPapi *papi_printer; gchar *loc, *printer_uri, *ppdfile; int state; papi_service_t service; papi_attribute_t **attrs = NULL; papi_printer_t current_printer = NULL; static int count = 0; papi_printer = GTK_PRINTER_PAPI (printer); if (papiServiceCreate (&service, NULL, NULL, NULL, NULL, PAPI_ENCRYPT_NEVER, NULL) != PAPI_OK) return FALSE; if (papiPrinterQuery (service, papi_printer->printer_name, NULL, NULL, ¤t_printer) != PAPI_OK) { /* SUN_BRANDING */ gtk_printer_set_state_message (printer, _("printer offline")); } if (current_printer != NULL) { attrs = papiPrinterGetAttributeList (current_printer); } if (papiAttributeListGetString (attrs, NULL, "printer-info", &loc) == PAPI_OK) { gtk_printer_set_location (printer, loc); } if (papiAttributeListGetInteger (attrs, NULL, "printer-state", &state) == PAPI_OK) { switch (state) { /* SUN_BRANDING */ case IDLE: gtk_printer_set_state_message (printer, _("ready to print")); break; /* SUN_BRANDING */ case PROCESSING: gtk_printer_set_state_message (printer, _("processing job")); break; /* SUN_BRANDING */ case STOPPED: gtk_printer_set_state_message (printer, _("paused")); break; /* SUN_BRANDING */ default: gtk_printer_set_state_message (printer, _("unknown")); break; } } papiPrinterFree (current_printer); papiServiceDestroy (service); gtk_printer_set_has_details (printer, TRUE); return FALSE; } static void papi_printer_request_details (GtkPrinter *printer) { g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, papi_display_printer_status, printer, papi_display_printer_status_done); } static GtkPrintCapabilities papi_printer_get_capabilities (GtkPrinter *printer) { return GTK_PRINT_CAPABILITY_COPIES | GTK_PRINT_CAPABILITY_PAGE_SET ; }