~ken-vandine/+junk/xdg-desktop-portal-gtk_0.10

« back to all changes in this revision

Viewing changes to src/print.c

  • Committer: Ken VanDine
  • Date: 2018-02-14 16:27:00 UTC
  • Revision ID: ken.vandine@canonical.com-20180214162700-e61q0115hwx2bb3z
init

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2016 Red Hat, Inc
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Lesser General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Lesser General Public
 
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 
16
 *
 
17
 * Authors:
 
18
 *       Matthias Clasen <mclasen@redhat.com>
 
19
 */
 
20
 
 
21
#define _GNU_SOURCE 1
 
22
 
 
23
#include "config.h"
 
24
 
 
25
#include <string.h>
 
26
 
 
27
#include <gtk/gtk.h>
 
28
#include <gtk/gtkunixprint.h>
 
29
 
 
30
#include <gio/gio.h>
 
31
#include <gio/gunixfdlist.h>
 
32
#include <gio/gunixinputstream.h>
 
33
#include <gio/gunixoutputstream.h>
 
34
 
 
35
#include "xdg-desktop-portal-dbus.h"
 
36
 
 
37
#include "print.h"
 
38
#include "request.h"
 
39
#include "utils.h"
 
40
#include "externalwindow.h"
 
41
 
 
42
#include "gtkbackports.h"
 
43
 
 
44
typedef struct {
 
45
  char *app_id;
 
46
  GtkPageSetup *page_setup;
 
47
  GtkPrintSettings *settings;
 
48
  GtkPrinter *printer;
 
49
  guint timeout_id;
 
50
  guint32 token;
 
51
} PrintParams;
 
52
 
 
53
static GHashTable *print_params;
 
54
 
 
55
static void
 
56
print_params_free (gpointer data)
 
57
{
 
58
  PrintParams *params = data;
 
59
 
 
60
  g_hash_table_remove (print_params, GUINT_TO_POINTER (params->token));
 
61
 
 
62
  g_free (params->app_id);
 
63
  g_object_unref (params->page_setup);
 
64
  g_object_unref (params->settings);
 
65
  g_object_unref (params->printer);
 
66
 
 
67
  g_free (params);
 
68
}
 
69
 
 
70
static void
 
71
ensure_print_params (void)
 
72
{
 
73
  if (print_params)
 
74
    return;
 
75
 
 
76
  print_params = g_hash_table_new (g_direct_hash, g_direct_equal);
 
77
}
 
78
 
 
79
static gboolean
 
80
print_params_timeout (gpointer data)
 
81
{
 
82
  print_params_free (data);
 
83
  g_debug ("Removing print params, now %d", g_hash_table_size (print_params));
 
84
  return G_SOURCE_REMOVE;
 
85
}
 
86
 
 
87
static PrintParams *
 
88
print_params_new (const char *app_id,
 
89
                  GtkPageSetup *page_setup,
 
90
                  GtkPrintSettings *settings,
 
91
                  GtkPrinter *printer)
 
92
{
 
93
  PrintParams *params;
 
94
  guint32 r;
 
95
 
 
96
  ensure_print_params ();
 
97
 
 
98
  params = g_new0 (PrintParams, 1);
 
99
  params->app_id = g_strdup (app_id);
 
100
  params->page_setup = g_object_ref (page_setup);
 
101
  params->settings = g_object_ref (settings);
 
102
  params->printer = g_object_ref (printer);
 
103
 
 
104
  do {
 
105
    r = g_random_int ();
 
106
  } while (r == 0 || g_hash_table_lookup (print_params, GUINT_TO_POINTER (r)) != NULL);
 
107
 
 
108
  params->token = r;
 
109
  g_hash_table_insert (print_params, GUINT_TO_POINTER (r), params);
 
110
 
 
111
  g_debug ("Remembering print params for %s, token %u, now %d",
 
112
           params->app_id, params->token,
 
113
           g_hash_table_size (print_params));
 
114
 
 
115
  params->timeout_id = g_timeout_add_seconds (300, print_params_timeout, params);
 
116
 
 
117
  return params;
 
118
}
 
119
 
 
120
static PrintParams *
 
121
get_print_params (const char *app_id,
 
122
                  guint32 token)
 
123
{
 
124
  PrintParams *params;
 
125
 
 
126
  params = (PrintParams *)g_hash_table_lookup (print_params, GUINT_TO_POINTER (token));
 
127
  if (params == NULL)
 
128
    return NULL;
 
129
 
 
130
  if (strcmp (params->app_id, app_id) != 0)
 
131
    return NULL;
 
132
 
 
133
  g_source_remove (params->timeout_id);
 
134
  g_hash_table_remove (print_params, GUINT_TO_POINTER (token));
 
135
 
 
136
  return params;
 
137
}
 
138
 
 
139
typedef struct {
 
140
  XdpImplPrint *impl;
 
141
  GDBusMethodInvocation *invocation;
 
142
  Request *request;
 
143
  GtkWidget *dialog;
 
144
  ExternalWindow *external_parent;
 
145
 
 
146
  int fd;
 
147
  int response;
 
148
 
 
149
  PrintParams *params;
 
150
 
 
151
} PrintDialogHandle;
 
152
 
 
153
static void
 
154
print_dialog_handle_free (gpointer data)
 
155
{
 
156
  PrintDialogHandle *handle = data;
 
157
 
 
158
  g_clear_object (&handle->external_parent);
 
159
  g_object_unref (handle->request);
 
160
  g_object_unref (handle->dialog);
 
161
  close (handle->fd);
 
162
 
 
163
  g_free (handle);
 
164
}
 
165
 
 
166
static void
 
167
print_dialog_handle_close (PrintDialogHandle *handle)
 
168
{
 
169
  gtk_widget_destroy (handle->dialog);
 
170
  print_dialog_handle_free (handle);
 
171
}
 
172
 
 
173
static gboolean
 
174
print_file (int fd,
 
175
            const char *app_id,
 
176
            GtkPrinter *printer,
 
177
            GtkPrintSettings *settings,
 
178
            GtkPageSetup *page_setup,
 
179
            GError **error)
 
180
{
 
181
  GtkPrintJob *job;
 
182
  g_autofree char *title = NULL;
 
183
#if !GTK_CHECK_VERSION (3, 22, 0)
 
184
  g_autofree char *filename = NULL;
 
185
  g_autoptr(GUnixInputStream) istream = NULL;
 
186
  g_autoptr(GUnixOutputStream) ostream = NULL;
 
187
  int fd2;
 
188
#endif
 
189
 
 
190
  title = g_strdup_printf ("Document from %s", app_id);
 
191
 
 
192
  job = gtk_print_job_new (title, printer, settings, page_setup);
 
193
 
 
194
#if GTK_CHECK_VERSION (3, 22, 0)
 
195
  if (!gtk_print_job_set_source_fd (job, fd, error))
 
196
    {
 
197
      g_object_unref (job);
 
198
      return FALSE;
 
199
    }
 
200
#else
 
201
  istream = g_unix_input_stream_new (fd, FALSE);
 
202
 
 
203
  if ((fd2 = g_file_open_tmp (PACKAGE_NAME "XXXXXX", &filename, error)) == -1)
 
204
    {
 
205
      g_object_unref (job);
 
206
      return FALSE;
 
207
    }
 
208
 
 
209
  ostream = g_unix_output_stream_new (fd2, TRUE);
 
210
 
 
211
  if (g_output_stream_splice (G_OUTPUT_STREAM (ostream),
 
212
                              G_INPUT_STREAM (istream),
 
213
                              G_OUTPUT_STREAM_SPLICE_NONE,
 
214
                              NULL,
 
215
                              error) == -1)
 
216
    {
 
217
      g_object_unref (job);
 
218
      return FALSE;
 
219
    }
 
220
 
 
221
  if (!gtk_print_job_set_source_file (job, filename, error))
 
222
    {
 
223
      g_object_unref (job);
 
224
      return FALSE;
 
225
    }
 
226
 
 
227
  /* The file will be removed when the GtkPrintJob closes it (once the job is
 
228
   * complete). */
 
229
  unlink (filename);
 
230
#endif
 
231
 
 
232
  gtk_print_job_send (job, NULL, NULL, NULL);
 
233
  g_object_unref (job);
 
234
 
 
235
  return TRUE;
 
236
}
 
237
 
 
238
static void
 
239
handle_print_response (GtkDialog *dialog,
 
240
                       gint response,
 
241
                       gpointer data)
 
242
{
 
243
  PrintDialogHandle *handle = data;
 
244
  g_autoptr(GError) error = NULL;
 
245
 
 
246
  switch (response)
 
247
    {
 
248
    default:
 
249
      g_warning ("Unexpected response: %d", response);
 
250
      /* Fall through */
 
251
    case GTK_RESPONSE_DELETE_EVENT:
 
252
      handle->response = 2;
 
253
      break;
 
254
 
 
255
    case GTK_RESPONSE_CANCEL:
 
256
      handle->response = 1;
 
257
      break;
 
258
 
 
259
    case GTK_RESPONSE_OK:
 
260
      {
 
261
        GtkPrinter *printer;
 
262
        GtkPrintSettings *settings;
 
263
        GtkPageSetup *page_setup;
 
264
 
 
265
        printer = gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (handle->dialog));
 
266
        settings = gtk_print_unix_dialog_get_settings (GTK_PRINT_UNIX_DIALOG (handle->dialog));
 
267
        page_setup = gtk_print_unix_dialog_get_page_setup (GTK_PRINT_UNIX_DIALOG (handle->dialog));
 
268
;
 
269
        if (!print_file (handle->fd,
 
270
                         handle->request->app_id,
 
271
                         printer,
 
272
                         settings,
 
273
                         page_setup,
 
274
                         &error))
 
275
          handle->response = 2;
 
276
        else
 
277
          handle->response = 0;
 
278
 
 
279
        g_object_unref (settings);
 
280
      }
 
281
      break;
 
282
    }
 
283
 
 
284
  if (handle->request->exported)
 
285
    request_unexport (handle->request);
 
286
 
 
287
  if (error)
 
288
    {
 
289
      g_dbus_method_invocation_take_error (handle->invocation, error);
 
290
    }
 
291
  else
 
292
    {
 
293
      GVariantBuilder opt_builder;
 
294
 
 
295
      g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
 
296
      xdp_impl_print_complete_print (handle->impl,
 
297
                                     handle->invocation,
 
298
                                     NULL,
 
299
                                     handle->response,
 
300
                                     g_variant_builder_end (&opt_builder));
 
301
    }
 
302
 
 
303
  print_dialog_handle_close (handle);
 
304
}
 
305
 
 
306
static gboolean
 
307
handle_close (XdpImplRequest *object,
 
308
              GDBusMethodInvocation *invocation,
 
309
              PrintDialogHandle *handle)
 
310
{
 
311
  GVariantBuilder opt_builder;
 
312
 
 
313
  g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
 
314
  xdp_impl_print_complete_print (handle->impl,
 
315
                                 handle->invocation,
 
316
                                 NULL,
 
317
                                 2,
 
318
                                 g_variant_builder_end (&opt_builder));
 
319
  print_dialog_handle_close (handle);
 
320
 
 
321
  if (handle->request->exported)
 
322
    request_unexport (handle->request);
 
323
 
 
324
  xdp_impl_request_complete_close (object, invocation);
 
325
 
 
326
  return TRUE;
 
327
}
 
328
 
 
329
static gboolean
 
330
handle_print (XdpImplPrint *object,
 
331
              GDBusMethodInvocation *invocation,
 
332
              GUnixFDList *fd_list,
 
333
              const char *arg_handle,
 
334
              const char *arg_app_id,
 
335
              const char *arg_parent_window,
 
336
              const char *arg_title,
 
337
              GVariant *arg_fd_in,
 
338
              GVariant *arg_options)
 
339
{
 
340
  g_autoptr(Request) request = NULL;
 
341
  const char *sender;
 
342
  GtkWidget *dialog;
 
343
  PrintDialogHandle *handle;
 
344
  guint32 token = 0;
 
345
  PrintParams *params;
 
346
  int idx, fd;
 
347
  gboolean modal;
 
348
  GdkDisplay *display;
 
349
  GdkScreen *screen;
 
350
  ExternalWindow *external_parent = NULL;
 
351
  GtkWidget *fake_parent;
 
352
 
 
353
  g_variant_get (arg_fd_in, "h", &idx);
 
354
  fd = g_unix_fd_list_get (fd_list, idx, NULL);
 
355
 
 
356
  g_variant_lookup (arg_options, "token", "u", &token);
 
357
  params = get_print_params (arg_app_id, token);
 
358
  if (params)
 
359
    {
 
360
      GVariantBuilder opt_builder;
 
361
 
 
362
      print_file (fd,
 
363
                  params->app_id,
 
364
                  params->printer,
 
365
                  params->settings,
 
366
                  params->page_setup,
 
367
                  NULL);
 
368
 
 
369
      print_params_free (params);
 
370
 
 
371
      g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
 
372
      xdp_impl_print_complete_print (object,
 
373
                                     invocation,
 
374
                                     NULL,
 
375
                                     0,
 
376
                                     g_variant_builder_end (&opt_builder));
 
377
      return TRUE;
 
378
    }
 
379
 
 
380
  sender = g_dbus_method_invocation_get_sender (invocation);
 
381
  request = request_new (sender, arg_app_id, arg_handle);
 
382
 
 
383
  if (arg_parent_window)
 
384
    {
 
385
      external_parent = create_external_window_from_handle (arg_parent_window);
 
386
      if (!external_parent)
 
387
        g_warning ("Failed to associate portal window with parent window %s",
 
388
                   arg_parent_window);
 
389
    }
 
390
 
 
391
  if (external_parent)
 
392
    display = external_window_get_display (external_parent);
 
393
  else
 
394
    display = gdk_display_get_default ();
 
395
  screen = gdk_display_get_default_screen (display);
 
396
 
 
397
  fake_parent = g_object_new (GTK_TYPE_WINDOW,
 
398
                              "type", GTK_WINDOW_TOPLEVEL,
 
399
                              "screen", screen,
 
400
                              NULL);
 
401
  g_object_ref_sink (fake_parent);
 
402
 
 
403
  if (!g_variant_lookup (arg_options, "modal", "b", &modal))
 
404
    modal = TRUE;
 
405
 
 
406
  dialog = gtk_print_unix_dialog_new (arg_title, NULL);
 
407
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fake_parent));
 
408
  gtk_window_set_modal (GTK_WINDOW (dialog), modal);
 
409
  gtk_print_unix_dialog_set_manual_capabilities (GTK_PRINT_UNIX_DIALOG (dialog), 0);
 
410
 
 
411
  handle = g_new0 (PrintDialogHandle, 1);
 
412
  handle->impl = object;
 
413
  handle->invocation = invocation;
 
414
  handle->request = g_object_ref (request);
 
415
  handle->dialog = g_object_ref (dialog);
 
416
  handle->external_parent = external_parent;
 
417
  handle->fd = fd;
 
418
 
 
419
  g_signal_connect (request, "handle-close", G_CALLBACK (handle_close), handle);
 
420
 
 
421
  g_signal_connect (dialog, "response", G_CALLBACK (handle_print_response), handle);
 
422
 
 
423
  gtk_widget_realize (dialog);
 
424
 
 
425
  if (external_parent)
 
426
    external_window_set_parent_of (external_parent, gtk_widget_get_window (dialog));
 
427
 
 
428
  request_export (request, g_dbus_method_invocation_get_connection (invocation));
 
429
 
 
430
  gtk_widget_show (dialog);
 
431
 
 
432
  return TRUE;
 
433
}
 
434
 
 
435
static void
 
436
send_prepare_print_response (PrintDialogHandle *handle)
 
437
{
 
438
  GVariantBuilder opt_builder;
 
439
 
 
440
  g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
 
441
 
 
442
  if (handle->response == 0)
 
443
    {
 
444
      g_variant_builder_add (&opt_builder, "{sv}", "settings", gtk_print_settings_to_gvariant (handle->params->settings));
 
445
      g_variant_builder_add (&opt_builder, "{sv}", "page-setup", gtk_page_setup_to_gvariant (handle->params->page_setup));
 
446
      g_variant_builder_add (&opt_builder, "{sv}", "token", g_variant_new_uint32 (handle->params->token));
 
447
    }
 
448
 
 
449
  if (handle->request->exported)
 
450
    request_unexport (handle->request);
 
451
 
 
452
  xdp_impl_print_complete_prepare_print (handle->impl,
 
453
                                         handle->invocation,
 
454
                                         handle->response,
 
455
                                         g_variant_builder_end (&opt_builder));
 
456
 
 
457
  print_dialog_handle_close (handle);
 
458
}
 
459
 
 
460
static void
 
461
handle_prepare_print_response (GtkDialog *dialog,
 
462
                               gint response,
 
463
                               gpointer data)
 
464
{
 
465
  PrintDialogHandle *handle = data;
 
466
 
 
467
  switch (response)
 
468
    {
 
469
    default:
 
470
      g_warning ("Unexpected response: %d", response);
 
471
      /* Fall through */
 
472
    case GTK_RESPONSE_DELETE_EVENT:
 
473
      handle->response = 2;
 
474
      break;
 
475
 
 
476
    case GTK_RESPONSE_CANCEL:
 
477
      handle->response = 1;
 
478
      break;
 
479
 
 
480
    case GTK_RESPONSE_OK:
 
481
      {
 
482
        handle->response = 0;
 
483
 
 
484
        handle->params = print_params_new (handle->request->app_id,
 
485
                                           gtk_print_unix_dialog_get_page_setup (GTK_PRINT_UNIX_DIALOG (handle->dialog)),
 
486
                                           gtk_print_unix_dialog_get_settings (GTK_PRINT_UNIX_DIALOG (handle->dialog)),
 
487
                                           gtk_print_unix_dialog_get_selected_printer (GTK_PRINT_UNIX_DIALOG (handle->dialog)));
 
488
      }
 
489
      break;
 
490
    }
 
491
 
 
492
  send_prepare_print_response (handle);
 
493
}
 
494
 
 
495
static gboolean
 
496
handle_prepare_print (XdpImplPrint *object,
 
497
                      GDBusMethodInvocation *invocation,
 
498
                      const char *arg_handle,
 
499
                      const char *arg_app_id,
 
500
                      const char *arg_parent_window,
 
501
                      const char *arg_title,
 
502
                      GVariant *arg_settings,
 
503
                      GVariant *arg_page_setup,
 
504
                      GVariant *arg_options)
 
505
{
 
506
  g_autoptr(Request) request = NULL;
 
507
  const char *sender;
 
508
  GtkWidget *dialog;
 
509
  PrintDialogHandle *handle;
 
510
  GtkPrintSettings *settings;
 
511
  GtkPageSetup *page_setup;
 
512
  gboolean modal;
 
513
  GdkDisplay *display;
 
514
  GdkScreen *screen;
 
515
  ExternalWindow *external_parent = NULL;
 
516
  GtkWidget *fake_parent;
 
517
 
 
518
  sender = g_dbus_method_invocation_get_sender (invocation);
 
519
 
 
520
  request = request_new (sender, arg_app_id, arg_handle);
 
521
 
 
522
  if (arg_parent_window)
 
523
    {
 
524
      external_parent = create_external_window_from_handle (arg_parent_window);
 
525
      if (!external_parent)
 
526
        g_warning ("Failed to associate portal window with parent window %s",
 
527
                   arg_parent_window);
 
528
    }
 
529
 
 
530
  if (external_parent)
 
531
    display = external_window_get_display (external_parent);
 
532
  else
 
533
    display = gdk_display_get_default ();
 
534
  screen = gdk_display_get_default_screen (display);
 
535
 
 
536
  fake_parent = g_object_new (GTK_TYPE_WINDOW,
 
537
                              "type", GTK_WINDOW_TOPLEVEL,
 
538
                              "screen", screen,
 
539
                              NULL);
 
540
  g_object_ref_sink (fake_parent);
 
541
 
 
542
  settings = gtk_print_settings_new_from_gvariant (arg_settings);
 
543
  page_setup = gtk_page_setup_new_from_gvariant (arg_page_setup);
 
544
  if (!g_variant_lookup (arg_options, "modal", "b", &modal))
 
545
    modal = TRUE;
 
546
 
 
547
  dialog = gtk_print_unix_dialog_new (arg_title, NULL);
 
548
  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fake_parent));
 
549
  gtk_window_set_modal (GTK_WINDOW (dialog), modal);
 
550
  gtk_print_unix_dialog_set_manual_capabilities (GTK_PRINT_UNIX_DIALOG (dialog), 0);
 
551
  gtk_print_unix_dialog_set_embed_page_setup (GTK_PRINT_UNIX_DIALOG (dialog), TRUE);
 
552
  gtk_print_unix_dialog_set_settings (GTK_PRINT_UNIX_DIALOG (dialog), settings);
 
553
  gtk_print_unix_dialog_set_page_setup (GTK_PRINT_UNIX_DIALOG (dialog), page_setup);
 
554
 
 
555
  g_object_unref (settings);
 
556
  g_object_unref (page_setup);
 
557
 
 
558
  handle = g_new0 (PrintDialogHandle, 1);
 
559
  handle->impl = object;
 
560
  handle->invocation = invocation;
 
561
  handle->request = g_object_ref (request);
 
562
  handle->dialog = g_object_ref (dialog);
 
563
  handle->external_parent = external_parent;
 
564
 
 
565
  g_signal_connect (request, "handle-close", G_CALLBACK (handle_close), handle);
 
566
 
 
567
  g_signal_connect (dialog, "response", G_CALLBACK (handle_prepare_print_response), handle);
 
568
 
 
569
  gtk_widget_realize (dialog);
 
570
 
 
571
  if (external_parent)
 
572
    external_window_set_parent_of (external_parent, gtk_widget_get_window (dialog));
 
573
 
 
574
  gtk_widget_show (dialog);
 
575
 
 
576
  request_export (request, g_dbus_method_invocation_get_connection (invocation));
 
577
 
 
578
  return TRUE;
 
579
}
 
580
 
 
581
gboolean
 
582
print_init (GDBusConnection *bus,
 
583
            GError **error)
 
584
{
 
585
  GDBusInterfaceSkeleton *helper;
 
586
 
 
587
  helper = G_DBUS_INTERFACE_SKELETON (xdp_impl_print_skeleton_new ());
 
588
 
 
589
  g_signal_connect (helper, "handle-print", G_CALLBACK (handle_print), NULL);
 
590
  g_signal_connect (helper, "handle-prepare-print", G_CALLBACK (handle_prepare_print), NULL);
 
591
 
 
592
  if (!g_dbus_interface_skeleton_export (helper,
 
593
                                         bus,
 
594
                                         DESKTOP_PORTAL_OBJECT_PATH,
 
595
                                         error))
 
596
    return FALSE;
 
597
 
 
598
  g_debug ("providing %s", g_dbus_interface_skeleton_get_info (helper)->name);
 
599
 
 
600
  return TRUE;
 
601
}