1 /*
2 * Copyright (C) 2021 Alberts Muktupāvels
3 *
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 2 of the License, or
7 * (at your option) any later version.
8 *
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.
13 *
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/>.
16 */
17
18 #define _GNU_SOURCE
19
20 #include "config.h"
21 #include "meta-request-csd.h"
22
23 #include <dlfcn.h>
24
25 typedef GType (* RegisterStaticSimple) (GType parent_type,
26 const gchar *type_name,
27 guint class_size,
28 GClassInitFunc class_init,
29 guint instance_size,
30 GInstanceInitFunc instance_init,
31 GTypeFlags flags);
32
33 typedef gint (* AddInstancePrivateFunc) (GType class_type,
34 gsize private_size);
35
36 typedef struct _GtkMnemnonicHash GtkMnemonicHash;
37 typedef struct _GtkCssNode GtkCssNode;
38
39 struct _GtkWindowPrivate
40 {
41 GtkMnemonicHash *mnemonic_hash;
42
43 GtkWidget *attach_widget;
44 GtkWidget *default_widget;
45 GtkWidget *initial_focus;
46 GtkWidget *focus_widget;
47 GtkWindow *transient_parent;
48 GtkWindowGeometryInfo *geometry_info;
49 GtkWindowGroup *group;
50 GdkScreen *screen;
51 GdkDisplay *display;
52 GtkApplication *application;
53
54 GList *popovers;
55
56 GdkModifierType mnemonic_modifier;
57
58 gchar *startup_id;
59 gchar *title;
60 gchar *wmclass_class;
61 gchar *wmclass_name;
62 gchar *wm_role;
63
64 guint keys_changed_handler;
65 guint delete_event_handler;
66
67 guint32 initial_timestamp;
68
69 guint16 configure_request_count;
70
71 guint mnemonics_display_timeout_id;
72
73 gint scale;
74
75 gint title_height;
76 GtkWidget *title_box;
77 GtkWidget *titlebar;
78 GtkWidget *popup_menu;
79
80 GdkWindow *border_window[8];
81 gint initial_fullscreen_monitor;
82 guint edge_constraints;
83
84 guint need_default_position : 1;
85 guint need_default_size : 1;
86
87 guint above_initially : 1;
88 guint accept_focus : 1;
89 guint below_initially : 1;
90 guint builder_visible : 1;
91 guint configure_notify_received : 1;
92 guint decorated : 1;
93 guint deletable : 1;
94 guint destroy_with_parent : 1;
95 guint focus_on_map : 1;
96 guint fullscreen_initially : 1;
97 guint has_focus : 1;
98 guint has_user_ref_count : 1;
99 guint has_toplevel_focus : 1;
100 guint hide_titlebar_when_maximized : 1;
101 guint iconify_initially : 1;
102 guint is_active : 1;
103 guint maximize_initially : 1;
104 guint mnemonics_visible : 1;
105 guint mnemonics_visible_set : 1;
106 guint focus_visible : 1;
107 guint modal : 1;
108 guint position : 3;
109 guint resizable : 1;
110 guint skips_pager : 1;
111 guint skips_taskbar : 1;
112 guint stick_initially : 1;
113 guint transient_parent_group : 1;
114 guint type : 4;
115 guint urgent : 1;
116 guint gravity : 5;
117 guint csd_requested : 1;
118 guint client_decorated : 1;
119 guint use_client_shadow : 1;
120 guint maximized : 1;
121 guint fullscreen : 1;
122 guint tiled : 1;
123 guint unlimited_guessed_size_x : 1;
124 guint unlimited_guessed_size_y : 1;
125 guint force_resize : 1;
126 guint fixate_size : 1;
127
128 guint use_subsurface : 1;
129
130 GdkWindowTypeHint type_hint;
131
132 GtkGesture *multipress_gesture;
133 GtkGesture *drag_gesture;
134
135 GdkWindow *hardcoded_window;
136
137 GtkCssNode *decoration_node;
138 };
139
140 static RegisterStaticSimple register_static_simple_orig_func = NULL;
141 static RegisterStaticSimple register_static_simple_func = NULL;
142 static GType gtk_window_type = 0;
143
144 static AddInstancePrivateFunc add_instance_private_orig_func = NULL;
145 static AddInstancePrivateFunc add_instance_private_func = NULL;
146 static gsize gtk_window_private_size = 0;
147
148 static GType
find_gtk_window_type(GType parent_type,const gchar * type_name,guint class_size,GClassInitFunc class_init,guint instance_size,GInstanceInitFunc instance_init,GTypeFlags flags)149 find_gtk_window_type (GType parent_type,
150 const gchar *type_name,
151 guint class_size,
152 GClassInitFunc class_init,
153 guint instance_size,
154 GInstanceInitFunc instance_init,
155 GTypeFlags flags)
156 {
157 GType type_id;
158
159 type_id = register_static_simple_orig_func (parent_type,
160 type_name,
161 class_size,
162 class_init,
163 instance_size,
164 instance_init,
165 flags);
166
167 if (g_strcmp0 (type_name, "GtkWindow") == 0)
168 {
169 register_static_simple_func = register_static_simple_orig_func;
170 gtk_window_type = type_id;
171 }
172
173 return type_id;
174 }
175
176 static gint
find_gtk_window_private_size(GType class_type,gsize private_size)177 find_gtk_window_private_size (GType class_type,
178 gsize private_size)
179 {
180 if (class_type == gtk_window_type)
181 {
182 add_instance_private_func = add_instance_private_orig_func;
183 gtk_window_private_size = private_size;
184 }
185
186 return add_instance_private_orig_func (class_type, private_size);
187 }
188
189 __attribute__((constructor))
190 static void
add_instance_private_init(void)191 add_instance_private_init (void)
192 {
193 void *func;
194
195 func = dlsym (RTLD_NEXT, "g_type_register_static_simple");
196 register_static_simple_orig_func = func;
197 register_static_simple_func = find_gtk_window_type;
198
199 func = dlsym (RTLD_NEXT, "g_type_add_instance_private");
200 add_instance_private_orig_func = func;
201 add_instance_private_func = find_gtk_window_private_size;
202 }
203
204 GType
g_type_register_static_simple(GType parent_type,const gchar * type_name,guint class_size,GClassInitFunc class_init,guint instance_size,GInstanceInitFunc instance_init,GTypeFlags flags)205 g_type_register_static_simple (GType parent_type,
206 const gchar *type_name,
207 guint class_size,
208 GClassInitFunc class_init,
209 guint instance_size,
210 GInstanceInitFunc instance_init,
211 GTypeFlags flags)
212 {
213 return register_static_simple_func (parent_type,
214 type_name,
215 class_size,
216 class_init,
217 instance_size,
218 instance_init,
219 flags);
220 }
221
222 gint
g_type_add_instance_private(GType class_type,gsize private_size)223 g_type_add_instance_private (GType class_type,
224 gsize private_size)
225 {
226 return add_instance_private_func (class_type, private_size);
227 }
228
229 static gboolean
check_gtk_window_private(void)230 check_gtk_window_private (void)
231 {
232 static gboolean ret = FALSE;
233 static gboolean checked = FALSE;
234
235 if (!checked)
236 {
237 GtkWindow *window;
238
239 if (gtk_window_private_size < sizeof (GtkWindowPrivate))
240 {
241 checked = TRUE;
242 return FALSE;
243 }
244
245 window = g_object_new (GTK_TYPE_WINDOW,
246 "type", GTK_WINDOW_POPUP,
247 "type-hint", GDK_WINDOW_TYPE_HINT_TOOLTIP,
248 NULL);
249
250 while (TRUE)
251 {
252 GtkWindowPrivate *priv;
253 GtkWidget *titlebar;
254
255 priv = window->priv;
256
257 if (priv->type != GTK_WINDOW_POPUP ||
258 priv->type_hint != GDK_WINDOW_TYPE_HINT_TOOLTIP)
259 break;
260
261 gtk_window_set_gravity (window, GDK_GRAVITY_STATIC);
262
263 if (priv->gravity != GDK_GRAVITY_STATIC)
264 break;
265
266 titlebar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
267 gtk_window_set_titlebar (window, titlebar);
268
269 if (priv->title_box != titlebar ||
270 !priv->client_decorated)
271 break;
272
273 if (priv->csd_requested)
274 break;
275
276 ret = TRUE;
277 break;
278 }
279
280 gtk_widget_destroy (GTK_WIDGET (window));
281 checked = TRUE;
282 }
283
284 return ret;
285 }
286
287 void
meta_request_csd(GtkWindow * window)288 meta_request_csd (GtkWindow *window)
289 {
290 if (!check_gtk_window_private ())
291 return;
292
293 window->priv->csd_requested = TRUE;
294 }
295