#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <X11/Xlib.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include "xapp-gtk-window.h"
struct _XAppGtkWindowPrivate
{
gchar *icon_name;
gchar *icon_path;
gboolean need_set_at_realize;
};
G_DEFINE_TYPE (XAppGtkWindow, xapp_gtk_window, GTK_TYPE_WINDOW);
static void
clear_strings (XAppGtkWindow *window)
{
XAppGtkWindowPrivate *priv = window->priv;
g_clear_pointer (&priv->icon_name, g_free);
g_clear_pointer (&priv->icon_path, g_free);
}
static void
set_window_hint (GtkWidget *widget,
const gchar *str)
{
GdkDisplay *display;
GdkWindow *window;
window = gtk_widget_get_window (widget);
if (gdk_window_get_effective_toplevel (window) != window)
{
g_warning ("Window is not toplevel");
return;
}
display = gdk_window_get_display (window);
if (str != NULL)
{
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XID (window),
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_XAPP_ICON_NAME"),
gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
PropModeReplace, (guchar *) str, strlen (str));
}
else
{
XDeleteProperty (GDK_DISPLAY_XDISPLAY (display),
GDK_WINDOW_XID (window),
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_XAPP_ICON_NAME"));
}
}
static void
update_window (XAppGtkWindow *window)
{
XAppGtkWindowPrivate *priv = window->priv;
if (priv->icon_name != NULL)
{
set_window_hint (GTK_WIDGET (window), priv->icon_name);
}
else if (priv->icon_path != NULL)
{
set_window_hint (GTK_WIDGET (window), priv->icon_path);
}
}
static void
xapp_gtk_window_realize (GtkWidget *widget)
{
XAppGtkWindow *window = XAPP_GTK_WINDOW (widget);
XAppGtkWindowPrivate *priv = window->priv;
GTK_WIDGET_CLASS (xapp_gtk_window_parent_class)->realize (widget);
if (priv->need_set_at_realize)
{
update_window (window);
priv->need_set_at_realize = FALSE;
}
}
static void
xapp_gtk_window_unrealize (GtkWidget *widget)
{
XAppGtkWindow *window = XAPP_GTK_WINDOW (widget);
XAppGtkWindowPrivate *priv = window->priv;
GTK_WIDGET_CLASS (xapp_gtk_window_parent_class)->unrealize (widget);
if (priv->icon_name != NULL || priv->icon_path != NULL)
{
priv->need_set_at_realize = TRUE;
}
}
static void
xapp_gtk_window_finalize (GObject *object)
{
XAppGtkWindow *window = XAPP_GTK_WINDOW (object);
XAppGtkWindowPrivate *priv = window->priv;
g_clear_pointer (&priv->icon_name, g_free);
g_clear_pointer (&priv->icon_path, g_free);
G_OBJECT_CLASS (xapp_gtk_window_parent_class)->finalize (object);
}
static void
xapp_gtk_window_init (XAppGtkWindow *window)
{
XAppGtkWindowPrivate *priv;
window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, XAPP_TYPE_GTK_WINDOW, XAppGtkWindowPrivate);
priv = window->priv;
priv->icon_name = NULL;
priv->icon_path = NULL;
priv->need_set_at_realize = FALSE;
}
static void
xapp_gtk_window_class_init (XAppGtkWindowClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
gobject_class->finalize = xapp_gtk_window_finalize;
wclass->realize = xapp_gtk_window_realize;
wclass->unrealize = xapp_gtk_window_unrealize;
g_type_class_add_private (gobject_class, sizeof (XAppGtkWindowPrivate));
}
XAppGtkWindow *
xapp_gtk_window_new (void)
{
return g_object_new (XAPP_TYPE_GTK_WINDOW, NULL);
}
/**
* xapp_gtk_window_set_icon_name:
* @window: The #GtkWindow to set the icon name for
* @icon_name: (nullable): The icon name or path to set, or %NULL to unset.
*
* Sets the icon name hint for a window manager (like muffin) to make
* available when applications want to change their icons during runtime
* without having to resort to the internal low-res pixbufs that GdkWindow
* sets on the client side. This also chains up and calls GtkWindow.set_icon_name
* for convenience and compatibility. Set to %NULL to unset.
*/
void
xapp_gtk_window_set_icon_name (XAppGtkWindow *window,
const gchar *icon_name)
{
g_return_if_fail (XAPP_IS_GTK_WINDOW (window));
clear_strings (window);
if (icon_name != NULL)
{
window->priv->icon_name = g_strdup (icon_name);
}
/* If the window is realized, set the icon name immediately */
if (gtk_widget_get_realized (GTK_WIDGET (window)))
{
update_window (window);
}
/* Otherwise, remind ourselves to do it in our realize vfunc */
else
{
window->priv->need_set_at_realize = TRUE;
}
/* Call the GtkWindow method for compatibility */
gtk_window_set_icon_name (GTK_WINDOW (window), icon_name);
}
/**
* xapp_gtk_window_set_icon_from_file:
* @window: The #XAppGtkWindow to set the icon name for
* @file_name: (nullable): The icon path to set, or %NULL to unset.
* @error: (nullable): An error to set if something goes wrong.
*
* Sets the icon name hint for a window manager (like muffin) to make
* available when applications want to change their icons during runtime
* without having to resort to the internal low-res pixbufs that GdkWindow
* sets on the client side. This also chains up and calls GtkWindow.set_icon_from_file
* for convenience and compatibility. Set to %NULL to unset.
*/
void
xapp_gtk_window_set_icon_from_file (XAppGtkWindow *window,
const gchar *file_name,
GError **error)
{
g_return_if_fail (XAPP_IS_GTK_WINDOW (window));
clear_strings (window);
if (file_name != NULL)
{
window->priv->icon_path = g_strdup (file_name);
}
/* If the window is realized, set the icon path immediately */
if (gtk_widget_get_realized (GTK_WIDGET (window)))
{
update_window (window);
}
/* Otherwise, remind ourselves to do it later */
else
{
window->priv->need_set_at_realize = TRUE;
}
gtk_window_set_icon_from_file (GTK_WINDOW (window), file_name, error);
}
/* Wrappers (for GtkWindow subclasses like GtkDialog)
* window must be a GtkWindow or descendant */
static void
on_gtk_window_realized (GtkWidget *widget,
gpointer user_data)
{
gchar *int_string;
g_return_if_fail (GTK_IS_WIDGET (widget));
int_string = (gchar *) user_data;
g_signal_handlers_disconnect_by_func (widget, on_gtk_window_realized, int_string);
g_object_weak_unref (G_OBJECT (widget), (GWeakNotify) g_free, int_string);
set_window_hint (widget, int_string);
g_free (int_string);
}
/**
* xapp_set_window_icon_name:
* @window: The #GtkWindow to set the icon name for
* @icon_name: (nullable): The icon name to set, or %NULL to unset.
*
* Sets the icon name hint for a window manager (like muffin) to make
* available when applications want to change their icons during runtime
* without having to resort to the internal low-res pixbufs that GdkWindow
* sets on the client side. This is a function, not a method, for taking
* advantage of this feature with descendants of GtkWindows, such as
* GtkDialogs. Sets gtk_window_set_icon_name as well, to avoid needing
* to have two calls each time. Set to %NULL to unset.
*/
void
xapp_set_window_icon_name (GtkWindow *window,
const gchar *icon_name)
{
g_return_if_fail (GTK_IS_WINDOW (window));
if (XAPP_IS_GTK_WINDOW (window))
{
g_warning("Window is an instance of XAppGtkWindow. Use the instance set_icon_name method instead.");
}
/* If the window is realized, set the icon name immediately */
if (gtk_widget_get_realized (GTK_WIDGET (window)))
{
set_window_hint (GTK_WIDGET (window), icon_name);
}
/* Otherwise, hang a callback on window's realize signal and do it then */
else
{
gchar *int_string;
int_string = g_strdup (icon_name);
g_signal_connect_after (GTK_WIDGET (window),
"realize",
G_CALLBACK (on_gtk_window_realized),
int_string);
/* Insurance, in case window gets destroyed without ever being realized */
g_object_weak_ref (G_OBJECT (window),
(GWeakNotify) g_free,
int_string);
}
/* Call the GtkWindow method for compatibility */
gtk_window_set_icon_name (GTK_WINDOW (window), icon_name);
}
/**
* xapp_set_window_icon_from_file:
* @window: The #GtkWindow to set the icon name for
* @file_name: (nullable): The icon path to set, or %NULL to unset.
* @error: (nullable): An error to set if something goes wrong.
*
* Sets the icon name hint for a window manager (like muffin) to make
* available when applications want to change their icons during runtime
* without having to resort to the internal low-res pixbufs that GdkWindow
* sets on the client side. This also chains up and calls GtkWindow.set_icon_from_file
* for convenience and compatibility. Set to %NULL to unset.
*/
void
xapp_set_window_icon_from_file (GtkWindow *window,
const gchar *file_name,
GError **error)
{
g_return_if_fail (GTK_IS_WINDOW (window));
if (XAPP_IS_GTK_WINDOW (window))
{
g_warning("Window is an instance of XAppGtkWindow. Use the instance set_icon_from_file method instead.");
}
/* If the window is realized, set the icon name immediately */
if (gtk_widget_get_realized (GTK_WIDGET (window)))
{
set_window_hint (GTK_WIDGET (window), file_name);
}
/* Otherwise, hang a callback on window's realize signal and do it then */
else
{
gchar *int_string;
int_string = g_strdup (file_name);
g_signal_connect_after (GTK_WIDGET (window),
"realize",
G_CALLBACK (on_gtk_window_realized),
int_string);
/* Insurance, in case window gets destroyed without ever being realized */
g_object_weak_ref (G_OBJECT (window),
(GWeakNotify) g_free,
int_string);
}
/* Call the GtkWindow method for compatibility */
gtk_window_set_icon_from_file (GTK_WINDOW (window), file_name, error);
}