2
* Copyright (C) 2015 Alberts Muktupāvels
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, either version 3 of the License, or
7
* (at your option) any later version.
9
* This program 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
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Based on code in gnome-screenshot:
18
* https://git.gnome.org/browse/gnome-screenshot/tree/src/screenshot-utils.c
19
* Copyright (C) 2001 - 2006 Jonathan Blandford, 2008 Cosimo Cecchi
26
#include <X11/extensions/shape.h>
28
#include "gf-dbus-screenshot.h"
29
#include "gf-flashspot.h"
30
#include "gf-screenshot.h"
31
#include "gf-select-area.h"
33
#define SCREENSHOT_DBUS_NAME "org.gnome.Shell.Screenshot"
34
#define SCREENSHOT_DBUS_PATH "/org/gnome/Shell/Screenshot"
36
typedef void (*GfInvocationCallback) (GfDBusScreenshot *dbus_screenshot,
37
GDBusMethodInvocation *invocation,
39
const gchar *filename);
45
GfDBusScreenshot *dbus_screenshot;
55
GfScreenshot *screenshot;
66
G_DEFINE_TYPE (GfScreenshot, gf_screenshot, G_TYPE_OBJECT)
69
get_unique_path (const gchar *path,
70
const gchar *filename)
77
ptr = g_strrstr (filename, ".png");
80
real_filename = g_strndup (filename, ptr - filename);
82
real_filename = g_strdup (filename);
92
name = g_strdup_printf ("%s.png", real_filename);
94
name = g_strdup_printf ("%s - %d.png", real_filename, idx);
97
real_path = g_build_filename (path, name, NULL);
102
while (g_file_test (real_path, G_FILE_TEST_EXISTS));
104
g_free (real_filename);
110
get_filename (const gchar *filename)
114
if (g_path_is_absolute (filename))
115
return g_strdup (filename);
117
path = g_get_user_special_dir (G_USER_DIRECTORY_PICTURES);
119
if (!g_file_test (path, G_FILE_TEST_EXISTS))
121
path = g_get_home_dir ();
123
if (!g_file_test (path, G_FILE_TEST_EXISTS))
127
return get_unique_path (path, filename);
131
save_screenshot (GdkPixbuf *pixbuf,
132
const gchar *filename_in,
133
gchar **filename_out)
142
filename = get_filename (filename_in);
145
result = gdk_pixbuf_save (pixbuf, filename, "png", &error, NULL);
148
*filename_out = filename;
154
g_warning ("Failed to save screenshot: %s", error->message);
155
g_error_free (error);
158
g_object_unref (pixbuf);
164
blank_rectangle_in_pixbuf (GdkPixbuf *pixbuf,
177
g_assert (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
179
x2 = rect->x + rect->width;
180
y2 = rect->y + rect->height;
182
pixels = gdk_pixbuf_get_pixels (pixbuf);
183
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
184
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
185
n_channels = gdk_pixbuf_get_n_channels (pixbuf);
187
for (y = rect->y; y < y2; y++)
191
row = pixels + y * rowstride;
192
p = row + rect->x * n_channels;
194
for (x = rect->x; x < x2; x++)
207
blank_region_in_pixbuf (GdkPixbuf *pixbuf,
208
cairo_region_t *region)
214
cairo_rectangle_int_t pixbuf_rect;
216
n_rects = cairo_region_num_rectangles (region);
218
width = gdk_pixbuf_get_width (pixbuf);
219
height = gdk_pixbuf_get_height (pixbuf);
223
pixbuf_rect.width = width;
224
pixbuf_rect.height = height;
226
for (i = 0; i < n_rects; i++)
228
cairo_rectangle_int_t rect;
229
cairo_rectangle_int_t dest;
231
cairo_region_get_rectangle (region, i, &rect);
233
if (gdk_rectangle_intersect (&rect, &pixbuf_rect, &dest))
234
blank_rectangle_in_pixbuf (pixbuf, &dest);
238
static cairo_region_t *
239
make_region_with_monitors (GdkScreen *screen)
241
cairo_region_t *region;
245
num_monitors = gdk_screen_get_n_monitors (screen);
246
region = cairo_region_create ();
248
for (i = 0; i < num_monitors; i++)
252
gdk_screen_get_monitor_geometry (screen, i, &rect);
253
cairo_region_union_rectangle (region, &rect);
260
mask_monitors (GdkPixbuf *pixbuf,
261
GdkWindow *root_window)
264
cairo_region_t *region_with_monitors;
265
cairo_region_t *invisible_region;
266
cairo_rectangle_int_t rect;
268
screen = gdk_window_get_screen (root_window);
269
region_with_monitors = make_region_with_monitors (screen);
273
rect.width = gdk_screen_get_width (screen);
274
rect.height = gdk_screen_get_height (screen);
276
invisible_region = cairo_region_create_rectangle (&rect);
277
cairo_region_subtract (invisible_region, region_with_monitors);
279
blank_region_in_pixbuf (pixbuf, invisible_region);
281
cairo_region_destroy (region_with_monitors);
282
cairo_region_destroy (invisible_region);
286
find_wm_window (GdkDisplay *display,
294
unsigned int nchildren;
296
if (window == gdk_get_default_root_window ())
299
xdisplay = GDK_DISPLAY_XDISPLAY (display);
300
xid = GDK_WINDOW_XID (window);
304
if (XQueryTree (xdisplay, xid, &root, &parent, &children, &nchildren) == 0)
306
g_warning ("Couldn't find window manager window");
312
if (children != NULL)
320
if (children != NULL)
326
get_window_rect_coords (GdkWindow *window,
327
gboolean include_frame,
328
GdkRectangle *real_out,
329
GdkRectangle *screenshot_out)
341
gdk_window_get_frame_extents (window, &real);
345
real.width = gdk_window_get_width (window);
346
real.height = gdk_window_get_height (window);
348
gdk_window_get_origin (window, &real.x, &real.y);
351
if (real_out != NULL)
357
height = real.height;
371
screen_width = gdk_screen_width ();
372
if (x + width > screen_width)
373
width = screen_width - x;
375
screen_height = gdk_screen_height ();
376
if (y + height > screen_height)
377
height = screen_height - y;
379
if (screenshot_out != NULL)
381
screenshot_out->x = x;
382
screenshot_out->y = y;
383
screenshot_out->width = width;
384
screenshot_out->height = height;
389
window_is_desktop (GdkWindow *window)
391
GdkWindow *root_window;
392
GdkWindowTypeHint type_hint;
394
root_window = gdk_get_default_root_window ();
396
if (window == root_window)
399
type_hint = gdk_window_get_type_hint (window);
401
if (type_hint == GDK_WINDOW_TYPE_HINT_DESKTOP)
408
find_active_window (void)
412
screen = gdk_screen_get_default ();
414
return gdk_screen_get_active_window (screen);
418
find_current_window (GdkDisplay *display)
423
window = find_active_window ();
427
GdkDeviceManager *manager;
430
manager = gdk_display_get_device_manager (display);
431
device = gdk_device_manager_get_client_pointer (manager);
433
window = gdk_device_get_window_at_position (device, NULL, NULL);
436
g_object_ref (window);
441
if (window_is_desktop (window))
443
g_object_unref (window);
447
toplevel = gdk_window_get_toplevel (window);
449
g_object_unref (window);
457
get_current_window (GdkDisplay *display,
460
if (type == SCREENSHOT_WINDOW)
461
return find_current_window (display);
463
return gdk_get_default_root_window ();
467
take_screenshot_real (GfScreenshot *screenshot,
469
gboolean include_frame,
470
gboolean include_cursor,
475
const gchar *filename_in,
476
gchar **filename_out)
483
GdkWindow *wm_window;
484
GtkBorder frame_offset;
488
display = gdk_display_get_default ();
489
window = get_current_window (display, type);
494
get_window_rect_coords (window, include_frame, &real, &s);
496
wm = find_wm_window (display, window);
500
GdkRectangle wm_real;
502
wm_window = gdk_x11_window_foreign_new_for_display (display, wm);
503
get_window_rect_coords (wm_window, FALSE, &wm_real, NULL);
505
frame_offset.left = (gdouble) (real.x - wm_real.x);
506
frame_offset.top = (gdouble) (real.y - wm_real.y);
507
frame_offset.right = (gdouble) (wm_real.width - real.width - frame_offset.left);
508
frame_offset.bottom = (gdouble) (wm_real.height - real.height - frame_offset.top);
512
frame_offset.left = 0;
513
frame_offset.top = 0;
514
frame_offset.right = 0;
515
frame_offset.bottom = 0;
518
if (type != SCREENSHOT_WINDOW)
526
root = gdk_get_default_root_window ();
527
pixbuf = gdk_pixbuf_get_from_window (root, s.x, s.y, s.width, s.height);
529
if (type != SCREENSHOT_WINDOW && type != SCREENSHOT_AREA)
530
mask_monitors (pixbuf, root);
532
if (include_frame && wm != None)
534
XRectangle *rectangles;
536
gint rectangle_count;
537
gint rectangle_order;
540
rectangles = XShapeGetRectangles (GDK_DISPLAY_XDISPLAY (display),
542
&rectangle_count, &rectangle_order);
544
if (rectangles && rectangle_count > 0)
548
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
550
tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, s.width, s.height);
551
gdk_pixbuf_fill (tmp, 0);
553
for (i = 0; i < rectangle_count; i++)
561
/* If we're using invisible borders, the ShapeBounding might not
562
* have the same size as the frame extents, as it would include
563
* the areas for the invisible borders themselves.
564
* In that case, trim every rectangle we get by the offset between
565
* the WM window size and the frame extents. */
566
rec_x = rectangles[i].x;
567
rec_y = rectangles[i].y;
568
rec_width = rectangles[i].width - (frame_offset.left + frame_offset.right);
569
rec_height = rectangles[i].height - (frame_offset.top + frame_offset.bottom);
574
rec_x = MAX(rec_x, 0);
581
rec_y = MAX(rec_y, 0);
582
rec_height += real.y;
585
if (s.x + rec_x + rec_width > gdk_screen_width ())
586
rec_width = gdk_screen_width () - s.x - rec_x;
588
if (s.y + rec_y + rec_height > gdk_screen_height ())
589
rec_height = gdk_screen_height () - s.y - rec_y;
591
for (y2 = rec_y; y2 < rec_y + rec_height; y2++)
597
src_pixels = gdk_pixbuf_get_pixels (pixbuf)
598
+ y2 * gdk_pixbuf_get_rowstride(pixbuf)
599
+ rec_x * (has_alpha ? 4 : 3);
600
dest_pixels = gdk_pixbuf_get_pixels (tmp)
601
+ y2 * gdk_pixbuf_get_rowstride (tmp)
604
for (x2 = 0; x2 < rec_width; x2++)
606
*dest_pixels++ = *src_pixels++;
607
*dest_pixels++ = *src_pixels++;
608
*dest_pixels++ = *src_pixels++;
611
*dest_pixels++ = *src_pixels++;
613
*dest_pixels++ = 255;
618
g_object_unref (pixbuf);
625
/* If we have a selected area, there were by definition no cursor in the
627
if (include_cursor && type == SCREENSHOT_AREA)
630
GdkPixbuf *cursor_pixbuf;
632
cursor = gdk_cursor_new_for_display (display, GDK_LEFT_PTR);
633
cursor_pixbuf = gdk_cursor_get_image (cursor);
635
if (cursor_pixbuf != NULL)
637
GdkDeviceManager *manager;
645
manager = gdk_display_get_device_manager (display);
646
device = gdk_device_manager_get_client_pointer (manager);
648
if (wm_window != NULL)
649
gdk_window_get_device_position (wm_window, device, &cx, &cy, NULL);
651
gdk_window_get_device_position (window, device, &cx, &cy, NULL);
653
sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "x_hot"), "%d", &xhot);
654
sscanf (gdk_pixbuf_get_option (cursor_pixbuf, "y_hot"), "%d", &yhot);
656
/* in rect we have the cursor window coordinates */
657
rect.x = cx + real.x;
658
rect.y = cy + real.y;
659
rect.width = gdk_pixbuf_get_width (cursor_pixbuf);
660
rect.height = gdk_pixbuf_get_height (cursor_pixbuf);
662
/* see if the pointer is inside the window */
663
if (gdk_rectangle_intersect (&real, &rect, &rect))
668
cursor_x = cx - xhot - frame_offset.left;
669
cursor_y = cy - yhot - frame_offset.top;
671
gdk_pixbuf_composite (cursor_pixbuf, pixbuf,
673
rect.width, rect.height,
681
g_clear_object (&cursor_pixbuf);
682
g_clear_object (&cursor);
685
if (type == SCREENSHOT_WINDOW)
689
get_window_rect_coords (window, include_frame, NULL, &rect);
694
*height = rect.height;
697
return save_screenshot (pixbuf, filename_in, filename_out);
701
remove_sender (GfScreenshot *screenshot,
706
name_id = g_hash_table_lookup (screenshot->senders, sender);
711
g_bus_unwatch_name (GPOINTER_TO_UINT (name_id));
712
g_hash_table_remove (screenshot->senders, sender);
715
static FlashspotData *
716
flashspot_data_new (GfScreenshot *screenshot,
721
data = (FlashspotData *) g_new0 (FlashspotData *, 1);
723
data->screenshot = g_object_ref (screenshot);
724
data->sender = g_strdup (sender);
730
flashspot_data_free (gpointer data)
732
FlashspotData *real_data;
734
real_data = (FlashspotData *) data;
736
g_object_unref (real_data->screenshot);
737
g_free (real_data->sender);
743
flashspot_finished (GfFlashspot *flashspot,
748
data = (FlashspotData *) g_object_get_data (G_OBJECT (flashspot), "data");
750
remove_sender (data->screenshot, data->sender);
754
name_vanished_handler (GDBusConnection *connection,
758
GfScreenshot *screenshot;
760
screenshot = GF_SCREENSHOT (user_data);
762
remove_sender (screenshot, name);
766
take_screenshot (GfScreenshot *screenshot,
767
GDBusMethodInvocation *invocation,
769
gboolean include_frame,
770
gboolean include_cursor,
775
GfInvocationCallback callback,
777
const gchar *filename_in)
785
sender = g_dbus_method_invocation_get_sender (invocation);
786
disabled = g_settings_get_boolean (screenshot->lockdown, "disable-save-to-disk");
788
if (g_hash_table_lookup (screenshot->senders, sender) != NULL || disabled)
790
callback (screenshot->dbus_screenshot, invocation, FALSE, "");
794
name_id = g_bus_watch_name (G_BUS_TYPE_SESSION, sender,
795
G_BUS_NAME_WATCHER_FLAGS_NONE,
796
NULL, name_vanished_handler,
799
g_hash_table_insert (screenshot->senders, g_strdup (sender),
800
GUINT_TO_POINTER (name_id));
803
result = take_screenshot_real (screenshot, type,
804
include_frame, include_cursor,
805
&x, &y, &width, &height,
806
filename_in, &filename_out);
810
GfFlashspot *flashspot;
813
flashspot = gf_flashspot_new ();
814
data = flashspot_data_new (screenshot, sender);
816
g_object_set_data_full (G_OBJECT (flashspot), "data", data,
817
flashspot_data_free);
819
g_signal_connect (flashspot, "finished",
820
G_CALLBACK (flashspot_finished), NULL);
822
gf_flashspot_fire (flashspot, x, y, width, height);
823
g_object_unref (flashspot);
827
remove_sender (screenshot, sender);
830
callback (screenshot->dbus_screenshot, invocation,
831
result, filename_out ? filename_out : "");
833
g_free (filename_out);
845
unscale_area (gint *x,
862
screen = gdk_screen_get_default ();
863
screen_width = gdk_screen_get_width (screen);
864
screen_height = gdk_screen_get_height (screen);
866
return x >= 0 && y >= 0 && width > 0 && height > 0 &&
867
x + width <= screen_width && y + height <= screen_height;
871
handle_screenshot (GfDBusScreenshot *dbus_screenshot,
872
GDBusMethodInvocation *invocation,
873
gboolean include_cursor,
875
const gchar *filename,
878
GfScreenshot *screenshot;
883
screenshot = GF_SCREENSHOT (user_data);
884
screen = gdk_screen_get_default ();
885
width = gdk_screen_get_width (screen);
886
height = gdk_screen_get_height (screen);
888
take_screenshot (screenshot, invocation, SCREENSHOT_SCREEN,
889
FALSE, include_cursor, 0, 0, width, height,
890
gf_dbus_screenshot_complete_screenshot,
897
handle_screenshot_window (GfDBusScreenshot *dbus_screenshot,
898
GDBusMethodInvocation *invocation,
899
gboolean include_frame,
900
gboolean include_cursor,
902
const gchar *filename,
905
GfScreenshot *screenshot;
907
screenshot = GF_SCREENSHOT (user_data);
909
take_screenshot (screenshot, invocation, SCREENSHOT_WINDOW,
910
include_frame, include_cursor, 0, 0, 0, 0,
911
gf_dbus_screenshot_complete_screenshot_window,
918
handle_screenshot_area (GfDBusScreenshot *dbus_screenshot,
919
GDBusMethodInvocation *invocation,
925
const gchar *filename,
928
GfScreenshot *screenshot;
930
screenshot = GF_SCREENSHOT (user_data);
932
if (!check_area (x, y, width, height))
934
g_dbus_method_invocation_return_error (invocation, G_IO_ERROR,
935
G_IO_ERROR_CANCELLED,
941
scale_area (&x, &y, &width, &height);
942
take_screenshot (screenshot, invocation, SCREENSHOT_AREA,
943
FALSE, FALSE, x, y, width, height,
944
gf_dbus_screenshot_complete_screenshot_area,
951
handle_flash_area (GfDBusScreenshot *dbus_screenshot,
952
GDBusMethodInvocation *invocation,
959
GfFlashspot *flashspot;
961
if (!check_area (x, y, width, height))
963
g_dbus_method_invocation_return_error (invocation, G_IO_ERROR,
964
G_IO_ERROR_CANCELLED,
970
scale_area (&x, &y, &width, &height);
972
flashspot = gf_flashspot_new ();
973
gf_flashspot_fire (flashspot, x, y, width, height);
974
g_object_unref (flashspot);
976
gf_dbus_screenshot_complete_flash_area (dbus_screenshot, invocation);
982
handle_select_area (GfDBusScreenshot *dbus_screenshot,
983
GDBusMethodInvocation *invocation,
986
GfSelectArea *select_area;
992
select_area = gf_select_area_new ();
993
x = y = width = height = 0;
995
if (gf_select_area_select (select_area, &x, &y, &width, &height))
997
unscale_area (&x, &y, &width, &height);
998
gf_dbus_screenshot_complete_select_area (dbus_screenshot, invocation,
999
x, y, width, height);
1003
g_dbus_method_invocation_return_error (invocation, G_IO_ERROR,
1004
G_IO_ERROR_CANCELLED,
1005
"Operation was cancelled");
1008
g_object_unref (select_area);
1014
bus_acquired_handler (GDBusConnection *connection,
1018
GfScreenshot *screenshot;
1019
GfDBusScreenshot *dbus_screenshot;
1020
GDBusInterfaceSkeleton *skeleton;
1024
screenshot = GF_SCREENSHOT (user_data);
1026
dbus_screenshot = screenshot->dbus_screenshot;
1027
skeleton = G_DBUS_INTERFACE_SKELETON (dbus_screenshot);
1029
g_signal_connect (dbus_screenshot, "handle-screenshot",
1030
G_CALLBACK (handle_screenshot), screenshot);
1031
g_signal_connect (dbus_screenshot, "handle-screenshot-window",
1032
G_CALLBACK (handle_screenshot_window), screenshot);
1033
g_signal_connect (dbus_screenshot, "handle-screenshot-area",
1034
G_CALLBACK (handle_screenshot_area), screenshot);
1035
g_signal_connect (dbus_screenshot, "handle-flash-area",
1036
G_CALLBACK (handle_flash_area), screenshot);
1037
g_signal_connect (dbus_screenshot, "handle-select-area",
1038
G_CALLBACK (handle_select_area), screenshot);
1041
exported = g_dbus_interface_skeleton_export (skeleton, connection,
1042
SCREENSHOT_DBUS_PATH,
1047
g_warning ("Failed to export interface: %s", error->message);
1048
g_error_free (error);
1054
gf_screenshot_dispose (GObject *object)
1056
GfScreenshot *screenshot;
1057
GDBusInterfaceSkeleton *skeleton;
1059
screenshot = GF_SCREENSHOT (object);
1061
if (screenshot->bus_name)
1063
g_bus_unown_name (screenshot->bus_name);
1064
screenshot->bus_name = 0;
1067
if (screenshot->dbus_screenshot)
1069
skeleton = G_DBUS_INTERFACE_SKELETON (screenshot->dbus_screenshot);
1071
g_dbus_interface_skeleton_unexport (skeleton);
1072
g_clear_object (&screenshot->dbus_screenshot);
1075
if (screenshot->senders)
1077
g_hash_table_destroy (screenshot->senders);
1078
screenshot->senders = NULL;
1081
g_clear_object (&screenshot->lockdown);
1083
G_OBJECT_CLASS (gf_screenshot_parent_class)->dispose (object);
1087
gf_screenshot_class_init (GfScreenshotClass *screenshot_class)
1089
GObjectClass *object_class;
1091
object_class = G_OBJECT_CLASS (screenshot_class);
1093
object_class->dispose = gf_screenshot_dispose;
1097
gf_screenshot_init (GfScreenshot *screenshot)
1099
screenshot->dbus_screenshot = gf_dbus_screenshot_skeleton_new ();
1101
screenshot->bus_name = g_bus_own_name (G_BUS_TYPE_SESSION,
1102
SCREENSHOT_DBUS_NAME,
1103
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
1104
G_BUS_NAME_OWNER_FLAGS_REPLACE,
1105
(GBusAcquiredCallback) bus_acquired_handler,
1106
NULL, NULL, screenshot, NULL);
1108
screenshot->senders = g_hash_table_new_full (g_str_hash, g_str_equal,
1111
screenshot->lockdown = g_settings_new ("org.gnome.desktop.lockdown");
1115
gf_screenshot_new (void)
1117
return g_object_new (GF_TYPE_SCREENSHOT, NULL);