1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2018, Red Hat, Inc
3  *
4  * This library 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 
18 #include "config.h"
19 
20 #include "gtkcolorpickerportalprivate.h"
21 #include "gtkprivate.h"
22 #include <gio/gio.h>
23 
24 struct _GtkColorPickerPortal
25 {
26   GObject parent_instance;
27 
28   GDBusProxy *portal_proxy;
29   guint portal_signal_id;
30   GTask *task;
31 };
32 
33 struct _GtkColorPickerPortalClass
34 {
35   GObjectClass parent_class;
36 };
37 
38 static GInitableIface *initable_parent_iface;
39 static void gtk_color_picker_portal_initable_iface_init (GInitableIface *iface);
40 static void gtk_color_picker_portal_iface_init (GtkColorPickerInterface *iface);
41 
G_DEFINE_TYPE_WITH_CODE(GtkColorPickerPortal,gtk_color_picker_portal,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,gtk_color_picker_portal_initable_iface_init)G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_PICKER,gtk_color_picker_portal_iface_init))42 G_DEFINE_TYPE_WITH_CODE (GtkColorPickerPortal, gtk_color_picker_portal, G_TYPE_OBJECT,
43                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gtk_color_picker_portal_initable_iface_init)
44                          G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_PICKER, gtk_color_picker_portal_iface_init))
45 
46 static gboolean
47 gtk_color_picker_portal_initable_init (GInitable     *initable,
48                                        GCancellable  *cancellable,
49                                        GError       **error)
50 {
51   GtkColorPickerPortal *picker = GTK_COLOR_PICKER_PORTAL (initable);
52   char *owner;
53   GVariant *ret;
54   guint version = 0;
55 
56   if (!gtk_should_use_portal ())
57     return FALSE;
58 
59   picker->portal_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
60                                                         G_DBUS_PROXY_FLAGS_NONE,
61                                                         NULL,
62                                                         "org.freedesktop.portal.Desktop",
63                                                         "/org/freedesktop/portal/desktop",
64                                                         "org.freedesktop.portal.Screenshot",
65                                                         NULL,
66                                                         error);
67 
68   if (picker->portal_proxy == NULL)
69     {
70       g_debug ("Failed to create screenshot portal proxy");
71       return FALSE;
72     }
73 
74   owner = g_dbus_proxy_get_name_owner (picker->portal_proxy);
75   if (owner == NULL)
76     {
77       g_debug ("org.freedesktop.portal.Screenshot not provided");
78       g_clear_object (&picker->portal_proxy);
79       return FALSE;
80     }
81   g_free (owner);
82 
83   ret = g_dbus_proxy_get_cached_property (picker->portal_proxy, "version");
84   if (ret)
85     {
86       version = g_variant_get_uint32 (ret);
87       g_variant_unref (ret);
88     }
89 
90   if (version != 2)
91     {
92       g_debug ("Screenshot portal version: %u", version);
93       g_clear_object (&picker->portal_proxy);
94       return FALSE;
95     }
96 
97   return TRUE;
98 }
99 
100 static void
gtk_color_picker_portal_initable_iface_init(GInitableIface * iface)101 gtk_color_picker_portal_initable_iface_init (GInitableIface *iface)
102 {
103   initable_parent_iface = g_type_interface_peek_parent (iface);
104   iface->init = gtk_color_picker_portal_initable_init;
105 }
106 
107 static void
gtk_color_picker_portal_init(GtkColorPickerPortal * picker)108 gtk_color_picker_portal_init (GtkColorPickerPortal *picker)
109 {
110 }
111 
112 static void
gtk_color_picker_portal_finalize(GObject * object)113 gtk_color_picker_portal_finalize (GObject *object)
114 {
115   GtkColorPickerPortal *picker = GTK_COLOR_PICKER_PORTAL (object);
116 
117   g_clear_object (&picker->portal_proxy);
118 
119   G_OBJECT_CLASS (gtk_color_picker_portal_parent_class)->finalize (object);
120 }
121 
122 static void
gtk_color_picker_portal_class_init(GtkColorPickerPortalClass * class)123 gtk_color_picker_portal_class_init (GtkColorPickerPortalClass *class)
124 {
125   GObjectClass *object_class = G_OBJECT_CLASS (class);
126 
127   object_class->finalize = gtk_color_picker_portal_finalize;
128 }
129 
130 GtkColorPicker *
gtk_color_picker_portal_new(void)131 gtk_color_picker_portal_new (void)
132 {
133   return GTK_COLOR_PICKER (g_initable_new (GTK_TYPE_COLOR_PICKER_PORTAL, NULL, NULL, NULL));
134 }
135 
136 static void
portal_response_received(GDBusConnection * connection,const char * sender_name,const char * object_path,const char * interface_name,const char * signal_name,GVariant * parameters,gpointer user_data)137 portal_response_received (GDBusConnection *connection,
138                           const char      *sender_name,
139                           const char      *object_path,
140                           const char      *interface_name,
141                           const char      *signal_name,
142                           GVariant        *parameters,
143                           gpointer         user_data)
144 {
145   GtkColorPickerPortal *picker = user_data;
146   guint32 response;
147   GVariant *ret;
148 
149   g_dbus_connection_signal_unsubscribe (connection, picker->portal_signal_id);
150   picker->portal_signal_id = 0;
151 
152   g_variant_get (parameters, "(u@a{sv})", &response, &ret);
153 
154   if (response == 0)
155     {
156       GdkRGBA c;
157 
158       c.alpha = 1.0;
159       if (g_variant_lookup (ret, "color", "(ddd)", &c.red, &c.green, &c.blue))
160         g_task_return_pointer (picker->task, gdk_rgba_copy (&c), (GDestroyNotify)gdk_rgba_free);
161       else
162         g_task_return_new_error (picker->task,
163                                  G_IO_ERROR,
164                                  G_IO_ERROR_FAILED,
165                                  "No color received");
166     }
167   else
168     g_task_return_new_error (picker->task,
169                              G_IO_ERROR,
170                              G_IO_ERROR_FAILED,
171                              "PickColor error");
172 
173   g_variant_unref (ret);
174 
175   g_clear_object (&picker->task);
176 }
177 
178 static void
gtk_color_picker_portal_pick(GtkColorPicker * cp,GAsyncReadyCallback callback,gpointer user_data)179 gtk_color_picker_portal_pick (GtkColorPicker      *cp,
180                               GAsyncReadyCallback  callback,
181                               gpointer             user_data)
182 {
183   GtkColorPickerPortal *picker = GTK_COLOR_PICKER_PORTAL (cp);
184   GVariantBuilder options;
185   GDBusConnection *connection;
186   char *token;
187   char *handle;
188 
189   if (picker->task)
190     return;
191 
192   picker->task = g_task_new (picker, NULL, callback, user_data);
193 
194   connection = g_dbus_proxy_get_connection (picker->portal_proxy);
195 
196   handle = gtk_get_portal_request_path (connection, &token);
197   picker->portal_signal_id = g_dbus_connection_signal_subscribe (connection,
198                                                                  "org.freedesktop.portal.Desktop",
199                                                                  "org.freedesktop.portal.Request",
200                                                                  "Response",
201                                                                  handle,
202                                                                  NULL,
203                                                                  G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
204                                                                  portal_response_received,
205                                                                  picker,
206                                                                  NULL);
207 
208   g_free (handle);
209 
210   g_variant_builder_init (&options, G_VARIANT_TYPE_VARDICT);
211   g_variant_builder_add (&options, "{sv}", "handle_token", g_variant_new_string (token));
212   g_free (token);
213 
214   g_dbus_proxy_call (picker->portal_proxy,
215                      "PickColor",
216                      g_variant_new ("(sa{sv})", "", &options),
217                      0,
218                      -1,
219                      NULL,
220                      NULL,
221                      NULL);
222 }
223 
224 static GdkRGBA *
gtk_color_picker_portal_pick_finish(GtkColorPicker * cp,GAsyncResult * res,GError ** error)225 gtk_color_picker_portal_pick_finish (GtkColorPicker  *cp,
226                                      GAsyncResult    *res,
227                                      GError         **error)
228 {
229   g_return_val_if_fail (g_task_is_valid (res, cp), NULL);
230 
231   return g_task_propagate_pointer (G_TASK (res), error);
232 }
233 
234 static void
gtk_color_picker_portal_iface_init(GtkColorPickerInterface * iface)235 gtk_color_picker_portal_iface_init (GtkColorPickerInterface *iface)
236 {
237   iface->pick = gtk_color_picker_portal_pick;
238   iface->pick_finish = gtk_color_picker_portal_pick_finish;
239 }
240