1 /* GTK - The GIMP Toolkit
2 * gtkprintoperation-portal.c: Print Operation Details for sandboxed apps
3 * Copyright (C) 2016, Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #include <string.h>
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26
27 #include <cairo-pdf.h>
28 #include <cairo-ps.h>
29
30 #include <gio/gunixfdlist.h>
31
32 #include "gtkprintoperation-private.h"
33 #include "gtkprintoperation-portal.h"
34 #include "gtkprintsettings.h"
35 #include "gtkpagesetup.h"
36 #include "gtkprintbackend.h"
37 #include "gtkshow.h"
38 #include "gtkintl.h"
39 #include "gtkwindowprivate.h"
40 #include "gtkprivate.h"
41
42
43 typedef struct {
44 GtkPrintOperation *op;
45 GDBusProxy *proxy;
46 guint response_signal_id;
47 gboolean do_print;
48 GtkPrintOperationResult result;
49 GtkPrintOperationPrintFunc print_cb;
50 GtkWindow *parent;
51 GMainLoop *loop;
52 guint32 token;
53 GDestroyNotify destroy;
54 GVariant *settings;
55 GVariant *setup;
56 GVariant *options;
57 char *prepare_print_handle;
58 } PortalData;
59
60 static void
portal_data_free(gpointer data)61 portal_data_free (gpointer data)
62 {
63 PortalData *portal = data;
64
65 g_object_unref (portal->op);
66 g_object_unref (portal->proxy);
67 if (portal->loop)
68 g_main_loop_unref (portal->loop);
69 if (portal->settings)
70 g_variant_unref (portal->settings);
71 if (portal->setup)
72 g_variant_unref (portal->setup);
73 if (portal->options)
74 g_variant_unref (portal->options);
75 g_free (portal->prepare_print_handle);
76 g_free (portal);
77 }
78
79 typedef struct {
80 GDBusProxy *proxy;
81 GtkPrintJob *job;
82 guint32 token;
83 cairo_surface_t *surface;
84 GMainLoop *loop;
85 gboolean file_written;
86 } GtkPrintOperationPortal;
87
88 static void
op_portal_free(GtkPrintOperationPortal * op_portal)89 op_portal_free (GtkPrintOperationPortal *op_portal)
90 {
91 g_clear_object (&op_portal->proxy);
92 g_clear_object (&op_portal->job);
93 if (op_portal->loop)
94 g_main_loop_unref (op_portal->loop);
95 g_free (op_portal);
96 }
97
98 static void
portal_start_page(GtkPrintOperation * op,GtkPrintContext * print_context,GtkPageSetup * page_setup)99 portal_start_page (GtkPrintOperation *op,
100 GtkPrintContext *print_context,
101 GtkPageSetup *page_setup)
102 {
103 GtkPrintOperationPortal *op_portal = op->priv->platform_data;
104 GtkPaperSize *paper_size;
105 cairo_surface_type_t type;
106 gdouble w, h;
107
108 paper_size = gtk_page_setup_get_paper_size (page_setup);
109
110 w = gtk_paper_size_get_width (paper_size, GTK_UNIT_POINTS);
111 h = gtk_paper_size_get_height (paper_size, GTK_UNIT_POINTS);
112
113 type = cairo_surface_get_type (op_portal->surface);
114
115 if ((op->priv->manual_number_up < 2) ||
116 (op->priv->page_position % op->priv->manual_number_up == 0))
117 {
118 if (type == CAIRO_SURFACE_TYPE_PS)
119 {
120 cairo_ps_surface_set_size (op_portal->surface, w, h);
121 cairo_ps_surface_dsc_begin_page_setup (op_portal->surface);
122 switch (gtk_page_setup_get_orientation (page_setup))
123 {
124 case GTK_PAGE_ORIENTATION_PORTRAIT:
125 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
126 cairo_ps_surface_dsc_comment (op_portal->surface, "%%PageOrientation: Portrait");
127 break;
128
129 case GTK_PAGE_ORIENTATION_LANDSCAPE:
130 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
131 cairo_ps_surface_dsc_comment (op_portal->surface, "%%PageOrientation: Landscape");
132 break;
133 }
134 }
135 else if (type == CAIRO_SURFACE_TYPE_PDF)
136 {
137 if (!op->priv->manual_orientation)
138 {
139 w = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
140 h = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
141 }
142 cairo_pdf_surface_set_size (op_portal->surface, w, h);
143 }
144 }
145 }
146
147 static void
portal_end_page(GtkPrintOperation * op,GtkPrintContext * print_context)148 portal_end_page (GtkPrintOperation *op,
149 GtkPrintContext *print_context)
150 {
151 cairo_t *cr;
152
153 cr = gtk_print_context_get_cairo_context (print_context);
154
155 if ((op->priv->manual_number_up < 2) ||
156 ((op->priv->page_position + 1) % op->priv->manual_number_up == 0) ||
157 (op->priv->page_position == op->priv->nr_of_pages_to_print - 1))
158 cairo_show_page (cr);
159 }
160
161 static void
print_file_done(GObject * source,GAsyncResult * result,gpointer data)162 print_file_done (GObject *source,
163 GAsyncResult *result,
164 gpointer data)
165 {
166 GtkPrintOperation *op = data;
167 GtkPrintOperationPortal *op_portal = op->priv->platform_data;
168 GError *error = NULL;
169 GVariant *ret;
170
171 ret = g_dbus_proxy_call_finish (op_portal->proxy,
172 result,
173 &error);
174 if (ret == NULL)
175 {
176 if (op->priv->error == NULL)
177 op->priv->error = g_error_copy (error);
178 g_warning ("Print file failed: %s", error->message);
179 g_error_free (error);
180 }
181 else
182 g_variant_unref (ret);
183
184 if (op_portal->loop)
185 g_main_loop_quit (op_portal->loop);
186
187 g_object_unref (op);
188 }
189
190 static void
portal_job_complete(GtkPrintJob * job,gpointer data,const GError * error)191 portal_job_complete (GtkPrintJob *job,
192 gpointer data,
193 const GError *error)
194 {
195 GtkPrintOperation *op = data;
196 GtkPrintOperationPortal *op_portal = op->priv->platform_data;
197 GtkPrintSettings *settings;
198 const char *uri;
199 char *filename;
200 int fd, idx;
201 GVariantBuilder opt_builder;
202 GUnixFDList *fd_list;
203
204 if (error != NULL && op->priv->error == NULL)
205 {
206 g_warning ("Print job failed: %s", error->message);
207 op->priv->error = g_error_copy (error);
208 return;
209 }
210
211 op_portal->file_written = TRUE;
212
213 settings = gtk_print_job_get_settings (job);
214 uri = gtk_print_settings_get (settings, GTK_PRINT_SETTINGS_OUTPUT_URI);
215 filename = g_filename_from_uri (uri, NULL, NULL);
216
217 fd = open (filename, O_RDONLY|O_CLOEXEC);
218 fd_list = g_unix_fd_list_new ();
219 idx = g_unix_fd_list_append (fd_list, fd, NULL);
220 close (fd);
221
222 g_free (filename);
223
224 g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
225 g_variant_builder_add (&opt_builder, "{sv}", "token", g_variant_new_uint32 (op_portal->token));
226
227 g_dbus_proxy_call_with_unix_fd_list (op_portal->proxy,
228 "Print",
229 g_variant_new ("(ssh@a{sv})",
230 "", /* window */
231 _("Print"), /* title */
232 idx,
233 g_variant_builder_end (&opt_builder)),
234 G_DBUS_CALL_FLAGS_NONE,
235 -1,
236 fd_list,
237 NULL,
238 print_file_done,
239 op);
240 g_object_unref (fd_list);
241 }
242
243 static void
portal_end_run(GtkPrintOperation * op,gboolean wait,gboolean cancelled)244 portal_end_run (GtkPrintOperation *op,
245 gboolean wait,
246 gboolean cancelled)
247 {
248 GtkPrintOperationPortal *op_portal = op->priv->platform_data;
249
250 cairo_surface_finish (op_portal->surface);
251
252 if (cancelled)
253 return;
254
255 if (wait)
256 op_portal->loop = g_main_loop_new (NULL, FALSE);
257
258 /* TODO: Check for error */
259 if (op_portal->job != NULL)
260 {
261 g_object_ref (op);
262 gtk_print_job_send (op_portal->job, portal_job_complete, op, NULL);
263 }
264
265 if (wait)
266 {
267 g_object_ref (op);
268 if (!op_portal->file_written)
269 {
270 gdk_threads_leave ();
271 g_main_loop_run (op_portal->loop);
272 gdk_threads_enter ();
273 }
274 g_object_unref (op);
275 }
276 }
277
278 static void
finish_print(PortalData * portal,GtkPrinter * printer,GtkPageSetup * page_setup,GtkPrintSettings * settings)279 finish_print (PortalData *portal,
280 GtkPrinter *printer,
281 GtkPageSetup *page_setup,
282 GtkPrintSettings *settings)
283 {
284 GtkPrintOperation *op = portal->op;
285 GtkPrintOperationPrivate *priv = op->priv;
286 GtkPrintJob *job;
287 GtkPrintOperationPortal *op_portal;
288 cairo_t *cr;
289
290 if (portal->do_print)
291 {
292 gtk_print_operation_set_print_settings (op, settings);
293 priv->print_context = _gtk_print_context_new (op);
294
295 _gtk_print_context_set_hard_margins (priv->print_context, 0, 0, 0, 0);
296
297 gtk_print_operation_set_default_page_setup (op, page_setup);
298 _gtk_print_context_set_page_setup (priv->print_context, page_setup);
299
300 op_portal = g_new0 (GtkPrintOperationPortal, 1);
301 priv->platform_data = op_portal;
302 priv->free_platform_data = (GDestroyNotify) op_portal_free;
303
304 priv->start_page = portal_start_page;
305 priv->end_page = portal_end_page;
306 priv->end_run = portal_end_run;
307
308 job = gtk_print_job_new (priv->job_name, printer, settings, page_setup);
309 op_portal->job = job;
310
311 op_portal->proxy = g_object_ref (portal->proxy);
312 op_portal->token = portal->token;
313
314 op_portal->surface = gtk_print_job_get_surface (job, &priv->error);
315 if (op_portal->surface == NULL)
316 {
317 portal->result = GTK_PRINT_OPERATION_RESULT_ERROR;
318 portal->do_print = FALSE;
319 goto out;
320 }
321
322 cr = cairo_create (op_portal->surface);
323 gtk_print_context_set_cairo_context (priv->print_context, cr, 72, 72);
324 cairo_destroy (cr);
325
326 priv->print_pages = gtk_print_job_get_pages (job);
327 priv->page_ranges = gtk_print_job_get_page_ranges (job, &priv->num_page_ranges);
328 priv->manual_num_copies = gtk_print_job_get_num_copies (job);
329 priv->manual_collation = gtk_print_job_get_collate (job);
330 priv->manual_reverse = gtk_print_job_get_reverse (job);
331 priv->manual_page_set = gtk_print_job_get_page_set (job);
332 priv->manual_scale = gtk_print_job_get_scale (job);
333 priv->manual_orientation = gtk_print_job_get_rotate (job);
334 priv->manual_number_up = gtk_print_job_get_n_up (job);
335 priv->manual_number_up_layout = gtk_print_job_get_n_up_layout (job);
336 }
337
338 out:
339 if (portal->print_cb)
340 portal->print_cb (op, portal->parent, portal->do_print, portal->result);
341
342 if (portal->destroy)
343 portal->destroy (portal);
344 }
345
346 static GtkPrinter *
find_file_printer(void)347 find_file_printer (void)
348 {
349 GList *backends, *l, *printers;
350 GtkPrinter *printer;
351
352 printer = NULL;
353
354 backends = gtk_print_backend_load_modules ();
355 for (l = backends; l; l = l->next)
356 {
357 GtkPrintBackend *backend = l->data;
358 if (strcmp (G_OBJECT_TYPE_NAME (backend), "GtkPrintBackendFile") == 0)
359 {
360 printers = gtk_print_backend_get_printer_list (backend);
361 printer = printers->data;
362 g_list_free (printers);
363 break;
364 }
365 }
366 g_list_free (backends);
367
368 return printer;
369 }
370
371 static void
prepare_print_response(GDBusConnection * connection,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer data)372 prepare_print_response (GDBusConnection *connection,
373 const char *sender_name,
374 const char *object_path,
375 const char *interface_name,
376 const char *signal_name,
377 GVariant *parameters,
378 gpointer data)
379 {
380 PortalData *portal = data;
381 guint32 response;
382 GVariant *options;
383
384 if (portal->response_signal_id != 0)
385 {
386 g_dbus_connection_signal_unsubscribe (connection,
387 portal->response_signal_id);
388 portal->response_signal_id = 0;
389 }
390
391 g_variant_get (parameters, "(u@a{sv})", &response, &options);
392
393 portal->do_print = (response == 0);
394
395 if (portal->do_print)
396 {
397 GVariant *v;
398 GtkPrintSettings *settings;
399 GtkPageSetup *page_setup;
400 GtkPrinter *printer;
401 char *filename;
402 char *uri;
403 int fd;
404
405 portal->result = GTK_PRINT_OPERATION_RESULT_APPLY;
406
407 v = g_variant_lookup_value (options, "settings", G_VARIANT_TYPE_VARDICT);
408 settings = gtk_print_settings_new_from_gvariant (v);
409 g_variant_unref (v);
410
411 v = g_variant_lookup_value (options, "page-setup", G_VARIANT_TYPE_VARDICT);
412 page_setup = gtk_page_setup_new_from_gvariant (v);
413 g_variant_unref (v);
414
415 g_variant_lookup (options, "token", "u", &portal->token);
416
417 printer = find_file_printer ();
418
419 fd = g_file_open_tmp ("gtkprintXXXXXX", &filename, NULL);
420 uri = g_filename_to_uri (filename, NULL, NULL);
421 gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_URI, uri);
422 g_free (uri);
423 close (fd);
424
425 finish_print (portal, printer, page_setup, settings);
426 g_free (filename);
427 }
428 else
429 {
430 portal->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
431
432 if (portal->print_cb)
433 portal->print_cb (portal->op, portal->parent, portal->do_print, portal->result);
434
435 if (portal->destroy)
436 portal->destroy (portal);
437 }
438 if (portal->loop)
439 g_main_loop_quit (portal->loop);
440 }
441
442 static void
prepare_print_called(GObject * source,GAsyncResult * result,gpointer data)443 prepare_print_called (GObject *source,
444 GAsyncResult *result,
445 gpointer data)
446 {
447 PortalData *portal = data;
448 GError *error = NULL;
449 const char *handle = NULL;
450 GVariant *ret;
451
452 ret = g_dbus_proxy_call_finish (portal->proxy, result, &error);
453 if (ret == NULL)
454 {
455 if (portal->op->priv->error == NULL)
456 portal->op->priv->error = g_error_copy (error);
457 g_error_free (error);
458 if (portal->loop)
459 g_main_loop_quit (portal->loop);
460 return;
461 }
462 else
463 g_variant_get (ret, "(&o)", &handle);
464
465 if (strcmp (portal->prepare_print_handle, handle) != 0)
466 {
467 g_free (portal->prepare_print_handle);
468 portal->prepare_print_handle = g_strdup (handle);
469 g_dbus_connection_signal_unsubscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY (portal->proxy)),
470 portal->response_signal_id);
471 portal->response_signal_id =
472 g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY (portal->proxy)),
473 "org.freedesktop.portal.Desktop",
474 "org.freedesktop.portal.Request",
475 "Response",
476 handle,
477 NULL,
478 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
479 prepare_print_response,
480 portal, NULL);
481 }
482
483 g_variant_unref (ret);
484 }
485
486 PortalData *
create_portal_data(GtkPrintOperation * op,GtkWindow * parent,GtkPrintOperationPrintFunc print_cb)487 create_portal_data (GtkPrintOperation *op,
488 GtkWindow *parent,
489 GtkPrintOperationPrintFunc print_cb)
490 {
491 GDBusProxy *proxy;
492 PortalData *portal;
493 guint signal_id;
494 GError *error = NULL;
495
496 signal_id = g_signal_lookup ("create-custom-widget", GTK_TYPE_PRINT_OPERATION);
497 if (g_signal_has_handler_pending (op, signal_id, 0, TRUE))
498 g_warning ("GtkPrintOperation::create-custom-widget not supported with portal");
499
500 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
501 G_DBUS_PROXY_FLAGS_NONE,
502 NULL,
503 "org.freedesktop.portal.Desktop",
504 "/org/freedesktop/portal/desktop",
505 "org.freedesktop.portal.Print",
506 NULL,
507 &error);
508
509 if (proxy == NULL)
510 {
511 if (op->priv->error == NULL)
512 op->priv->error = g_error_copy (error);
513 g_error_free (error);
514 return NULL;
515 }
516
517 portal = g_new0 (PortalData, 1);
518 portal->proxy = proxy;
519 portal->op = g_object_ref (op);
520 portal->parent = parent;
521 portal->result = GTK_PRINT_OPERATION_RESULT_CANCEL;
522 portal->print_cb = print_cb;
523
524 if (print_cb) /* async case */
525 {
526 portal->loop = NULL;
527 portal->destroy = portal_data_free;
528 }
529 else
530 {
531 portal->loop = g_main_loop_new (NULL, FALSE);
532 portal->destroy = NULL;
533 }
534
535 return portal;
536 }
537
538 static void
window_handle_exported(GtkWindow * window,const char * handle_str,gpointer user_data)539 window_handle_exported (GtkWindow *window,
540 const char *handle_str,
541 gpointer user_data)
542 {
543 PortalData *portal = user_data;
544
545 g_dbus_proxy_call (portal->proxy,
546 "PreparePrint",
547 g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
548 handle_str,
549 _("Print"), /* title */
550 portal->settings,
551 portal->setup,
552 portal->options),
553 G_DBUS_CALL_FLAGS_NONE,
554 -1,
555 NULL,
556 prepare_print_called,
557 portal);
558 }
559
560 static void
call_prepare_print(GtkPrintOperation * op,PortalData * portal)561 call_prepare_print (GtkPrintOperation *op,
562 PortalData *portal)
563 {
564 GtkPrintOperationPrivate *priv = op->priv;
565 GVariantBuilder opt_builder;
566 char *token;
567
568 portal->prepare_print_handle =
569 gtk_get_portal_request_path (g_dbus_proxy_get_connection (portal->proxy), &token);
570
571 portal->response_signal_id =
572 g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (G_DBUS_PROXY (portal->proxy)),
573 "org.freedesktop.portal.Desktop",
574 "org.freedesktop.portal.Request",
575 "Response",
576 portal->prepare_print_handle,
577 NULL,
578 G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
579 prepare_print_response,
580 portal, NULL);
581
582 g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
583 g_variant_builder_add (&opt_builder, "{sv}", "handle_token", g_variant_new_string (token));
584 g_free (token);
585 portal->options = g_variant_builder_end (&opt_builder);
586
587 if (priv->print_settings)
588 portal->settings = gtk_print_settings_to_gvariant (priv->print_settings);
589 else
590 {
591 GVariantBuilder builder;
592 g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
593 portal->settings = g_variant_builder_end (&builder);
594 }
595
596 if (priv->default_page_setup)
597 portal->setup = gtk_page_setup_to_gvariant (priv->default_page_setup);
598 else
599 {
600 GtkPageSetup *page_setup = gtk_page_setup_new ();
601 portal->setup = gtk_page_setup_to_gvariant (page_setup);
602 g_object_unref (page_setup);
603 }
604
605 g_variant_ref_sink (portal->options);
606 g_variant_ref_sink (portal->settings);
607 g_variant_ref_sink (portal->setup);
608
609 if (portal->parent != NULL &&
610 gtk_widget_is_visible (GTK_WIDGET (portal->parent)) &&
611 gtk_window_export_handle (portal->parent, window_handle_exported, portal))
612 return;
613
614 g_dbus_proxy_call (portal->proxy,
615 "PreparePrint",
616 g_variant_new ("(ss@a{sv}@a{sv}@a{sv})",
617 "",
618 _("Print"), /* title */
619 portal->settings,
620 portal->setup,
621 portal->options),
622 G_DBUS_CALL_FLAGS_NONE,
623 -1,
624 NULL,
625 prepare_print_called,
626 portal);
627 }
628
629 GtkPrintOperationResult
gtk_print_operation_portal_run_dialog(GtkPrintOperation * op,gboolean show_dialog,GtkWindow * parent,gboolean * do_print)630 gtk_print_operation_portal_run_dialog (GtkPrintOperation *op,
631 gboolean show_dialog,
632 GtkWindow *parent,
633 gboolean *do_print)
634 {
635 PortalData *portal;
636 GtkPrintOperationResult result;
637
638 portal = create_portal_data (op, parent, NULL);
639 if (portal == NULL)
640 return GTK_PRINT_OPERATION_RESULT_ERROR;
641
642 call_prepare_print (op, portal);
643
644 gdk_threads_leave ();
645 g_main_loop_run (portal->loop);
646 gdk_threads_enter ();
647
648 *do_print = portal->do_print;
649 result = portal->result;
650
651 portal_data_free (portal);
652
653 return result;
654 }
655
656 void
gtk_print_operation_portal_run_dialog_async(GtkPrintOperation * op,gboolean show_dialog,GtkWindow * parent,GtkPrintOperationPrintFunc print_cb)657 gtk_print_operation_portal_run_dialog_async (GtkPrintOperation *op,
658 gboolean show_dialog,
659 GtkWindow *parent,
660 GtkPrintOperationPrintFunc print_cb)
661 {
662 PortalData *portal;
663
664 portal = create_portal_data (op, parent, print_cb);
665 if (portal == NULL)
666 return;
667
668 call_prepare_print (op, portal);
669 }
670
671 void
gtk_print_operation_portal_launch_preview(GtkPrintOperation * op,cairo_surface_t * surface,GtkWindow * parent,const char * filename)672 gtk_print_operation_portal_launch_preview (GtkPrintOperation *op,
673 cairo_surface_t *surface,
674 GtkWindow *parent,
675 const char *filename)
676 {
677 char *uri;
678
679 uri = g_filename_to_uri (filename, NULL, NULL);
680 gtk_show_uri_on_window (parent, uri, GDK_CURRENT_TIME, NULL);
681 g_free (uri);
682 }
683