1 /*
2  * appmenu-gtk-module
3  * Copyright 2012 Canonical Ltd.
4  * Copyright (C) 2015-2017 Konstantin Pugin <ria.freelander@gmail.com>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors: Ryan Lortie <desrt@desrt.ca>
20  *          William Hua <william.hua@canonical.com>
21  *          Konstantin Pugin <ria.freelander@gmail.com>
22  *          Lester Carballo Perez <lestcape@gmail.com>
23  */
24 
25 #include "platform.h"
26 #include "consts.h"
27 #include "datastructs-private.h"
28 #include "datastructs.h"
29 
30 #ifdef GDK_WINDOWING_X11
gtk_widget_get_x11_property_string(GtkWidget * widget,const char * name)31 G_GNUC_INTERNAL char *gtk_widget_get_x11_property_string(GtkWidget *widget, const char *name)
32 {
33 	GdkWindow *window;
34 	GdkDisplay *display;
35 	Display *xdisplay;
36 	Window xwindow;
37 	Atom property;
38 	Atom actual_type;
39 	int actual_format;
40 	unsigned long nitems;
41 	unsigned long bytes_after;
42 	unsigned char *prop;
43 
44 	g_return_val_if_fail(GTK_IS_WIDGET(widget), NULL);
45 
46 	window   = gtk_widget_get_window(widget);
47 	display  = gdk_window_get_display(window);
48 	xdisplay = GDK_DISPLAY_XDISPLAY(display);
49 	xwindow  = GDK_WINDOW_XID(window);
50 
51 	property = None;
52 
53 	if (display != NULL)
54 		property = gdk_x11_get_xatom_by_name_for_display(display, name);
55 
56 	if (property == None)
57 		property = gdk_x11_get_xatom_by_name(name);
58 
59 	g_return_val_if_fail(property != None, NULL);
60 
61 	if (XGetWindowProperty(xdisplay,
62 	                       xwindow,
63 	                       property,
64 	                       0,
65 	                       G_MAXLONG,
66 	                       False,
67 	                       AnyPropertyType,
68 	                       &actual_type,
69 	                       &actual_format,
70 	                       &nitems,
71 	                       &bytes_after,
72 	                       &prop) == Success)
73 	{
74 		if (actual_format)
75 		{
76 			char *string = g_strdup((const char *)prop);
77 
78 			if (prop != NULL)
79 				XFree(prop);
80 
81 			return string;
82 		}
83 		else
84 			return NULL;
85 	}
86 
87 	return NULL;
88 }
89 
gtk_widget_set_x11_property_string(GtkWidget * widget,const char * name,const char * value)90 G_GNUC_INTERNAL void gtk_widget_set_x11_property_string(GtkWidget *widget, const char *name,
91                                                         const char *value)
92 {
93 	GdkWindow *window;
94 	GdkDisplay *display;
95 	Display *xdisplay;
96 	Window xwindow;
97 	Atom property;
98 	Atom type;
99 
100 	g_return_if_fail(GTK_IS_WIDGET(widget));
101 
102 	window   = gtk_widget_get_window(widget);
103 	display  = gdk_window_get_display(window);
104 	xdisplay = GDK_DISPLAY_XDISPLAY(display);
105 	xwindow  = GDK_WINDOW_XID(window);
106 
107 	property = None;
108 
109 	if (display != NULL)
110 		property = gdk_x11_get_xatom_by_name_for_display(display, name);
111 
112 	if (property == None)
113 		property = gdk_x11_get_xatom_by_name(name);
114 
115 	g_return_if_fail(property != None);
116 
117 	type = None;
118 
119 	if (display != NULL)
120 		type = gdk_x11_get_xatom_by_name_for_display(display, "UTF8_STRING");
121 
122 	if (type == None)
123 		type = gdk_x11_get_xatom_by_name("UTF8_STRING");
124 
125 	g_return_if_fail(type != None);
126 
127 	if (value != NULL)
128 		XChangeProperty(xdisplay,
129 		                xwindow,
130 		                property,
131 		                type,
132 		                8,
133 		                PropModeReplace,
134 		                (unsigned char *)value,
135 		                g_utf8_strlen(value, -1));
136 	else
137 		XDeleteProperty(xdisplay, xwindow, property);
138 }
139 
gtk_x11_window_get_window_data(GtkWindow * window)140 G_GNUC_INTERNAL WindowData *gtk_x11_window_get_window_data(GtkWindow *window)
141 {
142 	WindowData *window_data;
143 
144 	g_return_val_if_fail(GTK_IS_WINDOW(window), NULL);
145 
146 	window_data = g_object_get_qdata(G_OBJECT(window), window_data_quark());
147 
148 	if (window_data == NULL)
149 	{
150 		static guint window_id;
151 
152 		GDBusConnection *session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
153 		char *object_path        = g_strdup_printf(OBJECT_PATH "/%d", window_id);
154 		char *old_unique_bus_name =
155 		    gtk_widget_get_x11_property_string(GTK_WIDGET(window), _GTK_UNIQUE_BUS_NAME);
156 		char *old_unity_object_path =
157 		    gtk_widget_get_x11_property_string(GTK_WIDGET(window), _UNITY_OBJECT_PATH);
158 		char *old_menubar_object_path =
159 		    gtk_widget_get_x11_property_string(GTK_WIDGET(window),
160 		                                       _GTK_MENUBAR_OBJECT_PATH);
161 		GDBusActionGroup *old_action_group = NULL;
162 		GDBusMenuModel *old_menu_model     = NULL;
163 
164 		if (old_unique_bus_name != NULL)
165 		{
166 			if (old_unity_object_path != NULL)
167 				old_action_group = g_dbus_action_group_get(session,
168 				                                           old_unique_bus_name,
169 				                                           old_unity_object_path);
170 
171 			if (old_menubar_object_path != NULL)
172 				old_menu_model = g_dbus_menu_model_get(session,
173 				                                       old_unique_bus_name,
174 				                                       old_menubar_object_path);
175 		}
176 
177 		window_data             = window_data_new();
178 		window_data->window_id  = window_id++;
179 		window_data->menu_model = g_menu_new();
180 		window_data->action_group =
181 		    unity_gtk_action_group_new(G_ACTION_GROUP(old_action_group));
182 
183 		if (old_menu_model != NULL)
184 		{
185 			window_data->old_model = G_MENU_MODEL(g_object_ref(old_menu_model));
186 			g_menu_append_section(window_data->menu_model,
187 			                      NULL,
188 			                      G_MENU_MODEL(old_menu_model));
189 		}
190 
191 		window_data->menu_model_export_id =
192 		    g_dbus_connection_export_menu_model(session,
193 		                                        old_menubar_object_path != NULL
194 		                                            ? old_menubar_object_path
195 		                                            : object_path,
196 		                                        G_MENU_MODEL(window_data->menu_model),
197 		                                        NULL);
198 		window_data->action_group_export_id =
199 		    g_dbus_connection_export_action_group(session,
200 		                                          old_unity_object_path != NULL
201 		                                              ? old_unity_object_path
202 		                                              : object_path,
203 		                                          G_ACTION_GROUP(window_data->action_group),
204 		                                          NULL);
205 
206 		if (old_unique_bus_name == NULL)
207 			gtk_widget_set_x11_property_string(GTK_WIDGET(window),
208 			                                   _GTK_UNIQUE_BUS_NAME,
209 			                                   g_dbus_connection_get_unique_name(
210 			                                       session));
211 
212 		if (old_unity_object_path == NULL)
213 			gtk_widget_set_x11_property_string(GTK_WIDGET(window),
214 			                                   _UNITY_OBJECT_PATH,
215 			                                   object_path);
216 
217 		if (old_menubar_object_path == NULL)
218 			gtk_widget_set_x11_property_string(GTK_WIDGET(window),
219 			                                   _GTK_MENUBAR_OBJECT_PATH,
220 			                                   object_path);
221 
222 		g_object_set_qdata_full(G_OBJECT(window),
223 		                        window_data_quark(),
224 		                        window_data,
225 		                        window_data_free);
226 
227 		g_free(old_menubar_object_path);
228 		g_free(old_unity_object_path);
229 		g_free(old_unique_bus_name);
230 		g_free(object_path);
231 	}
232 
233 	return window_data;
234 }
235 #endif
236 #ifdef GDK_WINDOWING_WAYLAND
237 
238 #include <wayland-client.h>
239 
240 void gdk_wayland_window_set_dbus_properties_libgtk_only(
241     GdkWindow *window, const char *application_id, const char *app_menu_path,
242     const char *menubar_path, const char *window_object_path, const char *application_object_path,
243     const char *unique_bus_name);
244 
gtk_wayland_window_get_window_data(GtkWindow * window)245 G_GNUC_INTERNAL WindowData *gtk_wayland_window_get_window_data(GtkWindow *window)
246 {
247 	WindowData *window_data;
248 
249 	g_return_val_if_fail(GTK_IS_WINDOW(window), NULL);
250 
251 	window_data = g_object_get_qdata(G_OBJECT(window), window_data_quark());
252 	if (window_data == NULL)
253 	{
254 		static guint window_id;
255 		GMenuModel *old_menu_model         = NULL;
256 		GDBusActionGroup *old_action_group = NULL;
257 		GtkApplication *application;
258 		GApplication *gApp;
259 		GDBusConnection *connection;
260 
261 		char *unique_bus_name;
262 		char *object_path;
263 		char *menubar_object_path;
264 		char *application_id;
265 		char *application_object_path;
266 
267 		window_data             = window_data_new();
268 		window_data->menu_model = g_menu_new();
269 
270 		if (GTK_IS_APPLICATION_WINDOW(window))
271 		{
272 			char *unity_object_path;
273 
274 			application = gtk_window_get_application(window);
275 			g_return_val_if_fail(GTK_IS_APPLICATION(application), NULL);
276 
277 			window_data->action_group = NULL;
278 
279 			gApp = G_APPLICATION(application);
280 			g_return_val_if_fail(g_application_get_is_registered(gApp), NULL);
281 			g_return_val_if_fail(!g_application_get_is_remote(gApp), NULL);
282 			g_return_val_if_fail(window_data->menu_model == NULL ||
283 			                         G_IS_MENU_MODEL(window_data->menu_model),
284 			                     NULL);
285 
286 			application_id =
287 			    g_strdup_printf("%s", g_application_get_application_id(gApp));
288 			application_object_path =
289 			    g_strdup_printf("%s", g_application_get_dbus_object_path(gApp));
290 
291 			window_data->window_id = window_id++; // IN THE GNOME IMPLEMENTATION THIS IS
292 			                                      // STARTED IN ONE NOT CERO (So, we
293 			                                      // make is similar)
294 
295 			connection  = g_application_get_dbus_connection(gApp);
296 			object_path = g_strdup_printf(OBJECT_PATH "/%d", window_id);
297 
298 			unique_bus_name =
299 			    g_strdup_printf("%s", g_dbus_connection_get_unique_name(connection));
300 			unity_object_path =
301 			    g_strdup_printf("%s%s",
302 			                    g_application_get_dbus_object_path(gApp) != NULL
303 			                        ? g_application_get_dbus_object_path(gApp)
304 			                        : object_path,
305 			                    g_application_get_dbus_object_path(gApp) != NULL
306 			                        ? "/menus/menubar"
307 			                        : "");
308 			menubar_object_path = g_strdup_printf("%s", unity_object_path);
309 
310 			old_menu_model = G_MENU_MODEL(gtk_application_get_menubar(application));
311 			if (old_menu_model != NULL)
312 			{
313 				old_action_group       = g_dbus_action_group_get(connection,
314                                                                            unique_bus_name,
315                                                                            unity_object_path);
316 				window_data->old_model = g_object_ref(old_menu_model);
317 				g_menu_append_section(window_data->menu_model,
318 				                      NULL,
319 				                      old_menu_model);
320 			}
321 
322 			// Set the actions
323 			window_data->action_group =
324 			    unity_gtk_action_group_new(G_ACTION_GROUP(old_action_group));
325 			window_data->action_group_export_id =
326 			    g_dbus_connection_export_action_group(connection,
327 			                                          unity_object_path,
328 			                                          G_ACTION_GROUP(
329 			                                              window_data->action_group),
330 			                                          NULL);
331 
332 			// Set the menubar
333 			gtk_application_set_menubar(GTK_APPLICATION(application),
334 			                            G_MENU_MODEL(window_data->menu_model));
335 
336 			g_free(unity_object_path);
337 		}
338 		else
339 		{
340 			GdkWindow *gdk_win;
341 			const char *app_menu_path = NULL;
342 
343 			window_data->window_id = window_id++;
344 
345 			connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
346 			unique_bus_name =
347 			    g_strdup_printf("%s", g_dbus_connection_get_unique_name(connection));
348 			gdk_win     = gtk_widget_get_window(GTK_WIDGET(window));
349 			application = gtk_window_get_application(window);
350 
351 			old_menu_model = G_MENU_MODEL(window_data->menu_model);
352 
353 			window_data->action_group =
354 			    unity_gtk_action_group_new(G_ACTION_GROUP(old_action_group));
355 
356 			if (GTK_IS_APPLICATION(application))
357 			{
358 				gApp = G_APPLICATION(application);
359 				application_id =
360 				    g_strdup_printf("%s", g_application_get_application_id(gApp));
361 				object_path =
362 				    g_strdup_printf("%s/menus/menubar/%d",
363 				                    g_application_get_dbus_object_path(gApp),
364 				                    window_data->window_id);
365 				application_object_path =
366 				    g_strdup_printf("%s", g_application_get_dbus_object_path(gApp));
367 				menubar_object_path = g_strdup_printf("%s/window/%d",
368 				                                      object_path,
369 				                                      window_data->window_id);
370 			}
371 			else
372 			{
373 				application_id          = g_strdup_printf("%s",
374                                                                  g_get_prgname() != NULL
375                                                                      ? g_get_prgname()
376                                                                      : gdk_get_program_class());
377 				object_path             = g_strdup_printf("%s/menus/menubar/%d",
378                                                               OBJECT_PATH,
379                                                               window_data->window_id);
380 				application_object_path = g_strdup_printf("%s", OBJECT_PATH);
381 				menubar_object_path     = g_strdup_printf("%s/window/%d",
382                                                                       object_path,
383                                                                       window_data->window_id);
384 			}
385 
386 			window_data->menu_model_export_id =
387 			    g_dbus_connection_export_menu_model(connection,
388 			                                        object_path,
389 			                                        G_MENU_MODEL(
390 			                                            window_data->menu_model),
391 			                                        NULL);
392 			window_data->action_group_export_id =
393 			    g_dbus_connection_export_action_group(connection,
394 			                                          object_path,
395 			                                          G_ACTION_GROUP(
396 			                                              window_data->action_group),
397 			                                          NULL);
398 
399 			gdk_wayland_window_set_dbus_properties_libgtk_only(gdk_win,
400 			                                                   application_id,
401 			                                                   app_menu_path,
402 			                                                   object_path,
403 			                                                   menubar_object_path,
404 			                                                   application_object_path,
405 			                                                   unique_bus_name);
406 		}
407 		g_free(unique_bus_name);
408 		g_free(object_path);
409 		g_free(menubar_object_path);
410 		g_free(application_id);
411 		g_free(application_object_path);
412 		g_object_set_qdata_full(G_OBJECT(window),
413 		                        window_data_quark(),
414 		                        window_data,
415 		                        window_data_free);
416 	}
417 	return window_data;
418 }
419 #endif
420