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