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 | |
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 (ptr: initable); |
52 | char *owner; |
53 | GVariant *ret; |
54 | guint version = 0; |
55 | |
56 | if (!gdk_should_use_portal ()) |
57 | return FALSE; |
58 | |
59 | picker->portal_proxy = g_dbus_proxy_new_for_bus_sync (bus_type: G_BUS_TYPE_SESSION, |
60 | flags: G_DBUS_PROXY_FLAGS_NONE, |
61 | NULL, |
62 | PORTAL_BUS_NAME, |
63 | PORTAL_OBJECT_PATH, |
64 | PORTAL_SCREENSHOT_INTERFACE, |
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 (proxy: picker->portal_proxy); |
75 | if (owner == NULL) |
76 | { |
77 | g_debug ("%s not provided" , PORTAL_SCREENSHOT_INTERFACE); |
78 | g_clear_object (&picker->portal_proxy); |
79 | return FALSE; |
80 | } |
81 | g_free (mem: owner); |
82 | |
83 | ret = g_dbus_proxy_get_cached_property (proxy: picker->portal_proxy, property_name: "version" ); |
84 | if (ret) |
85 | { |
86 | version = g_variant_get_uint32 (value: ret); |
87 | g_variant_unref (value: 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 |
101 | gtk_color_picker_portal_initable_iface_init (GInitableIface *iface) |
102 | { |
103 | initable_parent_iface = g_type_interface_peek_parent (g_iface: iface); |
104 | iface->init = gtk_color_picker_portal_initable_init; |
105 | } |
106 | |
107 | static void |
108 | gtk_color_picker_portal_init (GtkColorPickerPortal *picker) |
109 | { |
110 | } |
111 | |
112 | static void |
113 | gtk_color_picker_portal_finalize (GObject *object) |
114 | { |
115 | GtkColorPickerPortal *picker = GTK_COLOR_PICKER_PORTAL (ptr: 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 |
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 * |
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 |
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, subscription_id: picker->portal_signal_id); |
150 | picker->portal_signal_id = 0; |
151 | |
152 | g_variant_get (value: parameters, format_string: "(u@a{sv})" , &response, &ret); |
153 | |
154 | if (response == 0) |
155 | { |
156 | double d1, d2, d3; |
157 | |
158 | if (g_variant_lookup (dictionary: ret, key: "color" , format_string: "(ddd)" , &d1, &d2, &d3)) |
159 | g_task_return_pointer (task: picker->task, |
160 | result: gdk_rgba_copy (rgba: &(GdkRGBA){d1, d2, d3, 1.0f}), result_destroy: (GDestroyNotify)gdk_rgba_free); |
161 | else |
162 | g_task_return_new_error (task: picker->task, |
163 | G_IO_ERROR, |
164 | code: G_IO_ERROR_FAILED, |
165 | format: "No color received" ); |
166 | } |
167 | else |
168 | g_task_return_new_error (task: picker->task, |
169 | G_IO_ERROR, |
170 | code: G_IO_ERROR_FAILED, |
171 | format: "PickColor error" ); |
172 | |
173 | g_variant_unref (value: ret); |
174 | |
175 | g_clear_object (&picker->task); |
176 | } |
177 | |
178 | static void |
179 | gtk_color_picker_portal_pick (GtkColorPicker *cp, |
180 | GAsyncReadyCallback callback, |
181 | gpointer user_data) |
182 | { |
183 | GtkColorPickerPortal *picker = GTK_COLOR_PICKER_PORTAL (ptr: 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 (source_object: picker, NULL, callback, callback_data: user_data); |
193 | |
194 | connection = g_dbus_proxy_get_connection (proxy: picker->portal_proxy); |
195 | |
196 | handle = gtk_get_portal_request_path (connection, token: &token); |
197 | picker->portal_signal_id = g_dbus_connection_signal_subscribe (connection, |
198 | PORTAL_BUS_NAME, |
199 | PORTAL_REQUEST_INTERFACE, |
200 | member: "Response" , |
201 | object_path: handle, |
202 | NULL, |
203 | flags: G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, |
204 | callback: portal_response_received, |
205 | user_data: picker, |
206 | NULL); |
207 | |
208 | g_free (mem: handle); |
209 | |
210 | g_variant_builder_init (builder: &options, G_VARIANT_TYPE_VARDICT); |
211 | g_variant_builder_add (builder: &options, format_string: "{sv}" , "handle_token" , g_variant_new_string (string: token)); |
212 | g_free (mem: token); |
213 | |
214 | g_dbus_proxy_call (proxy: picker->portal_proxy, |
215 | method_name: "PickColor" , |
216 | parameters: g_variant_new (format_string: "(sa{sv})" , "" , &options), |
217 | flags: 0, |
218 | timeout_msec: -1, |
219 | NULL, |
220 | NULL, |
221 | NULL); |
222 | } |
223 | |
224 | static GdkRGBA * |
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 |
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 | |