1 /* gnome-screenshot.c - Take a screenshot of the desktop
2  *
3  * Copyright (C) 2001 Jonathan Blandford <jrb@alum.mit.edu>
4  * Copyright (C) 2006 Emmanuele Bassi <ebassi@gnome.org>
5  * Copyright (C) 2008-2012 Cosimo Cecchi <cosimoc@gnome.org>
6  * Copyright (C) 2020 Philipp Wolfer <ph.wolfer@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21  * USA
22  */
23 
24 #include "config.h"
25 
26 #include <gdk/gdkkeysyms.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <locale.h>
31 #include <glib/gi18n.h>
32 #include <gio/gio.h>
33 
34 #include <handy.h>
35 
36 #include "screenshot-application.h"
37 #include "screenshot-area-selection.h"
38 #include "screenshot-config.h"
39 #include "screenshot-filename-builder.h"
40 #include "screenshot-interactive-dialog.h"
41 #include "screenshot-utils.h"
42 #include "screenshot-dialog.h"
43 
44 #define LAST_SAVE_DIRECTORY_KEY "last-save-directory"
45 
46 static void screenshot_save_to_file (ScreenshotApplication *self);
47 static void screenshot_show_interactive_dialog (ScreenshotApplication *self);
48 
49 struct _ScreenshotApplication
50 {
51   GtkApplication parent_instance;
52 
53   gchar *icc_profile_base64;
54   GdkPixbuf *screenshot;
55 
56   gchar *save_uri;
57   gboolean should_overwrite;
58 
59   GdkRectangle *rectangle;
60 
61   ScreenshotDialog *dialog;
62 };
63 
G_DEFINE_TYPE(ScreenshotApplication,screenshot_application,GTK_TYPE_APPLICATION)64 G_DEFINE_TYPE (ScreenshotApplication, screenshot_application, GTK_TYPE_APPLICATION)
65 
66 static void
67 save_folder_to_settings (ScreenshotApplication *self)
68 {
69   g_autofree gchar *folder = screenshot_dialog_get_folder (self->dialog);
70   g_settings_set_string (screenshot_config->settings,
71                          LAST_SAVE_DIRECTORY_KEY, folder);
72 }
73 
74 static void
set_recent_entry(ScreenshotApplication * self)75 set_recent_entry (ScreenshotApplication *self)
76 {
77   g_autofree gchar *app_exec = NULL;
78   g_autoptr(GAppInfo) app = NULL;
79   GtkRecentManager *recent;
80   GtkRecentData recent_data;
81   const char *exec_name = NULL;
82   static char * groups[2] = { "Graphics", NULL };
83 
84   app = g_app_info_get_default_for_type ("image/png", TRUE);
85 
86   if (!app) {
87     /* return early, as this would be an useless recent entry anyway. */
88     return;
89   }
90 
91   recent = gtk_recent_manager_get_default ();
92 
93   exec_name = g_app_info_get_executable (app);
94   app_exec = g_strjoin (" ", exec_name, "%u", NULL);
95 
96   recent_data.display_name = NULL;
97   recent_data.description = NULL;
98   recent_data.mime_type = "image/png";
99   recent_data.app_name = "GNOME Screenshot";
100   recent_data.app_exec = app_exec;
101   recent_data.groups = groups;
102   recent_data.is_private = FALSE;
103 
104   gtk_recent_manager_add_full (recent, self->save_uri, &recent_data);
105 }
106 
107 static void
screenshot_close_interactive_dialog(ScreenshotApplication * self)108 screenshot_close_interactive_dialog (ScreenshotApplication *self)
109 {
110   ScreenshotDialog *dialog = self->dialog;
111   save_folder_to_settings (self);
112   gtk_widget_destroy (GTK_WIDGET (dialog));
113 }
114 
115 static void
save_pixbuf_handle_success(ScreenshotApplication * self)116 save_pixbuf_handle_success (ScreenshotApplication *self)
117 {
118   set_recent_entry (self);
119 
120   if (screenshot_config->interactive)
121     {
122       screenshot_close_interactive_dialog (self);
123     }
124   else
125     {
126       g_application_release (G_APPLICATION (self));
127     }
128 }
129 
130 static void
screenshot_dialog_focus_cb(gint response,ScreenshotApplication * self)131 screenshot_dialog_focus_cb (gint                   response,
132                             ScreenshotApplication *self)
133 {
134   gtk_widget_grab_focus (screenshot_dialog_get_filename_entry (self->dialog));
135 }
136 
137 static void
screenshot_dialog_override_cb(gint response,ScreenshotApplication * self)138 screenshot_dialog_override_cb (gint                   response,
139                                ScreenshotApplication *self)
140 {
141   if (response == GTK_RESPONSE_YES)
142     {
143       self->should_overwrite = TRUE;
144       screenshot_save_to_file (self);
145 
146       return;
147     }
148 
149   screenshot_dialog_focus_cb (response, self);
150 }
151 
152 static void
save_pixbuf_handle_error(ScreenshotApplication * self,GError * error)153 save_pixbuf_handle_error (ScreenshotApplication *self,
154                           GError *error)
155 {
156   if (screenshot_config->interactive)
157     {
158       ScreenshotDialog *dialog = self->dialog;
159 
160       screenshot_dialog_set_busy (dialog, FALSE);
161 
162       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS) &&
163           !self->should_overwrite)
164         {
165           g_autofree gchar *folder = screenshot_dialog_get_folder (dialog);
166           g_autofree gchar *folder_uri = g_path_get_basename (folder);
167           g_autofree gchar *folder_name = g_uri_unescape_string (folder_uri, NULL);
168           g_autofree gchar *file_name = screenshot_dialog_get_filename (dialog);
169           g_autofree gchar *detail = g_strdup_printf (_("A file named “%s” already exists in “%s”"),
170                                                       file_name, folder_name);
171 
172           screenshot_show_dialog (GTK_WINDOW (dialog),
173                                   GTK_MESSAGE_WARNING,
174                                   GTK_BUTTONS_YES_NO,
175                                   _("Overwrite existing file?"),
176                                   detail,
177                                   (ScreenshotResponseFunc) screenshot_dialog_override_cb,
178                                   self);
179         }
180       else
181         {
182           screenshot_show_dialog (GTK_WINDOW (dialog),
183                                   GTK_MESSAGE_ERROR,
184                                   GTK_BUTTONS_OK,
185                                   _("Unable to capture a screenshot"),
186                                   _("Error creating file. Please choose another location and retry."),
187                                   (ScreenshotResponseFunc) screenshot_dialog_focus_cb,
188                                   dialog);
189         }
190     }
191   else
192     {
193       g_critical ("Unable to save the screenshot: %s", error->message);
194       g_application_release (G_APPLICATION (self));
195       if (screenshot_config->file != NULL)
196         exit (EXIT_FAILURE);
197     }
198 }
199 
200 static void
save_pixbuf_ready_cb(GObject * source,GAsyncResult * res,gpointer user_data)201 save_pixbuf_ready_cb (GObject *source,
202                       GAsyncResult *res,
203                       gpointer user_data)
204 {
205   g_autoptr(GError) error = NULL;
206   ScreenshotApplication *self = user_data;
207 
208   gdk_pixbuf_save_to_stream_finish (res, &error);
209 
210   if (error != NULL)
211     {
212       save_pixbuf_handle_error (self, error);
213       return;
214     }
215 
216   save_pixbuf_handle_success (self);
217 }
218 
219 static void
find_out_writable_format_by_extension(gpointer data,gpointer user_data)220 find_out_writable_format_by_extension (gpointer data,
221                                        gpointer user_data)
222 {
223   GdkPixbufFormat *format     = (GdkPixbufFormat*) data;
224   gchar          **name       = (gchar **) user_data;
225   g_auto(GStrv)    extensions = gdk_pixbuf_format_get_extensions (format);
226   gchar          **ptr        = extensions;
227 
228   while (*ptr != NULL)
229     {
230       if (g_strcmp0 (*ptr, *name) == 0 &&
231           gdk_pixbuf_format_is_writable (format) == TRUE)
232         {
233           g_free (*name);
234           *name = gdk_pixbuf_format_get_name (format);
235           break;
236         }
237       ptr++;
238     }
239 }
240 
241 static gboolean
is_png(gchar * format)242 is_png (gchar *format)
243 {
244   if (g_strcmp0 (format, "png") == 0)
245     return TRUE;
246   else
247     return FALSE;
248 }
249 
250 static gboolean
has_profile(ScreenshotApplication * self)251 has_profile (ScreenshotApplication *self)
252 {
253   return (self->icc_profile_base64 != NULL);
254 }
255 
256 static void
save_with_description_and_profile(ScreenshotApplication * self,GFileOutputStream * os,gchar * format)257 save_with_description_and_profile (ScreenshotApplication *self,
258                                    GFileOutputStream     *os,
259                                    gchar                 *format)
260 {
261   gdk_pixbuf_save_to_stream_async (self->screenshot,
262                                    G_OUTPUT_STREAM (os),
263                                    format, NULL,
264                                    save_pixbuf_ready_cb, self,
265                                    "icc-profile", self->icc_profile_base64,
266                                    "tEXt::Software", "gnome-screenshot",
267                                    NULL);
268 }
269 static void
save_with_description(ScreenshotApplication * self,GFileOutputStream * os,gchar * format)270 save_with_description (ScreenshotApplication *self,
271                        GFileOutputStream     *os,
272                        gchar                 *format)
273 {
274   gdk_pixbuf_save_to_stream_async (self->screenshot,
275                                    G_OUTPUT_STREAM (os),
276                                    format, NULL,
277                                    save_pixbuf_ready_cb, self,
278                                    "tEXt::Software", "gnome-screenshot",
279                                    NULL);
280 }
281 
282 static void
save_with_no_profile_or_description(ScreenshotApplication * self,GFileOutputStream * os,gchar * format)283 save_with_no_profile_or_description (ScreenshotApplication *self,
284                                      GFileOutputStream     *os,
285                                      gchar                 *format)
286 {
287   gdk_pixbuf_save_to_stream_async (self->screenshot,
288                                    G_OUTPUT_STREAM (os),
289                                    format, NULL,
290                                    save_pixbuf_ready_cb, self,
291                                    NULL);
292 }
293 
294 static void
save_file_create_ready_cb(GObject * source,GAsyncResult * res,gpointer user_data)295 save_file_create_ready_cb (GObject *source,
296                            GAsyncResult *res,
297                            gpointer user_data)
298 {
299   ScreenshotApplication *self = user_data;
300   g_autoptr(GFileOutputStream) os = NULL;
301   g_autoptr(GError) error = NULL;
302   g_autofree gchar *basename = g_file_get_basename (G_FILE (source));
303   g_autofree gchar *format = NULL;
304   gchar *extension = g_strrstr (basename, ".");
305   GSList *formats = NULL;
306 
307   if (extension == NULL)
308     extension = "png";
309   else
310     extension++;
311 
312   format = g_strdup (extension);
313 
314   formats = gdk_pixbuf_get_formats();
315   g_slist_foreach (formats,
316                    find_out_writable_format_by_extension,
317                    (gpointer) &format);
318   g_slist_free (formats);
319 
320   if (self->should_overwrite)
321     os = g_file_replace_finish (G_FILE (source), res, &error);
322   else
323     os = g_file_create_finish (G_FILE (source), res, &error);
324 
325   if (error != NULL)
326     {
327       save_pixbuf_handle_error (self, error);
328       return;
329     }
330 
331   if (is_png (format))
332     {
333       if (has_profile (self))
334         save_with_description_and_profile (self, os, format);
335       else
336         save_with_description (self, os, format);
337     }
338   else
339     {
340       save_with_no_profile_or_description (self, os, format);
341     }
342 }
343 
344 static void
screenshot_save_to_file(ScreenshotApplication * self)345 screenshot_save_to_file (ScreenshotApplication *self)
346 {
347   g_autoptr(GFile) target_file = NULL;
348 
349   if (self->dialog != NULL)
350     screenshot_dialog_set_busy (self->dialog, TRUE);
351 
352   target_file = g_file_new_for_uri (self->save_uri);
353 
354   if (self->should_overwrite)
355     {
356       g_file_replace_async (target_file,
357                             NULL, FALSE,
358                             G_FILE_CREATE_NONE,
359                             G_PRIORITY_DEFAULT,
360                             NULL,
361                             save_file_create_ready_cb, self);
362     }
363   else
364     {
365       g_file_create_async (target_file,
366                            G_FILE_CREATE_NONE,
367                            G_PRIORITY_DEFAULT,
368                            NULL,
369                            save_file_create_ready_cb, self);
370     }
371 }
372 
373 static void
screenshot_back(ScreenshotApplication * self)374 screenshot_back (ScreenshotApplication *self)
375 {
376   screenshot_close_interactive_dialog (self);
377   screenshot_show_interactive_dialog (self);
378 }
379 
380 static void
screenshot_save_to_clipboard(ScreenshotApplication * self)381 screenshot_save_to_clipboard (ScreenshotApplication *self)
382 {
383   GtkClipboard *clipboard;
384 
385   clipboard = gtk_clipboard_get_for_display (gdk_display_get_default (),
386                                              GDK_SELECTION_CLIPBOARD);
387   gtk_clipboard_set_image (clipboard, self->screenshot);
388 }
389 
390 static void
save_clicked_cb(ScreenshotDialog * dialog,ScreenshotApplication * self)391 save_clicked_cb (ScreenshotDialog      *dialog,
392                  ScreenshotApplication *self)
393 {
394   /* update to the new URI */
395   g_free (self->save_uri);
396   self->save_uri = screenshot_dialog_get_uri (self->dialog);
397   screenshot_save_to_file (self);
398 }
399 
400 static void
copy_clicked_cb(ScreenshotDialog * dialog,ScreenshotApplication * self)401 copy_clicked_cb (ScreenshotDialog      *dialog,
402                  ScreenshotApplication *self)
403 {
404   screenshot_save_to_clipboard (self);
405 }
406 
407 static void
back_clicked_cb(ScreenshotDialog * dialog,ScreenshotApplication * self)408 back_clicked_cb (ScreenshotDialog      *dialog,
409                  ScreenshotApplication *self)
410 {
411   screenshot_back (self);
412 }
413 
414 static void
build_filename_ready_cb(GObject * source,GAsyncResult * res,gpointer user_data)415 build_filename_ready_cb (GObject *source,
416                          GAsyncResult *res,
417                          gpointer user_data)
418 {
419   ScreenshotApplication *self = user_data;
420   g_autoptr(GError) error = NULL;
421   g_autofree gchar *save_path = screenshot_build_filename_finish (res, &error);
422 
423   if (save_path != NULL)
424     {
425       g_autoptr(GFile) file = g_file_new_for_path (save_path);
426       self->save_uri = g_file_get_uri (file);
427     }
428   else
429     self->save_uri = NULL;
430 
431   /* now release the application */
432   g_application_release (G_APPLICATION (self));
433 
434   if (error != NULL)
435     {
436       g_critical ("Impossible to find a valid location to save the screenshot: %s",
437                   error->message);
438 
439       if (screenshot_config->interactive)
440         screenshot_show_dialog (NULL,
441                                 GTK_MESSAGE_ERROR,
442                                 GTK_BUTTONS_OK,
443                                 _("Unable to capture a screenshot"),
444                                 _("Error creating file"),
445                                 NULL,
446                                 NULL);
447       else
448         {
449           if (screenshot_config->file != NULL)
450             exit (EXIT_FAILURE);
451         }
452 
453       return;
454     }
455 
456   if (screenshot_config->interactive)
457     {
458       self->dialog = screenshot_dialog_new (GTK_APPLICATION (self),
459                                             self->screenshot,
460                                             self->save_uri);
461       g_signal_connect_object (self->dialog, "save", G_CALLBACK (save_clicked_cb), self, 0);
462       g_signal_connect_object (self->dialog, "copy", G_CALLBACK (copy_clicked_cb), self, 0);
463       g_signal_connect_object (self->dialog, "back", G_CALLBACK (back_clicked_cb), self, 0);
464     }
465   else
466     {
467       g_application_hold (G_APPLICATION (self));
468       screenshot_save_to_file (self);
469     }
470 }
471 
472 static void
screenshot_release_cb(gint response,ScreenshotApplication * self)473 screenshot_release_cb (gint                   response,
474                        ScreenshotApplication *self)
475 {
476   g_application_release (G_APPLICATION (self));
477   if (screenshot_config->file != NULL)
478     exit (EXIT_FAILURE);
479 }
480 
481 static void
finish_take_screenshot(ScreenshotApplication * self)482 finish_take_screenshot (ScreenshotApplication *self)
483 {
484   GdkPixbuf *screenshot;
485 
486   screenshot = screenshot_get_pixbuf (self->rectangle);
487   g_clear_pointer (&self->rectangle, g_free);
488 
489   if (screenshot == NULL)
490     {
491       g_critical ("Unable to capture a screenshot of any window");
492 
493       if (screenshot_config->interactive)
494         screenshot_show_dialog (NULL,
495                                 GTK_MESSAGE_ERROR,
496                                 GTK_BUTTONS_OK,
497                                 _("Unable to capture a screenshot"),
498                                 _("All possible methods failed"),
499                                 (ScreenshotResponseFunc) screenshot_release_cb,
500                                 self);
501 
502       return;
503     }
504 
505   self->screenshot = screenshot;
506 
507   if (screenshot_config->copy_to_clipboard)
508     {
509       screenshot_save_to_clipboard (self);
510       if (screenshot_config->file == NULL)
511         {
512           g_application_release (G_APPLICATION (self));
513 
514           return;
515         }
516     }
517 
518   /* FIXME: apply the ICC profile according to the preferences.
519    * org.gnome.ColorManager.GetProfileForWindow() does not exist anymore,
520    * so we probably need to fetch the color profile of the screen where
521    * the area/window was.
522    *
523    * screenshot_ensure_icc_profile (window);
524    */
525   if (screenshot_config->file != NULL)
526     {
527       self->save_uri = g_file_get_uri (screenshot_config->file);
528       self->should_overwrite = TRUE;
529       screenshot_save_to_file (self);
530     }
531   else
532     screenshot_build_filename_async (screenshot_config->save_dir, NULL, build_filename_ready_cb, self);
533 }
534 
535 static gboolean
take_screenshot_timeout(gpointer user_data)536 take_screenshot_timeout (gpointer user_data)
537 {
538   ScreenshotApplication *self = user_data;
539   finish_take_screenshot (self);
540 
541   return FALSE;
542 }
543 
544 static void
start_screenshot_timeout(ScreenshotApplication * self)545 start_screenshot_timeout (ScreenshotApplication *self)
546 {
547   guint delay = screenshot_config->delay * 1000;
548 
549   if (!screenshot_config->take_area_shot)
550     /* hold the GApplication while doing the async screenshot op */
551     g_application_hold (G_APPLICATION (self));
552 
553   /* HACK: give time to the dialog to actually disappear.
554    * We don't have any way to tell when the compositor has finished
555    * re-drawing.
556    */
557   if (delay == 0 && screenshot_config->interactive)
558     delay = 200;
559 
560   if (delay > 0)
561     g_timeout_add (delay,
562                    take_screenshot_timeout,
563                    self);
564   else
565     g_idle_add (take_screenshot_timeout, self);
566 }
567 
568 static void
rectangle_found_cb(GdkRectangle * rectangle,gpointer user_data)569 rectangle_found_cb (GdkRectangle *rectangle,
570                     gpointer user_data)
571 {
572   ScreenshotApplication *self = user_data;
573 
574   if (rectangle != NULL)
575     {
576       self->rectangle = g_memdup (rectangle, sizeof *rectangle);
577       start_screenshot_timeout (self);
578     }
579   else
580     {
581       /* user dismissed the area selection, possibly show the dialog again */
582       g_application_release (G_APPLICATION (self));
583 
584       if (screenshot_config->interactive)
585         screenshot_show_interactive_dialog (self);
586     }
587 }
588 
589 static void
screenshot_start(ScreenshotApplication * self)590 screenshot_start (ScreenshotApplication *self)
591 {
592   if (screenshot_config->take_area_shot)
593     {
594       /* hold the GApplication while selecting the screen area */
595       g_application_hold (G_APPLICATION (self));
596       screenshot_select_area_async (rectangle_found_cb, self);
597     }
598   else
599     {
600       start_screenshot_timeout (self);
601     }
602 
603   screenshot_save_config ();
604 }
605 
606 static gboolean version_arg = FALSE;
607 
608 static const GOptionEntry entries[] = {
609   { "clipboard", 'c', 0, G_OPTION_ARG_NONE, NULL, N_("Send the grab directly to the clipboard"), NULL },
610   { "window", 'w', 0, G_OPTION_ARG_NONE, NULL, N_("Grab a window instead of the entire screen"), NULL },
611   { "area", 'a', 0, G_OPTION_ARG_NONE, NULL, N_("Grab an area of the screen instead of the entire screen"), NULL },
612   { "include-border", 'b', 0, G_OPTION_ARG_NONE, NULL, N_("Include the window border with the screenshot. This option is deprecated and window border is always included"), NULL },
613   { "remove-border", 'B', 0, G_OPTION_ARG_NONE, NULL, N_("Remove the window border from the screenshot. This option is deprecated and window border is always included"), NULL },
614   { "include-pointer", 'p', 0, G_OPTION_ARG_NONE, NULL, N_("Include the pointer with the screenshot"), NULL },
615   { "delay", 'd', 0, G_OPTION_ARG_INT, NULL, N_("Take screenshot after specified delay [in seconds]"), N_("seconds") },
616   { "border-effect", 'e', 0, G_OPTION_ARG_STRING, NULL, N_("Effect to add to the border (‘shadow’, ‘border’, ‘vintage’ or ‘none’). Note: This option is deprecated and is assumed to be ‘none’"), N_("effect") },
617   { "interactive", 'i', 0, G_OPTION_ARG_NONE, NULL, N_("Interactively set options"), NULL },
618   { "file", 'f', 0, G_OPTION_ARG_FILENAME, NULL, N_("Save screenshot directly to this file"), N_("filename") },
619   { "version", 0, 0, G_OPTION_ARG_NONE, &version_arg, N_("Print version information and exit"), NULL },
620   { NULL },
621 };
622 
623 static gint
screenshot_application_handle_local_options(GApplication * app,GVariantDict * options)624 screenshot_application_handle_local_options (GApplication *app,
625                                              GVariantDict *options)
626 {
627   if (version_arg)
628     {
629       g_print ("%s %s\n", g_get_application_name (), VERSION);
630       exit (EXIT_SUCCESS);
631     }
632 
633   return -1;
634 }
635 
636 static gint
screenshot_application_command_line(GApplication * app,GApplicationCommandLine * command_line)637 screenshot_application_command_line (GApplication            *app,
638                                      GApplicationCommandLine *command_line)
639 {
640   ScreenshotApplication *self = SCREENSHOT_APPLICATION (app);
641   gboolean clipboard_arg = FALSE;
642   gboolean window_arg = FALSE;
643   gboolean area_arg = FALSE;
644   gboolean include_border_arg = FALSE;
645   gboolean disable_border_arg = FALSE;
646   gboolean include_pointer_arg = FALSE;
647   gboolean interactive_arg = FALSE;
648   gchar *border_effect_arg = NULL;
649   guint delay_arg = 0;
650   gchar *file_arg = NULL;
651   GVariantDict *options;
652   gint exit_status = EXIT_SUCCESS;
653   gboolean res;
654 
655   options = g_application_command_line_get_options_dict (command_line);
656   g_variant_dict_lookup (options, "clipboard", "b", &clipboard_arg);
657   g_variant_dict_lookup (options, "window", "b", &window_arg);
658   g_variant_dict_lookup (options, "area", "b", &area_arg);
659   g_variant_dict_lookup (options, "include-border", "b", &include_border_arg);
660   g_variant_dict_lookup (options, "remove-border", "b", &disable_border_arg);
661   g_variant_dict_lookup (options, "include-pointer", "b", &include_pointer_arg);
662   g_variant_dict_lookup (options, "interactive", "b", &interactive_arg);
663   g_variant_dict_lookup (options, "border-effect", "&s", &border_effect_arg);
664   g_variant_dict_lookup (options, "delay", "i", &delay_arg);
665   g_variant_dict_lookup (options, "file", "^&ay", &file_arg);
666 
667   res = screenshot_config_parse_command_line (clipboard_arg,
668                                               window_arg,
669                                               area_arg,
670                                               include_border_arg,
671                                               disable_border_arg,
672                                               include_pointer_arg,
673                                               border_effect_arg,
674                                               delay_arg,
675                                               interactive_arg,
676                                               file_arg);
677   if (!res)
678     {
679       exit_status = EXIT_FAILURE;
680       goto out;
681     }
682 
683   /* interactive mode: trigger the dialog and wait for the response */
684   if (screenshot_config->interactive)
685     g_application_activate (app);
686   else
687     screenshot_start (self);
688 
689  out:
690   return exit_status;
691 }
692 
693 static void
capture_clicked_cb(ScreenshotInteractiveDialog * dialog,ScreenshotApplication * self)694 capture_clicked_cb (ScreenshotInteractiveDialog *dialog,
695                     ScreenshotApplication       *self)
696 {
697   gtk_widget_destroy (GTK_WIDGET (dialog));
698   screenshot_start (self);
699 }
700 
701 static void
screenshot_show_interactive_dialog(ScreenshotApplication * self)702 screenshot_show_interactive_dialog (ScreenshotApplication *self)
703 {
704   ScreenshotInteractiveDialog *dialog;
705 
706   dialog = screenshot_interactive_dialog_new (GTK_APPLICATION (self));
707 
708   g_signal_connect_object (dialog, "capture", G_CALLBACK (capture_clicked_cb), self, 0);
709 
710   gtk_widget_show (GTK_WIDGET (dialog));
711 }
712 
713 static void
action_quit(GSimpleAction * action,GVariant * parameter,gpointer user_data)714 action_quit (GSimpleAction *action,
715              GVariant *parameter,
716              gpointer user_data)
717 {
718   GList *windows = gtk_application_get_windows (GTK_APPLICATION (user_data));
719   gtk_widget_destroy (g_list_nth_data (windows, 0));
720 }
721 
722 static void
action_help(GSimpleAction * action,GVariant * parameter,gpointer user_data)723 action_help (GSimpleAction *action,
724              GVariant *parameter,
725              gpointer user_data)
726 {
727   GList *windows = gtk_application_get_windows (GTK_APPLICATION (user_data));
728   screenshot_display_help (g_list_nth_data (windows, 0));
729 }
730 
731 static void
action_about(GSimpleAction * action,GVariant * parameter,gpointer user_data)732 action_about (GSimpleAction *action,
733               GVariant *parameter,
734               gpointer user_data)
735 {
736   const gchar *authors[] = {
737     "Emmanuele Bassi",
738     "Jonathan Blandford",
739     "Cosimo Cecchi",
740     "Alexander Mikhaylenko",
741     NULL
742   };
743 
744   const gchar *artists[] = {
745     "Tobias Bernard",
746     "Jakub Steiner",
747     NULL
748   };
749 
750   GList *windows = gtk_application_get_windows (GTK_APPLICATION (user_data));
751   gtk_show_about_dialog (GTK_WINDOW (g_list_nth_data (windows, 0)),
752                          "version", VERSION,
753                          "authors", authors,
754                          "artists", artists,
755                          "program-name", _("Screenshot"),
756                          "comments", _("Save images of your screen or individual windows"),
757                          "logo-icon-name", SCREENSHOT_ICON_NAME,
758                          "translator-credits", _("translator-credits"),
759                          "license-type", GTK_LICENSE_GPL_2_0,
760                          "wrap-license", TRUE,
761                          NULL);
762 }
763 
764 static void
action_screen_shot(GSimpleAction * action,GVariant * parameter,gpointer user_data)765 action_screen_shot (GSimpleAction *action,
766                     GVariant *parameter,
767                     gpointer user_data)
768 {
769   ScreenshotApplication *self = SCREENSHOT_APPLICATION (user_data);
770 
771   screenshot_config_parse_command_line (FALSE, /* clipboard */
772                                         FALSE,  /* window */
773                                         FALSE, /* area */
774                                         FALSE, /* include border */
775                                         FALSE, /* disable border */
776                                         FALSE, /* include pointer */
777                                         NULL,  /* border effect */
778                                         0,     /* delay */
779                                         FALSE, /* interactive */
780                                         NULL); /* file */
781   screenshot_start (self);
782 }
783 
784 static void
action_window_shot(GSimpleAction * action,GVariant * parameter,gpointer user_data)785 action_window_shot (GSimpleAction *action,
786                     GVariant *parameter,
787                     gpointer user_data)
788 {
789   ScreenshotApplication *self = SCREENSHOT_APPLICATION (user_data);
790 
791   screenshot_config_parse_command_line (FALSE, /* clipboard */
792                                         TRUE,  /* window */
793                                         FALSE, /* area */
794                                         FALSE, /* include border */
795                                         FALSE, /* disable border */
796                                         FALSE, /* include pointer */
797                                         NULL,  /* border effect */
798                                         0,     /* delay */
799                                         FALSE, /* interactive */
800                                         NULL); /* file */
801   screenshot_start (self);
802 }
803 
804 static GActionEntry action_entries[] = {
805   { "about", action_about, NULL, NULL, NULL },
806   { "help", action_help, NULL, NULL, NULL },
807   { "quit", action_quit, NULL, NULL, NULL },
808   { "screen-shot", action_screen_shot, NULL, NULL, NULL },
809   { "window-shot", action_window_shot, NULL, NULL, NULL }
810 };
811 
812 static void
screenshot_application_startup(GApplication * app)813 screenshot_application_startup (GApplication *app)
814 {
815   const gchar *help_accels[2] = { "F1", NULL };
816   const gchar *quit_accels[2] = { "<Primary>q", NULL };
817   ScreenshotApplication *self = SCREENSHOT_APPLICATION (app);
818 
819   G_APPLICATION_CLASS (screenshot_application_parent_class)->startup (app);
820 
821   hdy_init ();
822 
823   screenshot_load_config ();
824 
825   g_set_application_name (_("Screenshot"));
826   gtk_window_set_default_icon_name (SCREENSHOT_ICON_NAME);
827 
828   g_action_map_add_action_entries (G_ACTION_MAP (self), action_entries,
829                                    G_N_ELEMENTS (action_entries), self);
830 
831   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.help", help_accels);
832   gtk_application_set_accels_for_action (GTK_APPLICATION (self), "app.quit", quit_accels);
833 }
834 
835 static void
screenshot_application_activate(GApplication * app)836 screenshot_application_activate (GApplication *app)
837 {
838   GtkWindow *window;
839 
840   window = gtk_application_get_active_window (GTK_APPLICATION (app));
841   if (window != NULL)
842     {
843       gtk_window_present (GTK_WINDOW (window));
844       return;
845     }
846 
847   screenshot_config->interactive = TRUE;
848   screenshot_show_interactive_dialog (SCREENSHOT_APPLICATION (app));
849 }
850 
851 static void
screenshot_application_finalize(GObject * object)852 screenshot_application_finalize (GObject *object)
853 {
854   ScreenshotApplication *self = SCREENSHOT_APPLICATION (object);
855 
856   g_clear_object (&self->screenshot);
857   g_free (self->icc_profile_base64);
858   g_free (self->save_uri);
859   g_clear_pointer (&self->rectangle, g_free);
860 
861   G_OBJECT_CLASS (screenshot_application_parent_class)->finalize (object);
862 }
863 
864 static void
screenshot_application_class_init(ScreenshotApplicationClass * klass)865 screenshot_application_class_init (ScreenshotApplicationClass *klass)
866 {
867   GObjectClass *object_class = G_OBJECT_CLASS (klass);
868   GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
869 
870   object_class->finalize = screenshot_application_finalize;
871 
872   application_class->handle_local_options = screenshot_application_handle_local_options;
873   application_class->command_line = screenshot_application_command_line;
874   application_class->startup = screenshot_application_startup;
875   application_class->activate = screenshot_application_activate;
876 }
877 
878 static void
screenshot_application_init(ScreenshotApplication * self)879 screenshot_application_init (ScreenshotApplication *self)
880 {
881   g_application_add_main_option_entries (G_APPLICATION (self), entries);
882 }
883 
884 ScreenshotApplication *
screenshot_application_new(void)885 screenshot_application_new (void)
886 {
887   return g_object_new (SCREENSHOT_TYPE_APPLICATION,
888                        "application-id", "org.gnome.Screenshot",
889                        "flags", G_APPLICATION_HANDLES_COMMAND_LINE,
890                        NULL);
891 }
892