/*
* Copyright (C) 2021 Alberts Muktupāvels
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#define _GNU_SOURCE
#include "config.h"
#include "meta-request-csd.h"
#include
typedef GType (* RegisterStaticSimple) (GType parent_type,
const gchar *type_name,
guint class_size,
GClassInitFunc class_init,
guint instance_size,
GInstanceInitFunc instance_init,
GTypeFlags flags);
typedef gint (* AddInstancePrivateFunc) (GType class_type,
gsize private_size);
typedef struct _GtkMnemnonicHash GtkMnemonicHash;
typedef struct _GtkCssNode GtkCssNode;
struct _GtkWindowPrivate
{
GtkMnemonicHash *mnemonic_hash;
GtkWidget *attach_widget;
GtkWidget *default_widget;
GtkWidget *initial_focus;
GtkWidget *focus_widget;
GtkWindow *transient_parent;
GtkWindowGeometryInfo *geometry_info;
GtkWindowGroup *group;
GdkScreen *screen;
GdkDisplay *display;
GtkApplication *application;
GList *popovers;
GdkModifierType mnemonic_modifier;
gchar *startup_id;
gchar *title;
gchar *wmclass_class;
gchar *wmclass_name;
gchar *wm_role;
guint keys_changed_handler;
guint delete_event_handler;
guint32 initial_timestamp;
guint16 configure_request_count;
guint mnemonics_display_timeout_id;
gint scale;
gint title_height;
GtkWidget *title_box;
GtkWidget *titlebar;
GtkWidget *popup_menu;
GdkWindow *border_window[8];
gint initial_fullscreen_monitor;
guint edge_constraints;
guint need_default_position : 1;
guint need_default_size : 1;
guint above_initially : 1;
guint accept_focus : 1;
guint below_initially : 1;
guint builder_visible : 1;
guint configure_notify_received : 1;
guint decorated : 1;
guint deletable : 1;
guint destroy_with_parent : 1;
guint focus_on_map : 1;
guint fullscreen_initially : 1;
guint has_focus : 1;
guint has_user_ref_count : 1;
guint has_toplevel_focus : 1;
guint hide_titlebar_when_maximized : 1;
guint iconify_initially : 1;
guint is_active : 1;
guint maximize_initially : 1;
guint mnemonics_visible : 1;
guint mnemonics_visible_set : 1;
guint focus_visible : 1;
guint modal : 1;
guint position : 3;
guint resizable : 1;
guint skips_pager : 1;
guint skips_taskbar : 1;
guint stick_initially : 1;
guint transient_parent_group : 1;
guint type : 4;
guint urgent : 1;
guint gravity : 5;
guint csd_requested : 1;
guint client_decorated : 1;
guint use_client_shadow : 1;
guint maximized : 1;
guint fullscreen : 1;
guint tiled : 1;
guint unlimited_guessed_size_x : 1;
guint unlimited_guessed_size_y : 1;
guint force_resize : 1;
guint fixate_size : 1;
guint use_subsurface : 1;
GdkWindowTypeHint type_hint;
GtkGesture *multipress_gesture;
GtkGesture *drag_gesture;
GdkWindow *hardcoded_window;
GtkCssNode *decoration_node;
};
static RegisterStaticSimple register_static_simple_orig_func = NULL;
static RegisterStaticSimple register_static_simple_func = NULL;
static GType gtk_window_type = 0;
static AddInstancePrivateFunc add_instance_private_orig_func = NULL;
static AddInstancePrivateFunc add_instance_private_func = NULL;
static gsize gtk_window_private_size = 0;
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)
{
GType type_id;
type_id = register_static_simple_orig_func (parent_type,
type_name,
class_size,
class_init,
instance_size,
instance_init,
flags);
if (g_strcmp0 (type_name, "GtkWindow") == 0)
{
register_static_simple_func = register_static_simple_orig_func;
gtk_window_type = type_id;
}
return type_id;
}
static gint
find_gtk_window_private_size (GType class_type,
gsize private_size)
{
if (class_type == gtk_window_type)
{
add_instance_private_func = add_instance_private_orig_func;
gtk_window_private_size = private_size;
}
return add_instance_private_orig_func (class_type, private_size);
}
__attribute__((constructor))
static void
add_instance_private_init (void)
{
void *func;
func = dlsym (RTLD_NEXT, "g_type_register_static_simple");
register_static_simple_orig_func = func;
register_static_simple_func = find_gtk_window_type;
func = dlsym (RTLD_NEXT, "g_type_add_instance_private");
add_instance_private_orig_func = func;
add_instance_private_func = find_gtk_window_private_size;
}
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)
{
return register_static_simple_func (parent_type,
type_name,
class_size,
class_init,
instance_size,
instance_init,
flags);
}
gint
g_type_add_instance_private (GType class_type,
gsize private_size)
{
return add_instance_private_func (class_type, private_size);
}
static gboolean
check_gtk_window_private (void)
{
static gboolean ret = FALSE;
static gboolean checked = FALSE;
if (!checked)
{
GtkWindow *window;
if (gtk_window_private_size < sizeof (GtkWindowPrivate))
{
checked = TRUE;
return FALSE;
}
window = g_object_new (GTK_TYPE_WINDOW,
"type", GTK_WINDOW_POPUP,
"type-hint", GDK_WINDOW_TYPE_HINT_TOOLTIP,
NULL);
while (TRUE)
{
GtkWindowPrivate *priv;
GtkWidget *titlebar;
priv = window->priv;
if (priv->type != GTK_WINDOW_POPUP ||
priv->type_hint != GDK_WINDOW_TYPE_HINT_TOOLTIP)
break;
gtk_window_set_gravity (window, GDK_GRAVITY_STATIC);
if (priv->gravity != GDK_GRAVITY_STATIC)
break;
titlebar = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_window_set_titlebar (window, titlebar);
if (priv->title_box != titlebar ||
!priv->client_decorated)
break;
if (priv->csd_requested)
break;
ret = TRUE;
break;
}
gtk_widget_destroy (GTK_WIDGET (window));
checked = TRUE;
}
return ret;
}
void
meta_request_csd (GtkWindow *window)
{
if (!check_gtk_window_private ())
return;
window->priv->csd_requested = TRUE;
}