summaryrefslogtreecommitdiff
path: root/modules/printbackends/cups
diff options
context:
space:
mode:
Diffstat (limited to 'modules/printbackends/cups')
-rw-r--r--modules/printbackends/cups/Makefile.am34
-rw-r--r--modules/printbackends/cups/gtkcupsutils.c922
-rw-r--r--modules/printbackends/cups/gtkcupsutils.h125
-rw-r--r--modules/printbackends/cups/gtkprintbackendcups.c2629
-rw-r--r--modules/printbackends/cups/gtkprintbackendcups.h42
-rw-r--r--modules/printbackends/cups/gtkprintercups.c125
-rw-r--r--modules/printbackends/cups/gtkprintercups.h70
7 files changed, 3947 insertions, 0 deletions
diff --git a/modules/printbackends/cups/Makefile.am b/modules/printbackends/cups/Makefile.am
new file mode 100644
index 0000000000..0174598256
--- /dev/null
+++ b/modules/printbackends/cups/Makefile.am
@@ -0,0 +1,34 @@
+if OS_WIN32
+no_undefined = -no-undefined
+endif
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/gtk \
+ -I$(top_builddir)/gtk \
+ -I$(top_srcdir)/gdk \
+ -I$(top_builddir)/gdk \
+ $(CUPS_CFLAGS) \
+ -DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED \
+ $(GTK_DEP_CFLAGS)
+
+LDADDS = \
+ $(GTK_DEP_LIBS) \
+ $(top_builddir)/gtk/$(gtktargetlib)
+
+backenddir = $(libdir)/gtk-2.0/$(GTK_BINARY_VERSION)/printbackends
+
+backend_LTLIBRARIES = libprintbackend-cups.la
+
+libprintbackend_cups_la_SOURCES = \
+ gtkprintbackendcups.c \
+ gtkprintercups.c \
+ gtkcupsutils.c
+
+noinst_HEADERS = \
+ gtkprintbackendcups.h \
+ gtkprintercups.h \
+ gtkcupsutils.h
+
+libprintbackend_cups_la_LDFLAGS = -avoid-version -module $(no_undefined)
+libprintbackend_cups_la_LIBADD = $(LDADDS) $(CUPS_LIBS)
diff --git a/modules/printbackends/cups/gtkcupsutils.c b/modules/printbackends/cups/gtkcupsutils.c
new file mode 100644
index 0000000000..458e1c4219
--- /dev/null
+++ b/modules/printbackends/cups/gtkcupsutils.c
@@ -0,0 +1,922 @@
+/* GTK - The GIMP Toolkit
+ * gtkcupsutils.h: Statemachine implementation of POST and GET
+ * cup calls which can be used to create a non-blocking cups API
+ * Copyright (C) 2003, Red Hat, 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 "gtkcupsutils.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <time.h>
+
+typedef void (*GtkCupsRequestStateFunc) (GtkCupsRequest *request);
+
+static void _connect (GtkCupsRequest *request);
+static void _post_send (GtkCupsRequest *request);
+static void _post_write_request (GtkCupsRequest *request);
+static void _post_write_data (GtkCupsRequest *request);
+static void _post_check (GtkCupsRequest *request);
+static void _post_read_response (GtkCupsRequest *request);
+
+static void _get_send (GtkCupsRequest *request);
+static void _get_check (GtkCupsRequest *request);
+static void _get_read_data (GtkCupsRequest *request);
+
+struct _GtkCupsResult
+{
+ gchar *error_msg;
+ ipp_t *ipp_response;
+
+ guint is_error : 1;
+ guint is_ipp_response : 1;
+};
+
+
+#define _GTK_CUPS_MAX_ATTEMPTS 10
+#define _GTK_CUPS_MAX_CHUNK_SIZE 8192
+
+GtkCupsRequestStateFunc post_states[] = {_connect,
+ _post_send,
+ _post_write_request,
+ _post_write_data,
+ _post_check,
+ _post_read_response};
+
+GtkCupsRequestStateFunc get_states[] = {_connect,
+ _get_send,
+ _get_check,
+ _get_read_data};
+
+static void
+gtk_cups_result_set_error (GtkCupsResult *result,
+ const char *error_msg,
+ ...)
+{
+ va_list args;
+
+ result->is_ipp_response = FALSE;
+
+ result->is_error = TRUE;
+
+ va_start (args, error_msg);
+ result->error_msg = g_strdup_vprintf (error_msg, args);
+ va_end (args);
+}
+
+GtkCupsRequest *
+gtk_cups_request_new (http_t *connection,
+ GtkCupsRequestType req_type,
+ gint operation_id,
+ gint data_fd,
+ const char *server,
+ const char *resource)
+{
+ GtkCupsRequest *request;
+ cups_lang_t *language;
+
+ request = g_new0 (GtkCupsRequest, 1);
+ request->result = g_new0 (GtkCupsResult, 1);
+
+ request->result->error_msg = NULL;
+ request->result->ipp_response = NULL;
+
+ request->result->is_error = FALSE;
+ request->result->is_ipp_response = FALSE;
+
+ request->type = req_type;
+ request->state = GTK_CUPS_REQUEST_START;
+
+ if (server)
+ request->server = g_strdup (server);
+ else
+ request->server = g_strdup (cupsServer());
+
+
+ if (resource)
+ request->resource = g_strdup (resource);
+ else
+ request->resource = g_strdup ("/");
+
+ if (connection != NULL)
+ {
+ request->http = connection;
+ request->own_http = FALSE;
+ }
+ else
+ {
+ request->http = NULL;
+ request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
+
+ if (request->http)
+ httpBlocking (request->http, 0);
+
+ request->own_http = TRUE;
+ }
+
+ request->last_status = HTTP_CONTINUE;
+
+ request->attempts = 0;
+ request->data_fd = data_fd;
+
+ request->ipp_request = ippNew();
+ request->ipp_request->request.op.operation_id = operation_id;
+ request->ipp_request->request.op.request_id = 1;
+
+ language = cupsLangDefault ();
+
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
+ "attributes-charset",
+ NULL, "utf-8");
+
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
+ "attributes-natural-language",
+ NULL, language->language);
+
+ cupsLangFree (language);
+
+ return request;
+}
+
+static void
+gtk_cups_result_free (GtkCupsResult *result)
+{
+ g_free (result->error_msg);
+
+ if (result->ipp_response)
+ ippDelete (result->ipp_response);
+
+ g_free (result);
+}
+
+void
+gtk_cups_request_free (GtkCupsRequest *request)
+{
+ if (request->own_http)
+ if (request->http)
+ httpClose (request->http);
+
+ if (request->ipp_request)
+ ippDelete (request->ipp_request);
+
+ g_free (request->server);
+ g_free (request->resource);
+
+ gtk_cups_result_free (request->result);
+
+ g_free (request);
+}
+
+gboolean
+gtk_cups_request_read_write (GtkCupsRequest *request)
+{
+ if (request->type == GTK_CUPS_POST)
+ post_states[request->state](request);
+ else if (request->type == GTK_CUPS_GET)
+ get_states[request->state](request);
+
+ if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS &&
+ request->state != GTK_CUPS_REQUEST_DONE)
+ {
+ gtk_cups_result_set_error (request->result, "Too many failed attempts");
+ request->state = GTK_CUPS_REQUEST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ }
+
+ if (request->state == GTK_CUPS_REQUEST_DONE)
+ {
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+GtkCupsPollState
+gtk_cups_request_get_poll_state (GtkCupsRequest *request)
+{
+ return request->poll_state;
+}
+
+
+
+GtkCupsResult *
+gtk_cups_request_get_result (GtkCupsRequest *request)
+{
+ return request->result;
+}
+
+void
+gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
+ ipp_tag_t group,
+ ipp_tag_t tag,
+ const char *name,
+ const char *charset,
+ const char *value)
+{
+ ippAddString (request->ipp_request,
+ group,
+ tag,
+ name,
+ charset,
+ value);
+}
+
+typedef struct
+{
+ const char *name;
+ ipp_tag_t value_tag;
+} ipp_option_t;
+
+static const ipp_option_t ipp_options[] =
+ {
+ { "blackplot", IPP_TAG_BOOLEAN },
+ { "brightness", IPP_TAG_INTEGER },
+ { "columns", IPP_TAG_INTEGER },
+ { "copies", IPP_TAG_INTEGER },
+ { "finishings", IPP_TAG_ENUM },
+ { "fitplot", IPP_TAG_BOOLEAN },
+ { "gamma", IPP_TAG_INTEGER },
+ { "hue", IPP_TAG_INTEGER },
+ { "job-k-limit", IPP_TAG_INTEGER },
+ { "job-page-limit", IPP_TAG_INTEGER },
+ { "job-priority", IPP_TAG_INTEGER },
+ { "job-quota-period", IPP_TAG_INTEGER },
+ { "landscape", IPP_TAG_BOOLEAN },
+ { "media", IPP_TAG_KEYWORD },
+ { "mirror", IPP_TAG_BOOLEAN },
+ { "natural-scaling", IPP_TAG_INTEGER },
+ { "number-up", IPP_TAG_INTEGER },
+ { "orientation-requested", IPP_TAG_ENUM },
+ { "page-bottom", IPP_TAG_INTEGER },
+ { "page-left", IPP_TAG_INTEGER },
+ { "page-ranges", IPP_TAG_RANGE },
+ { "page-right", IPP_TAG_INTEGER },
+ { "page-top", IPP_TAG_INTEGER },
+ { "penwidth", IPP_TAG_INTEGER },
+ { "ppi", IPP_TAG_INTEGER },
+ { "prettyprint", IPP_TAG_BOOLEAN },
+ { "printer-resolution", IPP_TAG_RESOLUTION },
+ { "print-quality", IPP_TAG_ENUM },
+ { "saturation", IPP_TAG_INTEGER },
+ { "scaling", IPP_TAG_INTEGER },
+ { "sides", IPP_TAG_KEYWORD },
+ { "wrap", IPP_TAG_BOOLEAN }
+ };
+
+
+static ipp_tag_t
+_find_option_tag (const gchar *option)
+{
+ int lower_bound, upper_bound, num_options;
+ int current_option;
+ ipp_tag_t result;
+
+ result = IPP_TAG_ZERO;
+
+ lower_bound = 0;
+ upper_bound = num_options = (int)(sizeof(ipp_options) / sizeof(ipp_options[0])) - 1;
+
+ while (1)
+ {
+ int match;
+ current_option = (int) (((upper_bound - lower_bound) / 2) + lower_bound);
+
+ match = strcasecmp(option, ipp_options[current_option].name);
+ if (match == 0)
+ {
+ result = ipp_options[current_option].value_tag;
+ return result;
+ }
+ else if (match < 0)
+ {
+ upper_bound = current_option - 1;
+ }
+ else
+ {
+ lower_bound = current_option + 1;
+ }
+
+ if (upper_bound == lower_bound && upper_bound == current_option)
+ return result;
+
+ if (upper_bound < 0)
+ return result;
+
+ if (lower_bound > num_options)
+ return result;
+
+ if (upper_bound < lower_bound)
+ return result;
+ }
+}
+
+void
+gtk_cups_request_encode_option (GtkCupsRequest *request,
+ const gchar *option,
+ const gchar *value)
+{
+ ipp_tag_t option_tag;
+
+ g_assert (option != NULL);
+ g_assert (value != NULL);
+
+ option_tag = _find_option_tag (option);
+
+ if (option_tag == IPP_TAG_ZERO)
+ {
+ option_tag = IPP_TAG_NAME;
+ if (strcasecmp (value, "true") == 0 ||
+ strcasecmp (value, "false") == 0)
+ {
+ option_tag = IPP_TAG_BOOLEAN;
+ }
+ }
+
+ switch (option_tag)
+ {
+ case IPP_TAG_INTEGER:
+ case IPP_TAG_ENUM:
+ ippAddInteger (request->ipp_request,
+ IPP_TAG_OPERATION,
+ option_tag,
+ option,
+ strtol (value, NULL, 0));
+ break;
+
+ case IPP_TAG_BOOLEAN:
+ {
+ char b;
+ b = 0;
+ if (!strcasecmp(value, "true") ||
+ !strcasecmp(value, "on") ||
+ !strcasecmp(value, "yes"))
+ b = 1;
+
+ ippAddBoolean(request->ipp_request,
+ IPP_TAG_OPERATION,
+ option,
+ b);
+
+ break;
+ }
+
+ case IPP_TAG_RANGE:
+ {
+ char *s;
+ int lower;
+ int upper;
+
+ if (*value == '-')
+ {
+ lower = 1;
+ s = (char *)value;
+ }
+ else
+ lower = strtol(value, &s, 0);
+
+ if (*s == '-')
+ {
+ if (s[1])
+ upper = strtol(s + 1, NULL, 0);
+ else
+ upper = 2147483647;
+ }
+ else
+ upper = lower;
+
+ ippAddRange (request->ipp_request,
+ IPP_TAG_OPERATION,
+ option,
+ lower,
+ upper);
+
+ break;
+ }
+
+ case IPP_TAG_RESOLUTION:
+ {
+ char *s;
+ int xres;
+ int yres;
+ ipp_res_t units;
+
+ xres = strtol(value, &s, 0);
+
+ if (*s == 'x')
+ yres = strtol(s + 1, &s, 0);
+ else
+ yres = xres;
+
+ if (strcasecmp(s, "dpc") == 0)
+ units = IPP_RES_PER_CM;
+ else
+ units = IPP_RES_PER_INCH;
+
+ ippAddResolution (request->ipp_request,
+ IPP_TAG_OPERATION,
+ option,
+ units,
+ xres,
+ yres);
+
+ break;
+ }
+
+ default:
+ ippAddString (request->ipp_request,
+ IPP_TAG_OPERATION,
+ option_tag,
+ option,
+ NULL,
+ value);
+
+ break;
+ }
+}
+
+
+static void
+_connect (GtkCupsRequest *request)
+{
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ if (request->http == NULL)
+ {
+ request->http = httpConnectEncrypt (request->server, ippPort(), cupsEncryption());
+
+ if (request->http == NULL)
+ request->attempts++;
+
+ if (request->http)
+ httpBlocking (request->http, 0);
+
+ request->own_http = TRUE;
+ }
+ else
+ {
+ request->attempts = 0;
+ request->state++;
+
+ /* we always write to the socket after we get
+ the connection */
+ request->poll_state = GTK_CUPS_HTTP_WRITE;
+ }
+}
+
+static void
+_post_send (GtkCupsRequest *request)
+{
+ gchar length[255];
+ struct stat data_info;
+
+ request->poll_state = GTK_CUPS_HTTP_WRITE;
+
+ if (request->data_fd != 0)
+ {
+ fstat (request->data_fd, &data_info);
+ sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request) + data_info.st_size);
+ }
+ else
+ {
+ sprintf (length, "%lu", (unsigned long)ippLength(request->ipp_request));
+ }
+
+ httpClearFields(request->http);
+ httpSetField(request->http, HTTP_FIELD_CONTENT_LENGTH, length);
+ httpSetField(request->http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+ httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+
+ if (httpPost(request->http, request->resource))
+ {
+ if (httpReconnect(request->http))
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "Failed Post");
+ }
+
+ request->attempts++;
+ return;
+ }
+
+ request->attempts = 0;
+
+ request->state = GTK_CUPS_POST_WRITE_REQUEST;
+ request->ipp_request->state = IPP_IDLE;
+}
+
+static void
+_post_write_request (GtkCupsRequest *request)
+{
+ ipp_state_t ipp_status;
+
+ request->poll_state = GTK_CUPS_HTTP_WRITE;
+
+ ipp_status = ippWrite(request->http, request->ipp_request);
+
+ if (ipp_status == IPP_ERROR)
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ()));
+ return;
+ }
+
+ if (ipp_status == IPP_DATA)
+ {
+ if (request->data_fd != 0)
+ request->state = GTK_CUPS_POST_WRITE_DATA;
+ else
+ {
+ request->state = GTK_CUPS_POST_CHECK;
+ request->poll_state = GTK_CUPS_HTTP_READ;
+ }
+ }
+}
+
+static void
+_post_write_data (GtkCupsRequest *request)
+{
+ ssize_t bytes;
+ char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
+ http_status_t http_status;
+
+ request->poll_state = GTK_CUPS_HTTP_WRITE;
+
+ if (httpCheck (request->http))
+ http_status = httpUpdate(request->http);
+ else
+ http_status = request->last_status;
+
+ request->last_status = http_status;
+
+
+ if (http_status == HTTP_CONTINUE || http_status == HTTP_OK)
+ {
+ /* send data */
+ bytes = read(request->data_fd, buffer, _GTK_CUPS_MAX_CHUNK_SIZE);
+
+ if (bytes == 0)
+ {
+ request->state = GTK_CUPS_POST_CHECK;
+ request->poll_state = GTK_CUPS_HTTP_READ;
+
+ request->attempts = 0;
+ return;
+ }
+ else if (bytes == -1)
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", strerror (errno));
+ return;
+ }
+
+ if (httpWrite(request->http, buffer, (int)bytes) < bytes)
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http)));
+ return;
+ }
+ }
+ else
+ {
+ request->attempts++;
+ }
+}
+
+static void
+_post_check (GtkCupsRequest *request)
+{
+ http_status_t http_status;
+
+ http_status = request->last_status;
+
+ request->poll_state = GTK_CUPS_HTTP_READ;
+
+ if (http_status == HTTP_CONTINUE)
+ {
+ goto again;
+ }
+ else if (http_status == HTTP_UNAUTHORIZED)
+ {
+ /* TODO: callout for auth */
+ g_warning ("NOT IMPLEMENTED: We need to prompt for authorization");
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
+ return;
+ }
+ else if (http_status == HTTP_ERROR)
+ {
+#ifdef G_OS_WIN32
+ if (request->http->error != WSAENETDOWN &&
+ request->http->error != WSAENETUNREACH)
+#else
+ if (request->http->error != ENETDOWN &&
+ request->http->error != ENETUNREACH)
+#endif /* G_OS_WIN32 */
+ {
+ request->attempts++;
+ goto again;
+ }
+ else
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "Unknown HTTP error");
+ return;
+ }
+ }
+/* TODO: detect ssl in configure.ac */
+#if HAVE_SSL
+ else if (http_status == HTTP_UPGRADE_REQUIRED)
+ {
+ /* Flush any error message... */
+ httpFlush (request->http);
+
+ /* Reconnect... */
+ httpReconnect (request->http);
+
+ /* Upgrade with encryption... */
+ httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
+
+ request->attempts++;
+ goto again;
+ }
+#endif
+ else if (http_status != HTTP_OK)
+ {
+ int http_errno;
+
+ http_errno = httpError (request->http);
+
+ if (http_errno == EPIPE)
+ request->state = GTK_CUPS_POST_CONNECT;
+ else
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno));
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ httpFlush(request->http);
+ return;
+ }
+
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ httpFlush(request->http);
+
+ request->last_status = HTTP_CONTINUE;
+ httpClose (request->http);
+ request->http = NULL;
+ return;
+ }
+ else
+ {
+ request->state = GTK_CUPS_POST_READ_RESPONSE;
+ return;
+ }
+
+ again:
+ http_status = HTTP_CONTINUE;
+
+ if (httpCheck (request->http))
+ http_status = httpUpdate (request->http);
+
+ request->last_status = http_status;
+}
+
+static void
+_post_read_response (GtkCupsRequest *request)
+{
+ ipp_state_t ipp_status;
+
+ request->poll_state = GTK_CUPS_HTTP_READ;
+
+ if (request->result->ipp_response == NULL)
+ request->result->ipp_response = ippNew();
+
+ ipp_status = ippRead (request->http,
+ request->result->ipp_response);
+
+ if (ipp_status == IPP_ERROR)
+ {
+ gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError()));
+
+ ippDelete (request->result->ipp_response);
+ request->result->ipp_response = NULL;
+
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ }
+ else if (ipp_status == IPP_DATA)
+ {
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ }
+}
+
+static void
+_get_send (GtkCupsRequest *request)
+{
+ request->poll_state = GTK_CUPS_HTTP_WRITE;
+
+ if (request->data_fd == 0)
+ {
+ gtk_cups_result_set_error (request->result, "Get requires an open file descriptor");
+ request->state = GTK_CUPS_GET_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ return;
+ }
+
+ httpClearFields(request->http);
+ httpSetField(request->http, HTTP_FIELD_AUTHORIZATION, request->http->authstring);
+
+ if (httpGet(request->http, request->resource))
+ {
+ if (httpReconnect(request->http))
+ {
+ request->state = GTK_CUPS_GET_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "Failed Get");
+ }
+
+ request->attempts++;
+ return;
+ }
+
+ request->attempts = 0;
+
+ request->state = GTK_CUPS_GET_CHECK;
+ request->poll_state = GTK_CUPS_HTTP_READ;
+
+ request->ipp_request->state = IPP_IDLE;
+}
+
+static void
+_get_check (GtkCupsRequest *request)
+{
+ http_status_t http_status;
+
+ http_status = request->last_status;
+
+ request->poll_state = GTK_CUPS_HTTP_READ;
+
+ if (http_status == HTTP_CONTINUE)
+ {
+ goto again;
+ }
+ else if (http_status == HTTP_UNAUTHORIZED)
+ {
+ /* TODO: callout for auth */
+ g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner");
+ request->state = GTK_CUPS_GET_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ gtk_cups_result_set_error (request->result, "Can't prompt for authorization");
+ return;
+ }
+/* TODO: detect ssl in configure.ac */
+#if HAVE_SSL
+ else if (http_status == HTTP_UPGRADE_REQUIRED)
+ {
+ /* Flush any error message... */
+ httpFlush (request->http);
+
+ /* Reconnect... */
+ httpReconnect (request->http);
+
+ /* Upgrade with encryption... */
+ httpEncryption(request->http, HTTP_ENCRYPT_REQUIRED);
+
+ request->attempts++;
+ goto again;
+ }
+#endif
+ else if (http_status != HTTP_OK)
+ {
+ int http_errno;
+
+ http_errno = httpError (request->http);
+
+ if (http_errno == EPIPE)
+ request->state = GTK_CUPS_GET_CONNECT;
+ else
+ {
+ request->state = GTK_CUPS_GET_DONE;
+ gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno));
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ httpFlush(request->http);
+
+ return;
+ }
+
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+ httpFlush (request->http);
+ httpClose (request->http);
+ request->last_status = HTTP_CONTINUE;
+ request->http = NULL;
+ return;
+
+ }
+ else
+ {
+ request->state = GTK_CUPS_GET_READ_DATA;
+ return;
+ }
+
+ again:
+ http_status = HTTP_CONTINUE;
+
+ if (httpCheck (request->http))
+ http_status = httpUpdate (request->http);
+
+ request->last_status = http_status;
+
+}
+
+static void
+_get_read_data (GtkCupsRequest *request)
+{
+ char buffer[_GTK_CUPS_MAX_CHUNK_SIZE];
+ int bytes;
+
+ request->poll_state = GTK_CUPS_HTTP_READ;
+
+ bytes = httpRead(request->http, buffer, sizeof(buffer));
+
+ if (bytes == 0)
+ {
+ request->state = GTK_CUPS_GET_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ return;
+ }
+
+ if (write (request->data_fd, buffer, bytes) == -1)
+ {
+ char *error_msg;
+
+ request->state = GTK_CUPS_POST_DONE;
+ request->poll_state = GTK_CUPS_HTTP_IDLE;
+
+ error_msg = strerror (errno);
+ gtk_cups_result_set_error (request->result, error_msg ? error_msg:"");
+ }
+}
+
+gboolean
+gtk_cups_request_is_done (GtkCupsRequest *request)
+{
+ return (request->state == GTK_CUPS_REQUEST_DONE);
+}
+
+gboolean
+gtk_cups_result_is_error (GtkCupsResult *result)
+{
+ return result->is_error;
+}
+
+ipp_t *
+gtk_cups_result_get_response (GtkCupsResult *result)
+{
+ return result->ipp_response;
+}
+
+const char *
+gtk_cups_result_get_error_string (GtkCupsResult *result)
+{
+ return result->error_msg;
+}
+
diff --git a/modules/printbackends/cups/gtkcupsutils.h b/modules/printbackends/cups/gtkcupsutils.h
new file mode 100644
index 0000000000..f49cd2e9bc
--- /dev/null
+++ b/modules/printbackends/cups/gtkcupsutils.h
@@ -0,0 +1,125 @@
+/* gtkcupsutils.h
+ * Copyright (C) 2006 John (J5) Palmieri <johnp@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CUPS_UTILS_H__
+#define __GTK_CUPS_UTILS_H__
+
+#include <glib.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/http.h>
+#include <cups/ipp.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkCupsRequest GtkCupsRequest;
+typedef struct _GtkCupsResult GtkCupsResult;
+
+typedef enum
+{
+ GTK_CUPS_POST,
+ GTK_CUPS_GET
+} GtkCupsRequestType;
+
+
+/**
+ * Direction we should be polling the http socket on.
+ * We are either reading or writting at each state.
+ * This makes it easy for mainloops to connect to poll.
+ */
+typedef enum
+{
+ GTK_CUPS_HTTP_IDLE,
+ GTK_CUPS_HTTP_READ,
+ GTK_CUPS_HTTP_WRITE
+} GtkCupsPollState;
+
+
+struct _GtkCupsRequest
+{
+ GtkCupsRequestType type;
+
+ http_t *http;
+ http_status_t last_status;
+ ipp_t *ipp_request;
+
+ gchar *server;
+ gchar *resource;
+ gint data_fd;
+ gint attempts;
+
+ GtkCupsResult *result;
+
+ gint state;
+ GtkCupsPollState poll_state;
+
+ gint own_http : 1;
+};
+
+#define GTK_CUPS_REQUEST_START 0
+#define GTK_CUPS_REQUEST_DONE 500
+
+/* POST states */
+enum
+{
+ GTK_CUPS_POST_CONNECT = GTK_CUPS_REQUEST_START,
+ GTK_CUPS_POST_SEND,
+ GTK_CUPS_POST_WRITE_REQUEST,
+ GTK_CUPS_POST_WRITE_DATA,
+ GTK_CUPS_POST_CHECK,
+ GTK_CUPS_POST_READ_RESPONSE,
+ GTK_CUPS_POST_DONE = GTK_CUPS_REQUEST_DONE
+};
+
+/* GET states */
+enum
+{
+ GTK_CUPS_GET_CONNECT = GTK_CUPS_REQUEST_START,
+ GTK_CUPS_GET_SEND,
+ GTK_CUPS_GET_CHECK,
+ GTK_CUPS_GET_READ_DATA,
+ GTK_CUPS_GET_DONE = GTK_CUPS_REQUEST_DONE
+};
+
+GtkCupsRequest * gtk_cups_request_new (http_t *connection,
+ GtkCupsRequestType req_type,
+ gint operation_id,
+ gint data_fd,
+ const char *server,
+ const char *resource);
+void gtk_cups_request_ipp_add_string (GtkCupsRequest *request,
+ ipp_tag_t group,
+ ipp_tag_t tag,
+ const char *name,
+ const char *charset,
+ const char *value);
+gboolean gtk_cups_request_read_write (GtkCupsRequest *request);
+GtkCupsPollState gtk_cups_request_get_poll_state (GtkCupsRequest *request);
+void gtk_cups_request_free (GtkCupsRequest *request);
+GtkCupsResult * gtk_cups_request_get_result (GtkCupsRequest *request);
+gboolean gtk_cups_request_is_done (GtkCupsRequest *request);
+void gtk_cups_request_encode_option (GtkCupsRequest *request,
+ const gchar *option,
+ const gchar *value);
+gboolean gtk_cups_result_is_error (GtkCupsResult *result);
+ipp_t * gtk_cups_result_get_response (GtkCupsResult *result);
+const char * gtk_cups_result_get_error_string (GtkCupsResult *result);
+
+G_END_DECLS
+#endif
diff --git a/modules/printbackends/cups/gtkprintbackendcups.c b/modules/printbackends/cups/gtkprintbackendcups.c
new file mode 100644
index 0000000000..79922a5670
--- /dev/null
+++ b/modules/printbackends/cups/gtkprintbackendcups.c
@@ -0,0 +1,2629 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcups.h: Default implementation of GtkPrintBackend
+ * for the Common Unix Print System (CUPS)
+ * Copyright (C) 2003, Red Hat, 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 <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+#include <config.h>
+#include <cups/cups.h>
+#include <cups/language.h>
+#include <cups/http.h>
+#include <cups/ipp.h>
+#include <errno.h>
+#include <cairo.h>
+#include <cairo-pdf.h>
+#include <cairo-ps.h>
+
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+
+#include <gtk/gtkprintoperation.h>
+#include <gtk/gtkprintsettings.h>
+#include <gtk/gtkprintbackend.h>
+#include <gtk/gtkprinter.h>
+
+#include "gtkprintbackendcups.h"
+#include "gtkprintercups.h"
+
+#include "gtkcupsutils.h"
+
+
+typedef struct _GtkPrintBackendCupsClass GtkPrintBackendCupsClass;
+
+#define GTK_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
+#define GTK_IS_PRINT_BACKEND_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_BACKEND_CUPS))
+#define GTK_PRINT_BACKEND_CUPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCupsClass))
+
+#define _CUPS_MAX_ATTEMPTS 10
+#define _CUPS_MAX_CHUNK_SIZE 8192
+
+#define _CUPS_MAP_ATTR_INT(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = attr->values[0].integer;}
+#define _CUPS_MAP_ATTR_STR(attr, v, a) {if (!g_ascii_strcasecmp (attr->name, (a))) v = g_strdup (attr->values[0].string.text);}
+
+static GType print_backend_cups_type = 0;
+
+typedef void (* GtkPrintCupsResponseCallbackFunc) (GtkPrintBackend *print_backend,
+ GtkCupsResult *result,
+ gpointer user_data);
+
+typedef enum
+{
+ DISPATCH_SETUP,
+ DISPATCH_REQUEST,
+ DISPATCH_SEND,
+ DISPATCH_CHECK,
+ DISPATCH_READ,
+ DISPATCH_ERROR
+} GtkPrintCupsDispatchState;
+
+typedef struct
+{
+ GSource source;
+
+ http_t *http;
+ GtkCupsRequest *request;
+ GPollFD *data_poll;
+ GtkPrintBackendCups *backend;
+
+} GtkPrintCupsDispatchWatch;
+
+struct _GtkPrintBackendCupsClass
+{
+ GObjectClass parent_class;
+};
+
+struct _GtkPrintBackendCups
+{
+ GObject parent_instance;
+
+ GHashTable *printers;
+
+ char *default_printer;
+
+ guint list_printers_poll;
+ guint list_printers_pending : 1;
+ guint got_default_printer : 1;
+};
+
+static GObjectClass *backend_parent_class;
+
+static void gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class);
+static void gtk_print_backend_cups_iface_init (GtkPrintBackendIface *iface);
+static void gtk_print_backend_cups_init (GtkPrintBackendCups *impl);
+static void gtk_print_backend_cups_finalize (GObject *object);
+static GList * cups_get_printer_list (GtkPrintBackend *print_backend);
+static void cups_request_execute (GtkPrintBackendCups *print_backend,
+ GtkCupsRequest *request,
+ GtkPrintCupsResponseCallbackFunc callback,
+ gpointer user_data,
+ GDestroyNotify notify,
+ GError **err);
+static void cups_printer_get_settings_from_options (GtkPrinter *printer,
+ GtkPrinterOptionSet *options,
+ GtkPrintSettings *settings);
+static gboolean cups_printer_mark_conflicts (GtkPrinter *printer,
+ GtkPrinterOptionSet *options);
+static GtkPrinterOptionSet *cups_printer_get_options (GtkPrinter *printer,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup);
+static void cups_printer_prepare_for_print (GtkPrinter *printer,
+ GtkPrintJob *print_job,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup);
+static GList * cups_printer_list_papers (GtkPrinter *printer);
+static void cups_printer_request_details (GtkPrinter *printer);
+static void cups_request_default_printer (GtkPrintBackendCups *print_backend);
+static void cups_request_ppd (GtkPrinter *printer);
+static void cups_printer_get_hard_margins (GtkPrinter *printer,
+ double *top,
+ double *bottom,
+ double *left,
+ double *right);
+static void set_option_from_settings (GtkPrinterOption *option,
+ GtkPrintSettings *setting);
+static void cups_begin_polling_info (GtkPrintBackendCups *print_backend,
+ GtkPrintJob *job,
+ int job_id);
+static gboolean cups_job_info_poll_timeout (gpointer user_data);
+
+static void
+gtk_print_backend_cups_register_type (GTypeModule *module)
+{
+ if (!print_backend_cups_type)
+ {
+ static const GTypeInfo print_backend_cups_info =
+ {
+ sizeof (GtkPrintBackendCupsClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_print_backend_cups_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkPrintBackendCups),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_print_backend_cups_init
+ };
+
+ static const GInterfaceInfo print_backend_info =
+ {
+ (GInterfaceInitFunc) gtk_print_backend_cups_iface_init, /* interface_init */
+ NULL, /* interface_finalize */
+ NULL /* interface_data */
+ };
+
+ print_backend_cups_type = g_type_module_register_type (module,
+ G_TYPE_OBJECT,
+ "GtkPrintBackendCups",
+ &print_backend_cups_info, 0);
+ g_type_module_add_interface (module,
+ print_backend_cups_type,
+ GTK_TYPE_PRINT_BACKEND,
+ &print_backend_info);
+ }
+}
+
+G_MODULE_EXPORT void
+pb_module_init (GTypeModule *module)
+{
+ gtk_print_backend_cups_register_type (module);
+ gtk_printer_cups_register_type (module);
+}
+
+G_MODULE_EXPORT void
+pb_module_exit (void)
+{
+
+}
+
+G_MODULE_EXPORT GtkPrintBackend *
+pb_module_create (void)
+{
+ return gtk_print_backend_cups_new ();
+}
+
+/*
+ * GtkPrintBackendCups
+ */
+GType
+gtk_print_backend_cups_get_type (void)
+{
+ return print_backend_cups_type;
+}
+
+/**
+ * gtk_print_backend_cups_new:
+ *
+ * Creates a new #GtkPrintBackendCups object. #GtkPrintBackendCups
+ * implements the #GtkPrintBackend interface with direct access to
+ * the filesystem using Unix/Linux API calls
+ *
+ * Return value: the new #GtkPrintBackendCups object
+ **/
+GtkPrintBackend *
+gtk_print_backend_cups_new (void)
+{
+ return g_object_new (GTK_TYPE_PRINT_BACKEND_CUPS, NULL);
+}
+
+static void
+gtk_print_backend_cups_class_init (GtkPrintBackendCupsClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ backend_parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->finalize = gtk_print_backend_cups_finalize;
+}
+
+static cairo_status_t
+_cairo_write_to_cups (void *cache_fd_as_pointer,
+ const unsigned char *data,
+ unsigned int length)
+{
+ cairo_status_t result;
+ gint cache_fd;
+ cache_fd = GPOINTER_TO_INT (cache_fd_as_pointer);
+
+ result = CAIRO_STATUS_WRITE_ERROR;
+
+ /* write out the buffer */
+ if (write (cache_fd, data, length) != -1)
+ result = CAIRO_STATUS_SUCCESS;
+
+ return result;
+}
+
+
+static cairo_surface_t *
+cups_printer_create_cairo_surface (GtkPrinter *printer,
+ gdouble width,
+ gdouble height,
+ gint cache_fd)
+{
+ cairo_surface_t *surface;
+
+ /* TODO: check if it is a ps or pdf printer */
+
+ surface = cairo_ps_surface_create_for_stream (_cairo_write_to_cups, GINT_TO_POINTER (cache_fd), width, height);
+
+ /* TODO: DPI from settings object? */
+ cairo_ps_surface_set_dpi (surface, 300, 300);
+
+ return surface;
+}
+
+static GtkPrinter *
+gtk_print_backend_cups_find_printer (GtkPrintBackend *print_backend,
+ const gchar *printer_name)
+{
+ GtkPrintBackendCups *cups_print_backend;
+
+ cups_print_backend = GTK_PRINT_BACKEND_CUPS (print_backend);
+
+ return (GtkPrinter *) g_hash_table_lookup (cups_print_backend->printers,
+ printer_name);
+}
+
+typedef struct {
+ GtkPrintJobCompleteFunc callback;
+ GtkPrintJob *job;
+ gpointer user_data;
+ GDestroyNotify dnotify;
+} CupsPrintStreamData;
+
+static void
+cups_free_print_stream_data (CupsPrintStreamData *data)
+{
+ if (data->dnotify)
+ data->dnotify (data->user_data);
+ g_object_unref (data->job);
+ g_free (data);
+}
+
+static void
+cups_print_cb (GtkPrintBackendCups *print_backend,
+ GtkCupsResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ CupsPrintStreamData *ps = user_data;
+
+ if (gtk_cups_result_is_error (result))
+ error = g_error_new_literal (gtk_print_error_quark (),
+ GTK_PRINT_ERROR_INTERNAL_ERROR,
+ gtk_cups_result_get_error_string (result));
+
+ if (ps->callback)
+ ps->callback (ps->job, ps->user_data, error);
+
+ if (error == NULL)
+ {
+ int job_id = 0;
+ ipp_attribute_t *attr; /* IPP job-id attribute */
+ ipp_t *response = gtk_cups_result_get_response (result);
+
+ if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
+ job_id = attr->values[0].integer;
+
+
+ if (job_id == 0)
+ gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED);
+ else
+ {
+ gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_PENDING);
+ cups_begin_polling_info (print_backend, ps->job, job_id);
+ }
+ }
+ else
+ gtk_print_job_set_status (ps->job, GTK_PRINT_STATUS_FINISHED_ABORTED);
+
+
+ if (error)
+ g_error_free (error);
+
+}
+
+static void
+add_cups_options (const char *key,
+ const char *value,
+ gpointer user_data)
+{
+ GtkCupsRequest *request = user_data;
+
+ if (!g_str_has_prefix (key, "cups-"))
+ return;
+
+ if (strcmp (value, "gtk-ignore-value") == 0)
+ return;
+
+ key = key + strlen("cups-");
+
+ gtk_cups_request_encode_option (request, key, value);
+}
+
+static void
+gtk_print_backend_cups_print_stream (GtkPrintBackend *print_backend,
+ GtkPrintJob *job,
+ gint data_fd,
+ GtkPrintJobCompleteFunc callback,
+ gpointer user_data,
+ GDestroyNotify dnotify)
+{
+ GError *error;
+ GtkPrinterCups *cups_printer;
+ CupsPrintStreamData *ps;
+ GtkCupsRequest *request;
+ GtkPrintSettings *settings;
+ const gchar *title;
+
+ cups_printer = GTK_PRINTER_CUPS (gtk_print_job_get_printer (job));
+ settings = gtk_print_job_get_settings (job);
+
+ error = NULL;
+
+ request = gtk_cups_request_new (NULL,
+ GTK_CUPS_POST,
+ IPP_PRINT_JOB,
+ data_fd,
+ NULL,
+ cups_printer->device_uri);
+
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, cups_printer->printer_uri);
+
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+ NULL, cupsUser());
+
+ title = gtk_print_job_get_title (job);
+ if (title)
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+ title);
+
+ gtk_print_settings_foreach (settings, add_cups_options, request);
+
+ ps = g_new0 (CupsPrintStreamData, 1);
+ ps->callback = callback;
+ ps->user_data = user_data;
+ ps->dnotify = dnotify;
+ ps->job = g_object_ref (job);
+
+ cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
+ request,
+ (GtkPrintCupsResponseCallbackFunc) cups_print_cb,
+ ps,
+ (GDestroyNotify)cups_free_print_stream_data,
+ &error);
+}
+
+
+static void
+gtk_print_backend_cups_iface_init (GtkPrintBackendIface *iface)
+{
+ iface->get_printer_list = cups_get_printer_list;
+ iface->find_printer = gtk_print_backend_cups_find_printer;
+ iface->print_stream = gtk_print_backend_cups_print_stream;
+ iface->printer_request_details = cups_printer_request_details;
+ iface->printer_create_cairo_surface = cups_printer_create_cairo_surface;
+ iface->printer_get_options = cups_printer_get_options;
+ iface->printer_mark_conflicts = cups_printer_mark_conflicts;
+ iface->printer_get_settings_from_options = cups_printer_get_settings_from_options;
+ iface->printer_prepare_for_print = cups_printer_prepare_for_print;
+ iface->printer_list_papers = cups_printer_list_papers;
+ iface->printer_get_hard_margins = cups_printer_get_hard_margins;
+}
+
+static void
+gtk_print_backend_cups_init (GtkPrintBackendCups *backend_cups)
+{
+ backend_cups->list_printers_poll = 0;
+ backend_cups->list_printers_pending = FALSE;
+ backend_cups->printers = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ cups_request_default_printer (backend_cups);
+}
+
+static void
+gtk_print_backend_cups_finalize (GObject *object)
+{
+ GtkPrintBackendCups *backend_cups;
+
+ backend_cups = GTK_PRINT_BACKEND_CUPS (object);
+
+ if (backend_cups->list_printers_poll > 0)
+ g_source_remove (backend_cups->list_printers_poll);
+
+ if (backend_cups->printers)
+ g_hash_table_unref (backend_cups->printers);
+
+ g_free (backend_cups->default_printer);
+ backend_cups->default_printer = NULL;
+
+ backend_parent_class->finalize (object);
+}
+
+static gboolean
+cups_dispatch_watch_check (GSource *source)
+{
+ GtkPrintCupsDispatchWatch *dispatch;
+ GtkCupsPollState poll_state;
+ gboolean result;
+
+ dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+ poll_state = gtk_cups_request_get_poll_state (dispatch->request);
+
+ if (dispatch->data_poll == NULL &&
+ dispatch->request->http != NULL)
+ {
+ dispatch->data_poll = g_new0 (GPollFD, 1);
+ dispatch->data_poll->fd = dispatch->request->http->fd;
+
+ g_source_add_poll (source, dispatch->data_poll);
+ }
+
+ if (dispatch->data_poll != NULL && dispatch->request->http != NULL)
+ {
+ if (dispatch->data_poll->fd != dispatch->request->http->fd)
+ dispatch->data_poll->fd = dispatch->request->http->fd;
+
+ if (poll_state == GTK_CUPS_HTTP_READ)
+ dispatch->data_poll->events = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI;
+ else if (poll_state == GTK_CUPS_HTTP_WRITE)
+ dispatch->data_poll->events = G_IO_OUT | G_IO_ERR;
+ else
+ dispatch->data_poll->events = 0;
+ }
+
+ if (poll_state != GTK_CUPS_HTTP_IDLE)
+ if (!(dispatch->data_poll->revents & dispatch->data_poll->events))
+ return FALSE;
+
+ result = gtk_cups_request_read_write (dispatch->request);
+ if (result && dispatch->data_poll != NULL)
+ {
+ g_source_remove_poll (source, dispatch->data_poll);
+ g_free (dispatch->data_poll);
+ dispatch->data_poll = NULL;
+ }
+
+ return result;
+}
+
+static gboolean
+cups_dispatch_watch_prepare (GSource *source,
+ gint *timeout_)
+{
+ GtkPrintCupsDispatchWatch *dispatch;
+
+ dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+
+ *timeout_ = -1;
+
+ return gtk_cups_request_read_write (dispatch->request);
+}
+
+static gboolean
+cups_dispatch_watch_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ GtkPrintCupsDispatchWatch *dispatch;
+ GtkPrintCupsResponseCallbackFunc ep_callback;
+ GtkCupsResult *result;
+
+ g_assert (callback != NULL);
+
+ ep_callback = (GtkPrintCupsResponseCallbackFunc) callback;
+
+ dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+ result = gtk_cups_request_get_result (dispatch->request);
+
+ if (gtk_cups_result_is_error (result))
+ g_warning (gtk_cups_result_get_error_string (result));
+
+ ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data);
+
+ g_source_unref (source);
+ return FALSE;
+}
+
+static void
+cups_dispatch_watch_finalize (GSource *source)
+{
+ GtkPrintCupsDispatchWatch *dispatch;
+
+ dispatch = (GtkPrintCupsDispatchWatch *) source;
+
+ gtk_cups_request_free (dispatch->request);
+
+ if (dispatch->backend)
+ {
+ g_object_unref (dispatch->backend);
+ dispatch->backend = NULL;
+ }
+
+ if (dispatch->data_poll != NULL)
+ g_free (dispatch->data_poll);
+}
+
+static GSourceFuncs _cups_dispatch_watch_funcs = {
+ cups_dispatch_watch_prepare,
+ cups_dispatch_watch_check,
+ cups_dispatch_watch_dispatch,
+ cups_dispatch_watch_finalize
+};
+
+
+static void
+cups_request_execute (GtkPrintBackendCups *print_backend,
+ GtkCupsRequest *request,
+ GtkPrintCupsResponseCallbackFunc callback,
+ gpointer user_data,
+ GDestroyNotify notify,
+ GError **err)
+{
+ GtkPrintCupsDispatchWatch *dispatch;
+
+ dispatch = (GtkPrintCupsDispatchWatch *) g_source_new (&_cups_dispatch_watch_funcs,
+ sizeof (GtkPrintCupsDispatchWatch));
+
+ dispatch->request = request;
+ dispatch->backend = g_object_ref (print_backend);
+ dispatch->data_poll = NULL;
+
+ g_source_set_callback ((GSource *) dispatch, (GSourceFunc) callback, user_data, notify);
+
+ g_source_attach ((GSource *) dispatch, NULL);
+}
+
+static void
+cups_request_printer_info_cb (GtkPrintBackendCups *print_backend,
+ GtkCupsResult *result,
+ gpointer user_data)
+{
+ ipp_attribute_t *attr;
+ ipp_t *response;
+ gchar *printer_name;
+ GtkPrinterCups *cups_printer;
+ GtkPrinter *printer;
+ gchar *printer_uri;
+ gchar *member_printer_uri;
+ gchar *loc;
+ gchar *desc;
+ gchar *state_msg;
+ int job_count;
+
+ char uri[HTTP_MAX_URI], /* Printer URI */
+ method[HTTP_MAX_URI], /* Method/scheme name */
+ username[HTTP_MAX_URI], /* Username:password */
+ hostname[HTTP_MAX_URI], /* Hostname */
+ resource[HTTP_MAX_URI]; /* Resource name */
+ int port; /* Port number */
+ gboolean status_changed;
+
+ g_assert (GTK_IS_PRINT_BACKEND_CUPS (print_backend));
+
+ printer_uri = NULL;
+ member_printer_uri = NULL;
+
+ printer_name = (gchar *)user_data;
+ cups_printer = (GtkPrinterCups *) g_hash_table_lookup (print_backend->printers, printer_name);
+
+ if (!cups_printer)
+ return;
+
+ printer = GTK_PRINTER (cups_printer);
+
+ if (gtk_cups_result_is_error (result))
+ {
+ if (gtk_printer_is_new (printer))
+ {
+ g_hash_table_remove (print_backend->printers,
+ printer_name);
+ return;
+ }
+ else
+ return; /* TODO: mark as inactive printer */
+ }
+
+ response = gtk_cups_result_get_response (result);
+
+ /* TODO: determine printer type and use correct icon */
+ gtk_printer_set_icon_name (printer, "printer");
+
+ cups_printer->device_uri = g_strdup_printf ("/printers/%s", printer_name);
+
+ state_msg = "";
+ loc = "";
+ desc = "";
+ job_count = 0;
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ if (!attr->name)
+ continue;
+
+ _CUPS_MAP_ATTR_STR (attr, loc, "printer-location");
+ _CUPS_MAP_ATTR_STR (attr, desc, "printer-info");
+ _CUPS_MAP_ATTR_STR (attr, state_msg, "printer-state-message");
+ _CUPS_MAP_ATTR_STR (attr, printer_uri, "printer-uri-supported");
+ _CUPS_MAP_ATTR_STR (attr, member_printer_uri, "member-uris");
+ _CUPS_MAP_ATTR_INT (attr, cups_printer->state, "printer-state");
+ _CUPS_MAP_ATTR_INT (attr, job_count, "queued-job-count");
+ }
+
+ /* if we got a member_printer_uri then this printer is part of a class
+ so use member_printer_uri, else user printer_uri */
+
+ if (cups_printer->printer_uri)
+ g_free (cups_printer->printer_uri);
+
+ if (member_printer_uri)
+ {
+ g_free (printer_uri);
+ cups_printer->printer_uri = member_printer_uri;
+ }
+ else
+ cups_printer->printer_uri = printer_uri;
+
+ status_changed = gtk_printer_set_job_count (printer, job_count);
+
+ status_changed |= gtk_printer_set_location (printer, loc);
+ status_changed |= gtk_printer_set_description (printer, desc);
+ status_changed |= gtk_printer_set_state_message (printer, state_msg);
+
+#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR >= 2) || CUPS_VERSION_MAJOR > 1
+ httpSeparateURI (HTTP_URI_CODING_ALL, cups_printer->printer_uri,
+ method, sizeof (method),
+ username, sizeof (username),
+ hostname, sizeof (hostname),
+ &port,
+ resource, sizeof (resource));
+
+#else
+ httpSeparate (cups_printer->printer_uri,
+ method,
+ username,
+ hostname,
+ &port,
+ resource);
+#endif
+
+ gethostname(uri, sizeof(uri));
+
+ if (strcasecmp(uri, hostname) == 0)
+ strcpy(hostname, "localhost");
+
+ if (cups_printer->hostname)
+ g_free (cups_printer->hostname);
+
+ cups_printer->hostname = g_strdup (hostname);
+ cups_printer->port = port;
+
+ if (status_changed)
+ g_signal_emit_by_name (GTK_PRINT_BACKEND (print_backend), "printer-status-changed", printer);
+}
+
+static void
+cups_request_printer_info (GtkPrintBackendCups *print_backend,
+ const gchar *printer_name)
+{
+ GError *error;
+ GtkCupsRequest *request;
+ gchar *printer_uri;
+
+ error = NULL;
+
+ request = gtk_cups_request_new (NULL,
+ GTK_CUPS_POST,
+ IPP_GET_PRINTER_ATTRIBUTES,
+ 0,
+ NULL,
+ NULL);
+
+ printer_uri = g_strdup_printf ("ipp://localhost/printers/%s",
+ printer_name);
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+
+ g_free (printer_uri);
+
+ cups_request_execute (print_backend,
+ request,
+ (GtkPrintCupsResponseCallbackFunc) cups_request_printer_info_cb,
+ g_strdup (printer_name),
+ (GDestroyNotify) g_free,
+ &error);
+
+}
+
+
+typedef struct {
+ GtkPrintBackendCups *print_backend;
+ GtkPrintJob *job;
+ int job_id;
+ int counter;
+} CupsJobPollData;
+
+static void
+job_object_died (gpointer user_data,
+ GObject *where_the_object_was)
+{
+ CupsJobPollData *data = user_data;
+ data->job = NULL;
+}
+
+static void
+cups_job_poll_data_free (CupsJobPollData *data)
+{
+ if (data->job)
+ g_object_weak_unref (G_OBJECT (data->job), job_object_died, data);
+
+ g_free (data);
+}
+
+static void
+cups_request_job_info_cb (GtkPrintBackendCups *print_backend,
+ GtkCupsResult *result,
+ gpointer user_data)
+{
+ CupsJobPollData *data = user_data;
+ ipp_attribute_t *attr;
+ ipp_t *response;
+ int state;
+ gboolean done;
+
+ if (data->job == NULL)
+ {
+ cups_job_poll_data_free (data);
+ return;
+ }
+
+ data->counter++;
+
+ response = gtk_cups_result_get_response (result);
+
+ state = 0;
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ if (!attr->name)
+ continue;
+
+ _CUPS_MAP_ATTR_INT (attr, state, "job-state");
+ }
+
+ done = FALSE;
+ switch (state)
+ {
+ case IPP_JOB_PENDING:
+ case IPP_JOB_HELD:
+ case IPP_JOB_STOPPED:
+ gtk_print_job_set_status (data->job,
+ GTK_PRINT_STATUS_PENDING);
+ break;
+ case IPP_JOB_PROCESSING:
+ gtk_print_job_set_status (data->job,
+ GTK_PRINT_STATUS_PRINTING);
+ break;
+ default:
+ case IPP_JOB_CANCELLED:
+ case IPP_JOB_ABORTED:
+ gtk_print_job_set_status (data->job,
+ GTK_PRINT_STATUS_FINISHED_ABORTED);
+ done = TRUE;
+ break;
+ case 0:
+ case IPP_JOB_COMPLETED:
+ gtk_print_job_set_status (data->job,
+ GTK_PRINT_STATUS_FINISHED);
+ done = TRUE;
+ break;
+ }
+
+ if (!done && data->job != NULL)
+ {
+ guint32 timeout;
+
+ if (data->counter < 5)
+ timeout = 100;
+ else if (data->counter < 10)
+ timeout = 500;
+ else
+ timeout = 1000;
+
+ g_timeout_add (timeout, cups_job_info_poll_timeout, data);
+ }
+ else
+ cups_job_poll_data_free (data);
+}
+
+static void
+cups_request_job_info (CupsJobPollData *data)
+{
+ GError *error;
+ GtkCupsRequest *request;
+ gchar *printer_uri;
+
+
+ error = NULL;
+ request = gtk_cups_request_new (NULL,
+ GTK_CUPS_POST,
+ IPP_GET_JOB_ATTRIBUTES,
+ 0,
+ NULL,
+ NULL);
+
+ printer_uri = g_strdup_printf ("ipp://localhost/jobs/%d", data->job_id);
+ gtk_cups_request_ipp_add_string (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "job-uri", NULL, printer_uri);
+ g_free (printer_uri);
+
+ cups_request_execute (data->print_backend,
+ request,
+ (GtkPrintCupsResponseCallbackFunc) cups_request_job_info_cb,
+ data,
+ NULL,
+ &error);
+}
+
+static gboolean
+cups_job_info_poll_timeout (gpointer user_data)
+{
+ CupsJobPollData *data = user_data;
+
+ if (data->job == NULL)
+ cups_job_poll_data_free (data);
+ else
+ cups_request_job_info (data);
+
+ return FALSE;
+}
+
+static void
+cups_begin_polling_info (GtkPrintBackendCups *print_backend,
+ GtkPrintJob *job,
+ int job_id)
+{
+ CupsJobPollData *data;
+
+ data = g_new0 (CupsJobPollData, 1);
+
+ data->print_backend = print_backend;
+ data->job = job;
+ data->job_id = job_id;
+ data->counter = 0;
+
+ g_object_weak_ref (G_OBJECT (job), job_object_died, data);
+
+ cups_request_job_info (data);
+}
+
+static gint
+printer_cmp (GtkPrinter *a, GtkPrinter *b)
+{
+ const char *name_a, *name_b;
+ g_assert (GTK_IS_PRINTER (a) && GTK_IS_PRINTER (b));
+
+ name_a = gtk_printer_get_name (a);
+ name_b = gtk_printer_get_name (b);
+ if (name_a == NULL && name_b == NULL)
+ return 0;
+ else if (name_a == NULL)
+ return G_MAXINT;
+ else if (name_b == NULL)
+ return G_MININT;
+ else
+ return g_ascii_strcasecmp (name_a, name_b);
+}
+
+static void
+printer_hash_to_sorted_active_list (const gchar *key,
+ gpointer value,
+ GList **out_list)
+{
+ GtkPrinter *printer;
+
+ printer = GTK_PRINTER (value);
+
+ if (gtk_printer_get_name (printer) == NULL)
+ return;
+
+ if (!gtk_printer_is_active (printer))
+ return;
+
+ *out_list = g_list_insert_sorted (*out_list, value, (GCompareFunc) printer_cmp);
+}
+
+static void
+printer_hash_to_sorted_active_name_list (const gchar *key,
+ gpointer value,
+ GList **out_list)
+{
+ GtkPrinter *printer;
+
+ printer = GTK_PRINTER (value);
+
+
+ if (gtk_printer_get_name (printer) == NULL)
+ return;
+
+ if (!gtk_printer_is_active (printer))
+ return;
+
+ if (gtk_printer_is_active (printer))
+ *out_list = g_list_insert_sorted (*out_list,
+ (char *)gtk_printer_get_name (printer),
+ g_str_equal);
+}
+
+static void
+mark_printer_inactive (const gchar *printer_name,
+ GtkPrintBackendCups *cups_backend)
+{
+ GtkPrinter *printer;
+ GHashTable *printer_hash;
+
+ printer_hash = cups_backend->printers;
+
+ printer = (GtkPrinter *) g_hash_table_lookup (printer_hash,
+ printer_name);
+
+ if (printer == NULL)
+ return;
+
+ gtk_printer_set_is_active (printer, FALSE);
+
+ g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend), "printer-removed", printer);
+}
+
+static void
+cups_request_printer_list_cb (GtkPrintBackendCups *cups_backend,
+ GtkCupsResult *result,
+ gpointer user_data)
+{
+ ipp_attribute_t *attr;
+ ipp_t *response;
+ gboolean list_has_changed;
+ GList *removed_printer_checklist;
+
+ list_has_changed = FALSE;
+
+ g_assert (GTK_IS_PRINT_BACKEND_CUPS (cups_backend));
+
+ cups_backend->list_printers_pending = FALSE;
+
+ if (gtk_cups_result_is_error (result))
+ {
+ g_warning ("Error getting printer list: %s", gtk_cups_result_get_error_string (result));
+ return;
+ }
+
+ /* gether the names of the printers in the current queue
+ so we may check to see if they were removed */
+ removed_printer_checklist = NULL;
+ if (cups_backend->printers != NULL)
+ g_hash_table_foreach (cups_backend->printers,
+ (GHFunc) printer_hash_to_sorted_active_name_list,
+ &removed_printer_checklist);
+
+ response = gtk_cups_result_get_response (result);
+
+ attr = ippFindAttribute (response, "printer-name", IPP_TAG_NAME);
+
+ while (attr)
+ {
+ GtkPrinterCups *cups_printer;
+ GtkPrinter *printer;
+ const gchar *printer_name;
+ GList *node;
+
+ printer_name = attr->values[0].string.text;
+ /* remove name from checklist if it was found */
+ node = g_list_find_custom (removed_printer_checklist, printer_name, (GCompareFunc) g_ascii_strcasecmp);
+ removed_printer_checklist = g_list_delete_link (removed_printer_checklist, node);
+
+ cups_printer = (GtkPrinterCups *) g_hash_table_lookup (cups_backend->printers,
+ printer_name);
+ printer = cups_printer ? GTK_PRINTER (cups_printer) : NULL;
+
+ if (!cups_printer)
+ {
+ list_has_changed = TRUE;
+ cups_printer = gtk_printer_cups_new (attr->values[0].string.text,
+ GTK_PRINT_BACKEND (cups_backend));
+ printer = GTK_PRINTER (cups_printer);
+
+ if (cups_backend->default_printer != NULL &&
+ strcmp (cups_backend->default_printer, gtk_printer_get_name (printer)) == 0)
+ gtk_printer_set_is_default (printer, TRUE);
+
+ g_hash_table_insert (cups_backend->printers,
+ g_strdup (gtk_printer_get_name (printer)),
+ cups_printer);
+ }
+
+ if (!gtk_printer_is_active (printer))
+ {
+ gtk_printer_set_is_active (printer, TRUE);
+ gtk_printer_set_is_new (printer, TRUE);
+ list_has_changed = TRUE;
+ }
+
+ if (gtk_printer_is_new (printer))
+ {
+ g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend),
+ "printer-added",
+ printer);
+
+ gtk_printer_set_is_new (printer, FALSE);
+ }
+
+ cups_request_printer_info (cups_backend, gtk_printer_get_name (printer));
+
+ attr = ippFindNextAttribute (response,
+ "printer-name",
+ IPP_TAG_NAME);
+ }
+
+ /* look at the removed printers checklist and mark any printer
+ as inactive if it is in the list, emitting a printer_removed signal */
+
+ if (removed_printer_checklist != NULL)
+ {
+ g_list_foreach (removed_printer_checklist, (GFunc) mark_printer_inactive, cups_backend);
+ g_list_free (removed_printer_checklist);
+ list_has_changed = TRUE;
+ }
+
+ if (list_has_changed)
+ g_signal_emit_by_name (GTK_PRINT_BACKEND (cups_backend), "printer-list-changed");
+
+}
+
+static gboolean
+cups_request_printer_list (GtkPrintBackendCups *cups_backend)
+{
+ GError *error;
+ GtkCupsRequest *request;
+
+ if (cups_backend->list_printers_pending ||
+ !cups_backend->got_default_printer)
+ return TRUE;
+
+ cups_backend->list_printers_pending = TRUE;
+
+ error = NULL;
+
+ request = gtk_cups_request_new (NULL,
+ GTK_CUPS_POST,
+ CUPS_GET_PRINTERS,
+ 0,
+ NULL,
+ NULL);
+
+ cups_request_execute (cups_backend,
+ request,
+ (GtkPrintCupsResponseCallbackFunc) cups_request_printer_list_cb,
+ request,
+ NULL,
+ &error);
+
+
+ return TRUE;
+}
+
+static GList *
+cups_get_printer_list (GtkPrintBackend *print_backend)
+{
+ GtkPrintBackendCups *cups_backend;
+ GList *result;
+
+ cups_backend = GTK_PRINT_BACKEND_CUPS (print_backend);
+
+ result = NULL;
+ if (cups_backend->printers != NULL)
+ g_hash_table_foreach (cups_backend->printers,
+ (GHFunc) printer_hash_to_sorted_active_list,
+ &result);
+
+ if (cups_backend->list_printers_poll == 0)
+ {
+ cups_request_printer_list (cups_backend);
+ cups_backend->list_printers_poll = g_timeout_add (3000,
+ (GSourceFunc) cups_request_printer_list,
+ print_backend);
+ }
+
+ return result;
+}
+
+typedef struct {
+ GtkPrinterCups *printer;
+ gint ppd_fd;
+ gchar *ppd_filename;
+} GetPPDData;
+
+static void
+get_ppd_data_free (GetPPDData *data)
+{
+ close (data->ppd_fd);
+ unlink (data->ppd_filename);
+ g_free (data->ppd_filename);
+ g_object_unref (data->printer);
+ g_free (data);
+}
+
+static void
+cups_request_ppd_cb (GtkPrintBackendCups *print_backend,
+ GtkCupsResult *result,
+ GetPPDData *data)
+{
+ ipp_t *response;
+ GtkPrinter *printer;
+
+ printer = GTK_PRINTER (data->printer);
+ GTK_PRINTER_CUPS (printer)->reading_ppd = FALSE;
+
+ if (gtk_cups_result_is_error (result))
+ {
+ g_signal_emit_by_name (printer, "details-acquired", printer, FALSE);
+ return;
+ }
+
+ response = gtk_cups_result_get_response (result);
+
+ data->printer->ppd_file = ppdOpenFile (data->ppd_filename);
+ gtk_printer_set_has_details (printer, TRUE);
+ g_signal_emit_by_name (printer, "details-acquired", printer, TRUE);
+}
+
+static void
+cups_request_ppd (GtkPrinter *printer)
+{
+ GError *error;
+ GtkPrintBackend *print_backend;
+ GtkPrinterCups *cups_printer;
+ GtkCupsRequest *request;
+ gchar *resource;
+ http_t *http;
+ GetPPDData *data;
+
+ cups_printer = GTK_PRINTER_CUPS (printer);
+
+ error = NULL;
+
+ http = httpConnectEncrypt(cups_printer->hostname,
+ cups_printer->port,
+ cupsEncryption());
+
+ data = g_new0 (GetPPDData, 1);
+
+ data->ppd_fd = g_file_open_tmp ("gtkprint_ppd_XXXXXX",
+ &data->ppd_filename,
+ &error);
+
+ if (error != NULL)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ httpClose (http);
+ g_free (data);
+
+ g_signal_emit_by_name (printer, "details-acquired", printer, FALSE);
+ return;
+ }
+
+ fchmod (data->ppd_fd, S_IRUSR | S_IWUSR);
+
+ data->printer = g_object_ref (printer);
+
+ resource = g_strdup_printf ("/printers/%s.ppd", gtk_printer_get_name (printer));
+ request = gtk_cups_request_new (http,
+ GTK_CUPS_GET,
+ 0,
+ data->ppd_fd,
+ cups_printer->hostname,
+ resource);
+
+ g_free (resource);
+
+ cups_printer->reading_ppd = TRUE;
+
+ print_backend = gtk_printer_get_backend (printer);
+
+ cups_request_execute (GTK_PRINT_BACKEND_CUPS (print_backend),
+ request,
+ (GtkPrintCupsResponseCallbackFunc) cups_request_ppd_cb,
+ data,
+ (GDestroyNotify)get_ppd_data_free,
+ &error);
+}
+
+
+static void
+cups_request_default_printer_cb (GtkPrintBackendCups *print_backend,
+ GtkCupsResult *result,
+ gpointer user_data)
+{
+ ipp_t *response;
+ ipp_attribute_t *attr;
+
+ response = gtk_cups_result_get_response (result);
+
+ if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL)
+ print_backend->default_printer = g_strdup (attr->values[0].string.text);
+
+ print_backend->got_default_printer = TRUE;
+
+ /* Make sure to kick off get_printers if we are polling it, as we could
+ have blocked this reading the default printer */
+ if (print_backend->list_printers_poll != 0)
+ cups_request_printer_list (print_backend);
+}
+
+static void
+cups_request_default_printer (GtkPrintBackendCups *print_backend)
+{
+ GError *error;
+ GtkCupsRequest *request;
+ const char *str;
+
+ error = NULL;
+
+ if ((str = getenv("LPDEST")) != NULL)
+ {
+ print_backend->default_printer = g_strdup (str);
+ print_backend->got_default_printer = TRUE;
+ return;
+ }
+ else if ((str = getenv("PRINTER")) != NULL &&
+ strcmp(str, "lp") != 0)
+ {
+ print_backend->default_printer = g_strdup (str);
+ print_backend->got_default_printer = TRUE;
+ return;
+ }
+
+ request = gtk_cups_request_new (NULL,
+ GTK_CUPS_POST,
+ CUPS_GET_DEFAULT,
+ 0,
+ NULL,
+ NULL);
+
+ cups_request_execute (print_backend,
+ request,
+ (GtkPrintCupsResponseCallbackFunc) cups_request_default_printer_cb,
+ g_object_ref (print_backend),
+ g_object_unref,
+ &error);
+}
+
+
+static void
+cups_printer_request_details (GtkPrinter *printer)
+{
+ GtkPrinterCups *cups_printer;
+
+ cups_printer = GTK_PRINTER_CUPS (printer);
+ if (!cups_printer->reading_ppd &&
+ gtk_printer_cups_get_ppd (cups_printer) == NULL)
+ cups_request_ppd (printer);
+}
+
+static char *
+ppd_text_to_utf8 (ppd_file_t *ppd_file, const char *text)
+{
+ const char *encoding = NULL;
+ char *res;
+
+ if (g_ascii_strcasecmp (ppd_file->lang_encoding, "UTF-8") == 0)
+ {
+ return g_strdup (text);
+ }
+ else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin1") == 0)
+ {
+ encoding = "ISO-8859-1";
+ }
+ else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin2") == 0)
+ {
+ encoding = "ISO-8859-2";
+ }
+ else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "ISOLatin5") == 0)
+ {
+ encoding = "ISO-8859-5";
+ }
+ else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "JIS83-RKSJ") == 0)
+ {
+ encoding = "SHIFT-JIS";
+ }
+ else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "MacStandard") == 0)
+ {
+ encoding = "MACINTOSH";
+ }
+ else if (g_ascii_strcasecmp (ppd_file->lang_encoding, "WindowsANSI") == 0)
+ {
+ encoding = "WINDOWS-1252";
+ }
+ else
+ {
+ /* Fallback, try iso-8859-1... */
+ encoding = "ISO-8859-1";
+ }
+
+ res = g_convert (text, -1, "UTF-8", encoding, NULL, NULL, NULL);
+
+ if (res == NULL)
+ {
+ g_warning ("unable to convert PPD text");
+ res = g_strdup ("???");
+ }
+
+ return res;
+}
+
+/* TODO: Add more translations for common settings here */
+
+static const struct {
+ const char *keyword;
+ const char *translation;
+} cups_option_translations[] = {
+ { "Duplex", N_("Two Sided") },
+};
+
+
+static const struct {
+ const char *keyword;
+ const char *choice;
+ const char *translation;
+} cups_choice_translations[] = {
+ { "Duplex", "None", N_("One Sided") },
+ { "InputSlot", "Auto", N_("Auto Select") },
+ { "InputSlot", "AutoSelect", N_("Auto Select") },
+ { "InputSlot", "Default", N_("Printer Default") },
+ { "InputSlot", "None", N_("Printer Default") },
+ { "InputSlot", "PrinterDefault", N_("Printer Default") },
+ { "InputSlot", "Unspecified", N_("Auto Select") },
+};
+
+static const struct {
+ const char *ppd_keyword;
+ const char *name;
+} option_names[] = {
+ {"Duplex", "gtk-duplex" },
+ {"MediaType", "gtk-paper-type"},
+ {"InputSlot", "gtk-paper-source"},
+ {"OutputBin", "gtk-output-tray"},
+};
+
+/* keep sorted when changing */
+static const char *color_option_whitelist[] = {
+ "BRColorEnhancement",
+ "BRColorMatching",
+ "BRColorMatching",
+ "BRColorMode",
+ "BRGammaValue",
+ "BRImprovedGray",
+ "BlackSubstitution",
+ "ColorModel",
+ "HPCMYKInks",
+ "HPCSGraphics",
+ "HPCSImages",
+ "HPCSText",
+ "HPColorSmart",
+ "RPSBlackMode",
+ "RPSBlackOverPrint",
+ "Rcmyksimulation",
+};
+
+/* keep sorted when changing */
+static const char *color_group_whitelist[] = {
+ "ColorPage",
+ "FPColorWise1",
+ "FPColorWise2",
+ "FPColorWise3",
+ "FPColorWise4",
+ "FPColorWise5",
+ "HPColorOptionsPanel",
+};
+
+/* keep sorted when changing */
+static const char *image_quality_option_whitelist[] = {
+ "BRDocument",
+ "BRHalfTonePattern",
+ "BRNormalPrt",
+ "BRPrintQuality",
+ "BitsPerPixel",
+ "Darkness",
+ "Dithering",
+ "EconoMode",
+ "Economode",
+ "HPEconoMode",
+ "HPEdgeControl",
+ "HPGraphicsHalftone",
+ "HPHalftone",
+ "HPLJDensity",
+ "HPPhotoHalftone",
+ "OutputMode",
+ "REt",
+ "RPSBitsPerPixel",
+ "RPSDitherType",
+ "Resolution",
+ "ScreenLock",
+ "Smoothing",
+ "TonerSaveMode",
+ "UCRGCRForImage",
+};
+
+/* keep sorted when changing */
+static const char *image_quality_group_whitelist[] = {
+ "FPImageQuality1",
+ "FPImageQuality2",
+ "FPImageQuality3",
+ "ImageQualityPage",
+};
+
+/* keep sorted when changing */
+static const char * finishing_option_whitelist[] = {
+ "BindColor",
+ "BindEdge",
+ "BindType",
+ "BindWhen",
+ "Booklet",
+ "FoldType",
+ "FoldWhen",
+ "HPStaplerOptions",
+ "Jog",
+ "Slipsheet",
+ "Sorter",
+ "StapleLocation",
+ "StapleOrientation",
+ "StapleWhen",
+ "StapleX",
+ "StapleY",
+};
+
+/* keep sorted when changing */
+static const char *finishing_group_whitelist[] = {
+ "FPFinishing1",
+ "FPFinishing2",
+ "FPFinishing3",
+ "FPFinishing4",
+ "FinishingPage",
+ "HPFinishingPanel",
+};
+
+/* keep sorted when changing */
+static const char *cups_option_blacklist[] = {
+ "Collate",
+ "Copies",
+ "OutputOrder",
+ "PageRegion",
+ "PageSize",
+};
+
+static char *
+get_option_text (ppd_file_t *ppd_file, ppd_option_t *option)
+{
+ int i;
+ char *utf8;
+
+ for (i = 0; i < G_N_ELEMENTS (cups_option_translations); i++)
+ {
+ if (strcmp (cups_option_translations[i].keyword, option->keyword) == 0)
+ return g_strdup (_(cups_option_translations[i].translation));
+ }
+
+ utf8 = ppd_text_to_utf8 (ppd_file, option->text);
+
+ /* Some ppd files have spaces in the text before the colon */
+ g_strchomp (utf8);
+
+ return utf8;
+}
+
+static char *
+get_choice_text (ppd_file_t *ppd_file, ppd_choice_t *choice)
+{
+ int i;
+ ppd_option_t *option = choice->option;
+ const char *keyword = option->keyword;
+
+ for (i = 0; i < G_N_ELEMENTS (cups_choice_translations); i++)
+ {
+ if (strcmp (cups_choice_translations[i].keyword, keyword) == 0 &&
+ strcmp (cups_choice_translations[i].choice, choice->choice) == 0)
+ return g_strdup (_(cups_choice_translations[i].translation));
+ }
+ return ppd_text_to_utf8 (ppd_file, choice->text);
+}
+
+static gboolean
+group_has_option (ppd_group_t *group, ppd_option_t *option)
+{
+ int i;
+
+ if (group == NULL)
+ return FALSE;
+
+ if (group->num_options > 0 &&
+ option >= group->options && option < group->options + group->num_options)
+ return TRUE;
+
+ for (i = 0; i < group->num_subgroups; i++)
+ {
+ if (group_has_option (&group->subgroups[i],option))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+set_option_off (GtkPrinterOption *option)
+{
+ /* Any of these will do, _set only applies the value
+ * if its allowed of the option */
+ gtk_printer_option_set (option, "False");
+ gtk_printer_option_set (option, "Off");
+ gtk_printer_option_set (option, "None");
+}
+
+static gboolean
+value_is_off (const char *value)
+{
+ return (strcasecmp (value, "None") == 0 ||
+ strcasecmp (value, "Off") == 0 ||
+ strcasecmp (value, "False") == 0);
+}
+
+static int
+available_choices (ppd_file_t *ppd,
+ ppd_option_t *option,
+ ppd_choice_t ***available,
+ gboolean keep_if_only_one_option)
+{
+ ppd_option_t *other_option;
+ int i, j;
+ char *conflicts;
+ ppd_const_t *constraint;
+ const char *choice, *other_choice;
+ ppd_option_t *option1, *option2;
+ ppd_group_t *installed_options;
+ int num_conflicts;
+ gboolean all_default;
+ int add_auto;
+
+ if (available)
+ *available = NULL;
+
+ conflicts = g_new0 (char, option->num_choices);
+
+ installed_options = NULL;
+ for (i = 0; i < ppd->num_groups; i++)
+ {
+ if (strcmp (ppd->groups[i].name, "InstallableOptions") == 0)
+ {
+ installed_options = &ppd->groups[i];
+ break;
+ }
+ }
+
+ for (i = ppd->num_consts, constraint = ppd->consts; i > 0; i--, constraint++)
+ {
+ option1 = ppdFindOption (ppd, constraint->option1);
+ if (option1 == NULL)
+ continue;
+
+ option2 = ppdFindOption (ppd, constraint->option2);
+ if (option2 == NULL)
+ continue;
+
+ if (option == option1)
+ {
+ choice = constraint->choice1;
+ other_option = option2;
+ other_choice = constraint->choice2;
+ }
+ else if (option == option2)
+ {
+ choice = constraint->choice2;
+ other_option = option1;
+ other_choice = constraint->choice1;
+ }
+ else
+ continue;
+
+ /* We only care of conflicts with installed_options and
+ PageSize */
+ if (!group_has_option (installed_options, other_option) &&
+ (strcmp (other_option->keyword, "PageSize") != 0))
+ continue;
+
+ if (*other_choice == 0)
+ {
+ /* Conflict only if the installed option is not off */
+ if (value_is_off (other_option->defchoice))
+ continue;
+ }
+ /* Conflict if the installed option has the specified default */
+ else if (strcasecmp (other_choice, other_option->defchoice) != 0)
+ continue;
+
+ if (*choice == 0)
+ {
+ /* Conflict with all non-off choices */
+ for (j = 0; j < option->num_choices; j++)
+ {
+ if (!value_is_off (option->choices[j].choice))
+ conflicts[j] = 1;
+ }
+ }
+ else
+ {
+ for (j = 0; j < option->num_choices; j++)
+ {
+ if (strcasecmp (option->choices[j].choice, choice) == 0)
+ conflicts[j] = 1;
+ }
+ }
+ }
+
+ num_conflicts = 0;
+ all_default = TRUE;
+ for (j = 0; j < option->num_choices; j++)
+ {
+ if (conflicts[j])
+ num_conflicts++;
+ else if (strcmp (option->choices[j].choice, option->defchoice) != 0)
+ all_default = FALSE;
+ }
+
+ if (all_default && !keep_if_only_one_option)
+ return 0;
+
+ if (num_conflicts == option->num_choices)
+ return 0;
+
+
+ /* Some ppds don't have a "use printer default" option for
+ InputSlot. This means you always have to select a particular slot,
+ and you can't auto-pick source based on the paper size. To support
+ this we always add an auto option if there isn't one already. If
+ the user chooses the generated option we don't send any InputSlot
+ value when printing. The way we detect existing auto-cases is based
+ on feedback from Michael Sweet of cups fame.
+ */
+ add_auto = 0;
+ if (strcmp (option->keyword, "InputSlot") == 0)
+ {
+ gboolean found_auto = FALSE;
+ for (j = 0; j < option->num_choices; j++)
+ {
+ if (!conflicts[j])
+ {
+ if (strcmp (option->choices[j].choice, "Auto") == 0 ||
+ strcmp (option->choices[j].choice, "AutoSelect") == 0 ||
+ strcmp (option->choices[j].choice, "Default") == 0 ||
+ strcmp (option->choices[j].choice, "None") == 0 ||
+ strcmp (option->choices[j].choice, "PrinterDefault") == 0 ||
+ strcmp (option->choices[j].choice, "Unspecified") == 0 ||
+ option->choices[j].code == NULL ||
+ option->choices[j].code[0] == 0)
+ {
+ found_auto = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found_auto)
+ add_auto = 1;
+ }
+
+ if (available)
+ {
+
+ *available = g_new (ppd_choice_t *, option->num_choices - num_conflicts + add_auto);
+
+ i = 0;
+ for (j = 0; j < option->num_choices; j++)
+ {
+ if (!conflicts[j])
+ (*available)[i++] = &option->choices[j];
+ }
+
+ if (add_auto)
+ (*available)[i++] = NULL;
+ }
+
+ return option->num_choices - num_conflicts + add_auto;
+}
+
+static GtkPrinterOption *
+create_pickone_option (ppd_file_t *ppd_file,
+ ppd_option_t *ppd_option,
+ const char *gtk_name)
+{
+ GtkPrinterOption *option;
+ ppd_choice_t **available;
+ char *label;
+ int n_choices;
+ int i;
+
+ g_assert (ppd_option->ui == PPD_UI_PICKONE);
+
+ option = NULL;
+
+ n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
+ if (n_choices > 0)
+ {
+ label = get_option_text (ppd_file, ppd_option);
+ option = gtk_printer_option_new (gtk_name, label,
+ GTK_PRINTER_OPTION_TYPE_PICKONE);
+ g_free (label);
+
+ gtk_printer_option_allocate_choices (option, n_choices);
+ for (i = 0; i < n_choices; i++)
+ {
+ if (available[i] == NULL)
+ {
+ /* This was auto-added */
+ option->choices[i] = g_strdup ("gtk-ignore-value");
+ option->choices_display[i] = g_strdup (_("Printer Default"));
+ }
+ else
+ {
+ option->choices[i] = g_strdup (available[i]->choice);
+ option->choices_display[i] = get_choice_text (ppd_file, available[i]);
+ }
+ }
+ gtk_printer_option_set (option, ppd_option->defchoice);
+ }
+#ifdef PRINT_IGNORED_OPTIONS
+ else
+ g_warning ("Ignoring pickone %s\n", ppd_option->text);
+#endif
+ g_free (available);
+
+ return option;
+}
+
+static GtkPrinterOption *
+create_boolean_option (ppd_file_t *ppd_file,
+ ppd_option_t *ppd_option,
+ const char *gtk_name)
+{
+ GtkPrinterOption *option;
+ ppd_choice_t **available;
+ char *label;
+ int n_choices;
+
+ g_assert (ppd_option->ui == PPD_UI_BOOLEAN);
+
+ option = NULL;
+
+ n_choices = available_choices (ppd_file, ppd_option, &available, g_str_has_prefix (gtk_name, "gtk-"));
+ if (n_choices == 2)
+ {
+ label = get_option_text (ppd_file, ppd_option);
+ option = gtk_printer_option_new (gtk_name, label,
+ GTK_PRINTER_OPTION_TYPE_BOOLEAN);
+ g_free (label);
+
+ gtk_printer_option_allocate_choices (option, 2);
+ option->choices[0] = g_strdup ("True");
+ option->choices_display[0] = g_strdup ("True");
+ option->choices[1] = g_strdup ("True");
+ option->choices_display[1] = g_strdup ("True");
+
+ gtk_printer_option_set (option, ppd_option->defchoice);
+ }
+#ifdef PRINT_IGNORED_OPTIONS
+ else
+ g_warning ("Ignoring boolean %s\n", ppd_option->text);
+#endif
+ g_free (available);
+
+ return option;
+}
+
+static char *
+get_option_name (const char *keyword)
+{
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (option_names); i++)
+ if (strcmp (option_names[i].ppd_keyword, keyword) == 0)
+ return g_strdup (option_names[i].name);
+
+ return g_strdup_printf ("cups-%s", keyword);
+}
+
+static int
+strptr_cmp (const void *a, const void *b)
+{
+ char **aa = (char **)a;
+ char **bb = (char **)b;
+ return strcmp (*aa, *bb);
+}
+
+
+static gboolean
+string_in_table (char *str, const char *table[], int table_len)
+{
+ return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
+}
+
+#define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
+
+static void
+handle_option (GtkPrinterOptionSet *set,
+ ppd_file_t *ppd_file,
+ ppd_option_t *ppd_option,
+ ppd_group_t *toplevel_group,
+ GtkPrintSettings *settings)
+{
+ GtkPrinterOption *option;
+ char *name;
+
+ if (STRING_IN_TABLE (ppd_option->keyword, cups_option_blacklist))
+ return;
+
+ name = get_option_name (ppd_option->keyword);
+
+ option = NULL;
+ if (ppd_option->ui == PPD_UI_PICKONE)
+ {
+ option = create_pickone_option (ppd_file, ppd_option, name);
+ }
+ else if (ppd_option->ui == PPD_UI_BOOLEAN)
+ {
+ option = create_boolean_option (ppd_file, ppd_option, name);
+ }
+ else
+ g_warning ("Ignored pickmany setting %s\n", ppd_option->text);
+
+
+ if (option)
+ {
+ if (STRING_IN_TABLE (toplevel_group->name,
+ color_group_whitelist) ||
+ STRING_IN_TABLE (ppd_option->keyword,
+ color_option_whitelist))
+ {
+ option->group = g_strdup ("ColorPage");
+ }
+ else if (STRING_IN_TABLE (toplevel_group->name,
+ image_quality_group_whitelist) ||
+ STRING_IN_TABLE (ppd_option->keyword,
+ image_quality_option_whitelist))
+ {
+ option->group = g_strdup ("ImageQualityPage");
+ }
+ else if (STRING_IN_TABLE (toplevel_group->name,
+ finishing_group_whitelist) ||
+ STRING_IN_TABLE (ppd_option->keyword,
+ finishing_option_whitelist))
+ {
+ option->group = g_strdup ("FinishingPage");
+ }
+ else
+ {
+ option->group = g_strdup (toplevel_group->text);
+ }
+
+ set_option_from_settings (option, settings);
+
+ gtk_printer_option_set_add (set, option);
+ }
+
+ g_free (name);
+}
+
+static void
+handle_group (GtkPrinterOptionSet *set,
+ ppd_file_t *ppd_file,
+ ppd_group_t *group,
+ ppd_group_t *toplevel_group,
+ GtkPrintSettings *settings)
+{
+ int i;
+
+ /* Ignore installable options */
+ if (strcmp (toplevel_group->name, "InstallableOptions") == 0)
+ return;
+
+ for (i = 0; i < group->num_options; i++)
+ handle_option (set, ppd_file, &group->options[i], toplevel_group, settings);
+
+ for (i = 0; i < group->num_subgroups; i++)
+ handle_group (set, ppd_file, &group->subgroups[i], toplevel_group, settings);
+
+}
+
+static GtkPrinterOptionSet *
+cups_printer_get_options (GtkPrinter *printer,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup)
+{
+ GtkPrinterOptionSet *set;
+ GtkPrinterOption *option;
+ ppd_file_t *ppd_file;
+ int i;
+ char *print_at[] = { "now", "at", "on-hold" };
+ char *n_up[] = {"1", "2", "4", "6", "9", "16" };
+ char *prio[] = {"100", "80", "50", "30" };
+ char *prio_display[] = {N_("Urgent"), N_("High"), N_("Medium"), N_("Low") };
+ char *cover[] = {"none", "classified", "confidential", "secret", "standard", "topsecret", "unclassified" };
+ char *cover_display[] = {N_("None"), N_("Classified"), N_("Confidential"), N_("Secret"), N_("Standard"), N_("Top Secret"), N_("Unclassified"),};
+
+
+ set = gtk_printer_option_set_new ();
+
+ /* Cups specific, non-ppd related settings */
+
+ 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");
+ set_option_from_settings (option, settings);
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ for (i = 0; i < G_N_ELEMENTS(prio_display); i++)
+ prio_display[i] = _(prio_display[i]);
+
+ option = gtk_printer_option_new ("gtk-job-prio", "Job Priority", GTK_PRINTER_OPTION_TYPE_PICKONE);
+ gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (prio),
+ prio, prio_display);
+ gtk_printer_option_set (option, "50");
+ set_option_from_settings (option, settings);
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ option = gtk_printer_option_new ("gtk-billing-info", "Billing Info", GTK_PRINTER_OPTION_TYPE_STRING);
+ gtk_printer_option_set (option, "");
+ set_option_from_settings (option, settings);
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ for (i = 0; i < G_N_ELEMENTS(cover_display); i++)
+ cover_display[i] = _(cover_display[i]);
+
+ option = gtk_printer_option_new ("gtk-cover-before", "Before", GTK_PRINTER_OPTION_TYPE_PICKONE);
+ gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover),
+ cover, cover_display);
+ gtk_printer_option_set (option, "none");
+ set_option_from_settings (option, settings);
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ option = gtk_printer_option_new ("gtk-cover-after", "After", GTK_PRINTER_OPTION_TYPE_PICKONE);
+ gtk_printer_option_choices_from_array (option, G_N_ELEMENTS (cover),
+ cover, cover_display);
+ gtk_printer_option_set (option, "none");
+ set_option_from_settings (option, settings);
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ 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");
+ set_option_from_settings (option, settings);
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ option = gtk_printer_option_new ("gtk-print-time-text", "Print at time", GTK_PRINTER_OPTION_TYPE_STRING);
+ gtk_printer_option_set (option, "");
+ set_option_from_settings (option, settings);
+ gtk_printer_option_set_add (set, option);
+ g_object_unref (option);
+
+ /* Printer (ppd) specific settings */
+ ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+ if (ppd_file)
+ {
+ GtkPaperSize *paper_size;
+ ppd_option_t *option;
+
+ ppdMarkDefaults (ppd_file);
+
+ paper_size = gtk_page_setup_get_paper_size (page_setup);
+
+ option = ppdFindOption(ppd_file, "PageSize");
+ strncpy (option->defchoice, gtk_paper_size_get_ppd_name (paper_size),
+ PPD_MAX_NAME);
+
+ for (i = 0; i < ppd_file->num_groups; i++)
+ handle_group (set, ppd_file, &ppd_file->groups[i], &ppd_file->groups[i], settings);
+ }
+
+ return set;
+}
+
+
+static void
+mark_option_from_set (GtkPrinterOptionSet *set,
+ ppd_file_t *ppd_file,
+ ppd_option_t *ppd_option)
+{
+ GtkPrinterOption *option;
+ char *name = get_option_name (ppd_option->keyword);
+
+ option = gtk_printer_option_set_lookup (set, name);
+
+ if (option)
+ ppdMarkOption (ppd_file, ppd_option->keyword, option->value);
+
+ g_free (name);
+}
+
+
+static void
+mark_group_from_set (GtkPrinterOptionSet *set,
+ ppd_file_t *ppd_file,
+ ppd_group_t *group)
+{
+ int i;
+
+ for (i = 0; i < group->num_options; i++)
+ mark_option_from_set (set, ppd_file, &group->options[i]);
+
+ for (i = 0; i < group->num_subgroups; i++)
+ mark_group_from_set (set, ppd_file, &group->subgroups[i]);
+}
+
+static void
+set_conflicts_from_option (GtkPrinterOptionSet *set,
+ ppd_file_t *ppd_file,
+ ppd_option_t *ppd_option)
+{
+ GtkPrinterOption *option;
+ char *name;
+ if (ppd_option->conflicted)
+ {
+ name = get_option_name (ppd_option->keyword);
+ option = gtk_printer_option_set_lookup (set, name);
+
+ if (option)
+ gtk_printer_option_set_has_conflict (option, TRUE);
+ else
+ g_warning ("conflict for option %s ignored", ppd_option->keyword);
+
+ g_free (name);
+ }
+}
+
+static void
+set_conflicts_from_group (GtkPrinterOptionSet *set,
+ ppd_file_t *ppd_file,
+ ppd_group_t *group)
+{
+ int i;
+
+ for (i = 0; i < group->num_options; i++)
+ set_conflicts_from_option (set, ppd_file, &group->options[i]);
+
+ for (i = 0; i < group->num_subgroups; i++)
+ set_conflicts_from_group (set, ppd_file, &group->subgroups[i]);
+}
+
+static gboolean
+cups_printer_mark_conflicts (GtkPrinter *printer,
+ GtkPrinterOptionSet *options)
+{
+ ppd_file_t *ppd_file;
+ int num_conflicts;
+ int i;
+
+ ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+
+ if (ppd_file == NULL)
+ return FALSE;
+
+ ppdMarkDefaults (ppd_file);
+
+ for (i = 0; i < ppd_file->num_groups; i++)
+ mark_group_from_set (options, ppd_file, &ppd_file->groups[i]);
+
+ num_conflicts = ppdConflicts (ppd_file);
+
+ if (num_conflicts > 0)
+ {
+ for (i = 0; i < ppd_file->num_groups; i++)
+ set_conflicts_from_group (options, ppd_file, &ppd_file->groups[i]);
+ }
+
+ return num_conflicts > 0;
+}
+
+struct OptionData {
+ GtkPrinter *printer;
+ GtkPrinterOptionSet *options;
+ GtkPrintSettings *settings;
+ ppd_file_t *ppd_file;
+};
+
+typedef struct {
+ const char *cups;
+ const char *standard;
+} NameMapping;
+
+static void
+map_settings_to_option (GtkPrinterOption *option,
+ const NameMapping table[],
+ int n_elements,
+ GtkPrintSettings *settings,
+ const char *standard_name,
+ const char *cups_name)
+{
+ int i;
+ char *name;
+ const char *cups_value;
+ const char *standard_value;
+
+ /* If the cups-specific setting is set, always use that */
+
+ name = g_strdup_printf ("cups-%s", cups_name);
+ cups_value = gtk_print_settings_get (settings, name);
+ g_free (name);
+
+ if (cups_value != NULL) {
+ gtk_printer_option_set (option, cups_value);
+ return;
+ }
+
+ /* Otherwise we try to convert from the general setting */
+ standard_value = gtk_print_settings_get (settings, standard_name);
+ if (standard_value == NULL)
+ return;
+
+ for (i = 0; i < n_elements; i++)
+ {
+ if (table[i].cups == NULL && table[i].standard == NULL)
+ {
+ gtk_printer_option_set (option, standard_value);
+ break;
+ }
+ else if (table[i].cups == NULL &&
+ strcmp (table[i].standard, standard_value) == 0)
+ {
+ set_option_off (option);
+ break;
+ }
+ else if (strcmp (table[i].standard, standard_value) == 0)
+ {
+ gtk_printer_option_set (option, table[i].cups);
+ break;
+ }
+ }
+}
+
+static void
+map_option_to_settings (const char *value,
+ const NameMapping table[],
+ int n_elements,
+ GtkPrintSettings *settings,
+ const char *standard_name,
+ const char *cups_name)
+{
+ int i;
+ char *name;
+
+ for (i = 0; i < n_elements; i++)
+ {
+ if (table[i].cups == NULL && table[i].standard == NULL)
+ {
+ gtk_print_settings_set (settings,
+ standard_name,
+ value);
+ break;
+ }
+ else if (table[i].cups == NULL && table[i].standard != NULL)
+ {
+ if (value_is_off (value))
+ {
+ gtk_print_settings_set (settings,
+ standard_name,
+ table[i].standard);
+ break;
+ }
+ }
+ else if (strcmp (table[i].cups, value) == 0)
+ {
+ gtk_print_settings_set (settings,
+ standard_name,
+ table[i].standard);
+ break;
+ }
+ }
+
+ /* Always set the corresponding cups-specific setting */
+ name = g_strdup_printf ("cups-%s", cups_name);
+ gtk_print_settings_set (settings, name, value);
+ g_free (name);
+}
+
+
+static const NameMapping paper_source_map[] = {
+ { "Lower", "lower"},
+ { "Middle", "middle"},
+ { "Upper", "upper"},
+ { "Rear", "rear"},
+ { "Envelope", "envelope"},
+ { "Cassette", "cassette"},
+ { "LargeCapacity", "large-capacity"},
+ { "AnySmallFormat", "small-format"},
+ { "AnyLargeFormat", "large-format"},
+ { NULL, NULL}
+};
+
+static const NameMapping output_tray_map[] = {
+ { "Upper", "upper"},
+ { "Lower", "lower"},
+ { "Rear", "rear"},
+ { NULL, NULL}
+};
+
+static const NameMapping duplex_map[] = {
+ { "DuplexTumble", "vertical" },
+ { "DuplexNoTumble", "horizontal" },
+ { NULL, "simplex" }
+};
+
+static const NameMapping output_mode_map[] = {
+ { "Standard", "normal" },
+ { "Normal", "normal" },
+ { "Draft", "draft" },
+ { "Fast", "draft" },
+};
+
+static const NameMapping media_type_map[] = {
+ { "Transparency", "transparency"},
+ { "Standard", "stationery"},
+ { NULL, NULL}
+};
+
+static const NameMapping all_map[] = {
+ { NULL, NULL}
+};
+
+
+static void
+set_option_from_settings (GtkPrinterOption *option,
+ GtkPrintSettings *settings)
+{
+ const char *cups_value;
+ char *value;
+
+ if (settings == NULL)
+ return;
+
+ if (strcmp (option->name, "gtk-paper-source") == 0)
+ map_settings_to_option (option, paper_source_map, G_N_ELEMENTS (paper_source_map),
+ settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
+ else if (strcmp (option->name, "gtk-output-tray") == 0)
+ map_settings_to_option (option, output_tray_map, G_N_ELEMENTS (output_tray_map),
+ settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
+ else if (strcmp (option->name, "gtk-duplex") == 0)
+ map_settings_to_option (option, duplex_map, G_N_ELEMENTS (duplex_map),
+ settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
+ else if (strcmp (option->name, "cups-OutputMode") == 0)
+ map_settings_to_option (option, output_mode_map, G_N_ELEMENTS (output_mode_map),
+ settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
+ else if (strcmp (option->name, "cups-Resolution") == 0)
+ {
+ cups_value = gtk_print_settings_get (settings, option->name);
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ else
+ {
+ int res = gtk_print_settings_get_resolution (settings);
+ if (res != 0)
+ {
+ value = g_strdup_printf ("%ddpi", res);
+ gtk_printer_option_set (option, value);
+ g_free (value);
+ }
+ }
+ }
+ else if (strcmp (option->name, "gtk-paper-type") == 0)
+ map_settings_to_option (option, media_type_map, G_N_ELEMENTS (media_type_map),
+ settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
+ else if (strcmp (option->name, "gtk-n-up") == 0)
+ {
+ map_settings_to_option (option, all_map, G_N_ELEMENTS (all_map),
+ settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
+ }
+ else if (strcmp (option->name, "gtk-billing-info") == 0)
+ {
+ cups_value = gtk_print_settings_get (settings, "cups-job-billing");
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ }
+ else if (strcmp (option->name, "gtk-job-prio") == 0)
+ {
+ cups_value = gtk_print_settings_get (settings, "cups-job-priority");
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ }
+ else if (strcmp (option->name, "gtk-cover-before") == 0)
+ {
+ cups_value = gtk_print_settings_get (settings, "cover-before");
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ }
+ else if (strcmp (option->name, "gtk-cover-after") == 0)
+ {
+ cups_value = gtk_print_settings_get (settings, "cover-after");
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ }
+ else if (strcmp (option->name, "gtk-print-time") == 0)
+ {
+ cups_value = gtk_print_settings_get (settings, "print-at");
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ }
+ else if (strcmp (option->name, "gtk-print-time-text") == 0)
+ {
+ cups_value = gtk_print_settings_get (settings, "print-at-time");
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ }
+ else if (g_str_has_prefix (option->name, "cups-"))
+ {
+ cups_value = gtk_print_settings_get (settings, option->name);
+ if (cups_value)
+ gtk_printer_option_set (option, cups_value);
+ }
+}
+
+static void
+foreach_option_get_settings (GtkPrinterOption *option,
+ gpointer user_data)
+{
+ struct OptionData *data = user_data;
+ GtkPrintSettings *settings = data->settings;
+ const char *value;
+
+ value = option->value;
+
+ if (strcmp (option->name, "gtk-paper-source") == 0)
+ map_option_to_settings (value, paper_source_map, G_N_ELEMENTS (paper_source_map),
+ settings, GTK_PRINT_SETTINGS_DEFAULT_SOURCE, "InputSlot");
+ else if (strcmp (option->name, "gtk-output-tray") == 0)
+ map_option_to_settings (value, output_tray_map, G_N_ELEMENTS (output_tray_map),
+ settings, GTK_PRINT_SETTINGS_OUTPUT_BIN, "OutputBin");
+ else if (strcmp (option->name, "gtk-duplex") == 0)
+ map_option_to_settings (value, duplex_map, G_N_ELEMENTS (duplex_map),
+ settings, GTK_PRINT_SETTINGS_DUPLEX, "Duplex");
+ else if (strcmp (option->name, "cups-OutputMode") == 0)
+ map_option_to_settings (value, output_mode_map, G_N_ELEMENTS (output_mode_map),
+ settings, GTK_PRINT_SETTINGS_QUALITY, "OutputMode");
+ else if (strcmp (option->name, "cups-Resolution") == 0)
+ {
+ int res = atoi (value);
+ /* TODO: What if resolution is on XXXxYYYdpi form? */
+ if (res != 0)
+ gtk_print_settings_set_resolution (settings, res);
+ gtk_print_settings_set (settings, option->name, value);
+ }
+ else if (strcmp (option->name, "gtk-paper-type") == 0)
+ map_option_to_settings (value, media_type_map, G_N_ELEMENTS (media_type_map),
+ settings, GTK_PRINT_SETTINGS_MEDIA_TYPE, "MediaType");
+ else if (strcmp (option->name, "gtk-n-up") == 0)
+ map_option_to_settings (value, all_map, G_N_ELEMENTS (all_map),
+ settings, GTK_PRINT_SETTINGS_NUMBER_UP, "number-up");
+ else if (strcmp (option->name, "gtk-billing-info") == 0 && strlen (value) > 0)
+ gtk_print_settings_set (settings, "cups-job-billing", value);
+ else if (strcmp (option->name, "gtk-job-prio") == 0)
+ gtk_print_settings_set (settings, "cups-job-priority", value);
+ else if (strcmp (option->name, "gtk-cover-before") == 0)
+ gtk_print_settings_set (settings, "cover-before", value);
+ else if (strcmp (option->name, "gtk-cover-after") == 0)
+ gtk_print_settings_set (settings, "cover-after", value);
+ else if (strcmp (option->name, "gtk-print-time") == 0)
+ gtk_print_settings_set (settings, "print-at", value);
+ else if (strcmp (option->name, "gtk-print-time-text") == 0)
+ gtk_print_settings_set (settings, "print-at-time", value);
+ else if (g_str_has_prefix (option->name, "cups-"))
+ gtk_print_settings_set (settings, option->name, value);
+}
+
+static void
+cups_printer_get_settings_from_options (GtkPrinter *printer,
+ GtkPrinterOptionSet *options,
+ GtkPrintSettings *settings)
+{
+ struct OptionData data;
+ const char *print_at, *print_at_time;
+
+ data.printer = printer;
+ data.options = options;
+ data.settings = settings;
+ data.ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+
+ if (data.ppd_file != NULL)
+ {
+ GtkPrinterOption *cover_before, *cover_after;
+
+ gtk_printer_option_set_foreach (options, foreach_option_get_settings, &data);
+
+ cover_before = gtk_printer_option_set_lookup (options, "gtk-cover-before");
+ cover_after = gtk_printer_option_set_lookup (options, "gtk-cover-after");
+ if (cover_before && cover_after)
+ {
+ char *value = g_strdup_printf ("%s,%s", cover_before->value, cover_after->value);
+ gtk_print_settings_set (settings, "cups-job-sheets", value);
+ g_free (value);
+ }
+
+ print_at = gtk_print_settings_get (settings, "print-at");
+ print_at_time = gtk_print_settings_get (settings, "print-at-time");
+ if (strcmp (print_at, "at") == 0)
+ gtk_print_settings_set (settings, "cups-job-hold-until", print_at_time);
+ else if (strcmp (print_at, "on-hold") == 0)
+ gtk_print_settings_set (settings, "cups-job-hold-until", "indefinite");
+ }
+}
+
+static void
+cups_printer_prepare_for_print (GtkPrinter *printer,
+ GtkPrintJob *print_job,
+ GtkPrintSettings *settings,
+ GtkPageSetup *page_setup)
+{
+ GtkPageSet page_set;
+ GtkPaperSize *paper_size;
+ const char *ppd_paper_name;
+ double scale;
+
+ 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);
+
+ if (gtk_print_settings_get_collate (settings))
+ gtk_print_settings_set (settings, "cups-Collate", "True");
+ print_job->collate = FALSE;
+
+ if (gtk_print_settings_get_reverse (settings))
+ gtk_print_settings_set (settings, "cups-OutputOrder", "Reverse");
+ print_job->reverse = FALSE;
+
+ if (gtk_print_settings_get_num_copies (settings) > 1)
+ gtk_print_settings_set_int (settings, "cups-copies",
+ gtk_print_settings_get_num_copies (settings));
+ print_job->num_copies = 1;
+
+ scale = gtk_print_settings_get_scale (settings);
+ print_job->scale = 1.0;
+ if (scale != 100.0)
+ print_job->scale = scale/100.0;
+
+ page_set = gtk_print_settings_get_page_set (settings);
+ if (page_set == GTK_PAGE_SET_EVEN)
+ gtk_print_settings_set (settings, "cups-page-set", "even");
+ else if (page_set == GTK_PAGE_SET_ODD)
+ gtk_print_settings_set (settings, "cups-page-set", "odd");
+ print_job->page_set = GTK_PAGE_SET_ALL;
+
+ paper_size = gtk_page_setup_get_paper_size (page_setup);
+ ppd_paper_name = gtk_paper_size_get_ppd_name (paper_size);
+ if (ppd_paper_name != NULL)
+ gtk_print_settings_set (settings, "cups-PageSize", ppd_paper_name);
+ else
+ {
+ char *custom_name = g_strdup_printf ("Custom.%2fx%.2f",
+ gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS),
+ gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS));
+ gtk_print_settings_set (settings, "cups-PageSize", custom_name);
+ g_free (custom_name);
+ }
+
+ print_job->rotate_to_orientation = TRUE;
+}
+
+static GList *
+cups_printer_list_papers (GtkPrinter *printer)
+{
+ ppd_file_t *ppd_file;
+ ppd_size_t *size;
+ char *display_name;
+ GtkPageSetup *page_setup;
+ GtkPaperSize *paper_size;
+ ppd_option_t *option;
+ ppd_choice_t *choice;
+ GList *l;
+ int i;
+
+ ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+ if (ppd_file == NULL)
+ return NULL;
+
+ l = NULL;
+
+ for (i = 0; i < ppd_file->num_sizes; i++)
+ {
+ size = &ppd_file->sizes[i];
+
+ display_name = NULL;
+ option = ppdFindOption(ppd_file, "PageSize");
+ if (option)
+ {
+ choice = ppdFindChoice(option, size->name);
+ if (choice)
+ display_name = ppd_text_to_utf8 (ppd_file, choice->text);
+ }
+ if (display_name == NULL)
+ display_name = g_strdup (size->name);
+
+ page_setup = gtk_page_setup_new ();
+ paper_size = gtk_paper_size_new_from_ppd (size->name,
+ display_name,
+ size->width,
+ size->length);
+ gtk_page_setup_set_paper_size (page_setup, paper_size);
+ gtk_paper_size_free (paper_size);
+
+ gtk_page_setup_set_top_margin (page_setup, size->length - size->top, GTK_UNIT_POINTS);
+ gtk_page_setup_set_bottom_margin (page_setup, size->bottom, GTK_UNIT_POINTS);
+ gtk_page_setup_set_left_margin (page_setup, size->left, GTK_UNIT_POINTS);
+ gtk_page_setup_set_right_margin (page_setup, size->width - size->right, GTK_UNIT_POINTS);
+
+ g_free (display_name);
+
+ l = g_list_prepend (l, page_setup);
+ }
+
+ return g_list_reverse (l);
+}
+
+static void
+cups_printer_get_hard_margins (GtkPrinter *printer,
+ double *top,
+ double *bottom,
+ double *left,
+ double *right)
+{
+ ppd_file_t *ppd_file;
+
+ ppd_file = gtk_printer_cups_get_ppd (GTK_PRINTER_CUPS (printer));
+ if (ppd_file == NULL)
+ return;
+
+ *left = ppd_file->custom_margins[0];
+ *bottom = ppd_file->custom_margins[1];
+ *right = ppd_file->custom_margins[2];
+ *top = ppd_file->custom_margins[3];
+}
diff --git a/modules/printbackends/cups/gtkprintbackendcups.h b/modules/printbackends/cups/gtkprintbackendcups.h
new file mode 100644
index 0000000000..b1e136970a
--- /dev/null
+++ b/modules/printbackends/cups/gtkprintbackendcups.h
@@ -0,0 +1,42 @@
+/* GTK - The GIMP Toolkit
+ * gtkprintbackendcups.h: Default implementation of GtkPrintBackend for the Common Unix Print System (CUPS)
+ * Copyright (C) 2003, Red Hat, 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.
+ */
+
+#ifndef __GTK_PRINT_BACKEND_CUPS_H__
+#define __GTK_PRINT_BACKEND_CUPS_H__
+
+#include <glib-object.h>
+#include "gtkprintbackend.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINT_BACKEND_CUPS (gtk_print_backend_cups_get_type ())
+#define GTK_PRINT_BACKEND_CUPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINT_BACKEND_CUPS, GtkPrintBackendCups))
+#define GTK_IS_PRINT_BACKEND_CUPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINT_BACKEND_CUPS))
+
+typedef struct _GtkPrintBackendCups GtkPrintBackendCups;
+
+GtkPrintBackend *gtk_print_backend_cups_new (void);
+GType gtk_print_backend_cups_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GTK_PRINT_BACKEND_CUPS_H__ */
+
+
diff --git a/modules/printbackends/cups/gtkprintercups.c b/modules/printbackends/cups/gtkprintercups.c
new file mode 100644
index 0000000000..c36b8077e5
--- /dev/null
+++ b/modules/printbackends/cups/gtkprintercups.c
@@ -0,0 +1,125 @@
+/* GtkPrinterCupsCups
+ * Copyright (C) 2006 John (J5) Palmieri <johnp@redhat.com>
+ *
+ * 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 "config.h"
+#include "gtkprintercups.h"
+
+static void gtk_printer_cups_init (GtkPrinterCups *printer);
+static void gtk_printer_cups_class_init (GtkPrinterCupsClass *class);
+static void gtk_printer_cups_finalize (GObject *object);
+
+static GtkPrinterClass *gtk_printer_cups_parent_class;
+static GType gtk_printer_cups_type = 0;
+
+void
+gtk_printer_cups_register_type (GTypeModule *module)
+{
+ static const GTypeInfo object_info =
+ {
+ sizeof (GtkPrinterCupsClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) gtk_printer_cups_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GtkPrinterCups),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_printer_cups_init,
+ };
+
+ gtk_printer_cups_type = g_type_module_register_type (module,
+ GTK_TYPE_PRINTER,
+ "GtkPrinterCups",
+ &object_info, 0);
+}
+
+GType
+gtk_printer_cups_get_type (void)
+{
+ return gtk_printer_cups_type;
+}
+
+static void
+gtk_printer_cups_class_init (GtkPrinterCupsClass *class)
+{
+ GObjectClass *object_class = (GObjectClass *) class;
+
+ gtk_printer_cups_parent_class = g_type_class_peek_parent (class);
+
+ object_class->finalize = gtk_printer_cups_finalize;
+}
+
+static void
+gtk_printer_cups_init (GtkPrinterCups *printer)
+{
+ printer->device_uri = NULL;
+ printer->printer_uri = NULL;
+ printer->state = 0;
+ printer->hostname = NULL;
+ printer->port = 0;
+ printer->ppd_file = NULL;
+}
+
+static void
+gtk_printer_cups_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+
+ GtkPrinterCups *printer = GTK_PRINTER_CUPS (object);
+
+ g_free (printer->device_uri);
+ g_free (printer->printer_uri);
+ g_free (printer->hostname);
+
+ if (printer->ppd_file)
+ ppdClose (printer->ppd_file);
+
+ if (G_OBJECT_CLASS (gtk_printer_cups_parent_class)->finalize)
+ G_OBJECT_CLASS (gtk_printer_cups_parent_class)->finalize (object);
+}
+
+/**
+ * gtk_printer_cups_new:
+ *
+ * Creates a new #GtkPrinterCups.
+ *
+ * Return value: a new #GtkPrinterCups
+ *
+ * Since: 2.10
+ **/
+GtkPrinterCups *
+gtk_printer_cups_new (const char *name,
+ GtkPrintBackend *backend)
+{
+ GObject *result;
+
+ result = g_object_new (GTK_TYPE_PRINTER_CUPS,
+ "name", name,
+ "backend", backend,
+ "is-virtual", FALSE,
+ NULL);
+
+ return (GtkPrinterCups *) result;
+}
+
+ppd_file_t *
+gtk_printer_cups_get_ppd (GtkPrinterCups *printer)
+{
+ return printer->ppd_file;
+}
diff --git a/modules/printbackends/cups/gtkprintercups.h b/modules/printbackends/cups/gtkprintercups.h
new file mode 100644
index 0000000000..510050918f
--- /dev/null
+++ b/modules/printbackends/cups/gtkprintercups.h
@@ -0,0 +1,70 @@
+/* GtkPrinterCups
+ * Copyright (C) 2006 John (J5) Palmieri <johnp@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_PRINTER_CUPS_H__
+#define __GTK_PRINTER_CUPS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <cups/cups.h>
+#include <cups/ppd.h>
+
+#include "gtkprinter.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PRINTER_CUPS (gtk_printer_cups_get_type ())
+#define GTK_PRINTER_CUPS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCups))
+#define GTK_PRINTER_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass))
+#define GTK_IS_PRINTER_CUPS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PRINTER_CUPS))
+#define GTK_IS_PRINTER_CUPS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINTER_CUPS))
+#define GTK_PRINTER_CUPS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINTER_CUPS, GtkPrinterCupsClass))
+
+typedef struct _GtkPrinterCups GtkPrinterCups;
+typedef struct _GtkPrinterCupsClass GtkPrinterCupsClass;
+typedef struct _GtkPrinterCupsPrivate GtkPrinterCupsPrivate;
+
+struct _GtkPrinterCups
+{
+ GtkPrinter parent_instance;
+
+ gchar *device_uri;
+ gchar *printer_uri;
+ gchar *hostname;
+ gint port;
+
+ ipp_pstate_t state;
+ gboolean reading_ppd;
+ ppd_file_t *ppd_file;
+};
+
+struct _GtkPrinterCupsClass
+{
+ GtkPrinterClass parent_class;
+
+};
+
+GType gtk_printer_cups_get_type (void) G_GNUC_CONST;
+void gtk_printer_cups_register_type (GTypeModule *module);
+GtkPrinterCups *gtk_printer_cups_new (const char *name,
+ GtkPrintBackend *backend);
+ppd_file_t * gtk_printer_cups_get_ppd (GtkPrinterCups *printer);
+
+G_END_DECLS
+
+#endif /* __GTK_PRINTER_CUPS_H__ */