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