1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * Screenshot plug-in
5  * Copyright 1998-2007 Sven Neumann <sven@gimp.org>
6  * Copyright 2003      Henrik Brix Andersen <brix@gimp.org>
7  * Copyright 2016      Michael Natterer <mitch@gimp.org>
8  * Copyright 2017      Jehan <jehan@gimp.org>
9  *
10  * This program is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 
26 #include <glib.h>
27 #include <glib/gstdio.h> /* g_unlink() */
28 
29 #include <libgimp/gimp.h>
30 #include <libgimp/gimpui.h>
31 
32 #include "screenshot.h"
33 #include "screenshot-kwin.h"
34 
35 
36 static GDBusProxy *proxy = NULL;
37 
38 
39 gboolean
screenshot_kwin_available(void)40 screenshot_kwin_available (void)
41 {
42   proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
43                                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
44                                          NULL,
45                                          "org.kde.KWin",
46                                          "/Screenshot",
47                                          "org.kde.kwin.Screenshot",
48                                          NULL, NULL);
49 
50   if (proxy)
51     {
52       GError *error = NULL;
53 
54       g_dbus_proxy_call_sync (proxy, "org.freedesktop.DBus.Peer.Ping",
55                               NULL,
56                               G_DBUS_CALL_FLAGS_NONE,
57                               -1, NULL, &error);
58       if (! error)
59         return TRUE;
60 
61       g_clear_error (&error);
62 
63       g_object_unref (proxy);
64       proxy = NULL;
65     }
66 
67   return FALSE;
68 }
69 
70 ScreenshotCapabilities
screenshot_kwin_get_capabilities(void)71 screenshot_kwin_get_capabilities (void)
72 {
73   return (SCREENSHOT_CAN_SHOOT_DECORATIONS |
74           SCREENSHOT_CAN_SHOOT_POINTER     |
75           SCREENSHOT_CAN_SHOOT_WINDOW      |
76           SCREENSHOT_CAN_PICK_WINDOW);
77   /* TODO: SCREENSHOT_CAN_SHOOT_REGION.
78    * The KDE API has "screenshotArea" method but no method to get
79    * coordinates could be found. See below.
80    */
81 }
82 
83 GimpPDBStatusType
screenshot_kwin_shoot(ScreenshotValues * shootvals,GdkScreen * screen,gint32 * image_ID,GError ** error)84 screenshot_kwin_shoot (ScreenshotValues  *shootvals,
85                        GdkScreen         *screen,
86                        gint32            *image_ID,
87                        GError           **error)
88 {
89   gchar       *filename = NULL;
90   const gchar *method   = NULL;
91   GVariant    *args     = NULL;
92   GVariant    *retval;
93   gint         monitor = shootvals->monitor;
94   gint32       mask;
95 
96   switch (shootvals->shoot_type)
97     {
98     case SHOOT_ROOT:
99       if (shootvals->screenshot_delay > 0)
100         screenshot_delay (shootvals->screenshot_delay);
101       else
102         {
103           /* As an exception, I force a delay of at least 0.5 seconds
104            * for KWin. Because of windows effect slowly fading out, the
105            * screenshot plug-in GUI was constantly visible (with
106            * transparency as it is fading out) in 0s-delay screenshots.
107            */
108           g_usleep (500000);
109         }
110 
111       method = "screenshotFullscreen";
112       args   = g_variant_new ("(b)", shootvals->show_cursor);
113 
114       /* FIXME: figure profile */
115       break;
116 
117     case SHOOT_REGION:
118       break;
119       /* FIXME: GNOME-shell has a "SelectArea" returning coordinates
120        * which can be fed to "ScreenshotArea". KDE has the equivalent
121        * "screenshotArea", but no "SelectArea" equivalent that I could
122        * find.
123        * Also at first, I expected "interactive" method to take care of
124        * the whole selecting-are-then-screenshotting workflow, but this
125        * is apparently only made to select interactively a specific
126        * window, not an area.
127        */
128       method = "screenshotArea";
129       args   = g_variant_new ("(iiii)",
130                               shootvals->x1,
131                               shootvals->y1,
132                               shootvals->x2 - shootvals->x1,
133                               shootvals->y2 - shootvals->y1);
134       args   = NULL;
135 
136       break;
137 
138     case SHOOT_WINDOW:
139       if (shootvals->select_delay > 0)
140         screenshot_delay (shootvals->select_delay);
141 
142       /* XXX I expected "screenshotWindowUnderCursor" method to be the
143        * right one, but it returns nothing, nor is there a file
144        * descriptor in argument. So I don't understand how to grab the
145        * screenshot. Also "interactive" changes the cursor to a
146        * crosshair, waiting for click, which is more helpful than
147        * immediate screenshot under cursor.
148        */
149       method = "interactive";
150       mask = (shootvals->decorate ? 1 : 0) |
151              (shootvals->show_cursor ? 1 << 1 : 0);
152       args   = g_variant_new ("(i)", mask);
153 
154       /* FIXME: figure monitor */
155       break;
156     }
157 
158   retval = g_dbus_proxy_call_sync (proxy, method, args,
159                                    G_DBUS_CALL_FLAGS_NONE,
160                                    -1, NULL, error);
161   if (! retval)
162     goto failure;
163 
164   g_variant_get (retval, "(s)",
165                  &filename);
166   g_variant_unref (retval);
167 
168   if (filename)
169     {
170       GimpColorProfile *profile;
171 
172       *image_ID = gimp_file_load (GIMP_RUN_NONINTERACTIVE,
173                                   filename, filename);
174       gimp_image_set_filename (*image_ID, "screenshot.png");
175 
176       /* This is very wrong in multi-display setups since we have no
177        * idea which profile is to be used. Let's keep it anyway and
178        * assume always the monitor 0, which will still work in common
179        * cases.
180        */
181       profile = gimp_screen_get_color_profile (screen, monitor);
182 
183       if (profile)
184         {
185           gimp_image_set_color_profile (*image_ID, profile);
186           g_object_unref (profile);
187         }
188 
189       g_unlink (filename);
190       g_free (filename);
191 
192       g_object_unref (proxy);
193       proxy = NULL;
194 
195       return GIMP_PDB_SUCCESS;
196     }
197 
198  failure:
199 
200   if (filename)
201     g_free (filename);
202 
203   g_object_unref (proxy);
204   proxy = NULL;
205 
206   return GIMP_PDB_EXECUTION_ERROR;
207 }
208