Panel
/* $Id$ */
/* Copyright (c) 2011-2020 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Panel */
/* 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, version 3 of the License.
*
* 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 <http://www.gnu.org/licenses/>. */
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libintl.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#include <System.h>
#include "Panel/applet.h"
#define _(string) gettext(string)
#define N_(string) string
/* Title */
/* private */
/* types */
typedef struct _PanelApplet
{
PanelAppletHelper * helper;
GtkWidget * widget;
gulong source;
GdkDisplay * display;
GdkScreen * screen;
GdkWindow * root;
Atom atom_active;
Atom atom_name;
Atom atom_utf8_string;
Atom atom_visible_name;
} Title;
/* prototypes */
/* title */
static Title * _title_init(PanelAppletHelper * helper, GtkWidget ** widget);
static void _title_destroy(Title * title);
/* accessors */
static int _title_get_text_property(Title * title, Window window, Atom property,
char ** ret);
/* useful */
static void _title_do(Title * title);
/* callbacks */
static GdkFilterReturn _title_on_filter(GdkXEvent * xevent, GdkEvent * event,
gpointer data);
static void _title_on_screen_changed(GtkWidget * widget, GdkScreen * previous,
gpointer data);
/* public */
/* variables */
PanelAppletDefinition applet =
{
N_("Title"),
NULL,
NULL,
_title_init,
_title_destroy,
NULL,
FALSE,
TRUE
};
/* private */
/* functions */
/* title_init */
static Title * _title_init(PanelAppletHelper * helper, GtkWidget ** widget)
{
Title * title;
PangoFontDescription * bold;
if((title = malloc(sizeof(*title))) == NULL)
{
error_set("%s: %s", applet.name, strerror(errno));
return NULL;
}
title->helper = helper;
bold = pango_font_description_new();
pango_font_description_set_weight(bold, PANGO_WEIGHT_BOLD);
title->widget = gtk_label_new("");
#if GTK_CHECK_VERSION(3, 0, 0)
gtk_widget_override_font(title->widget, bold);
#else
gtk_widget_modify_font(title->widget, bold);
#endif
pango_font_description_free(bold);
title->source = g_signal_connect(title->widget, "screen-changed",
G_CALLBACK(_title_on_screen_changed), title);
title->display = NULL;
title->screen = NULL;
title->root = NULL;
title->atom_active = 0;
title->atom_name = 0;
title->atom_visible_name = 0;
gtk_widget_show(title->widget);
*widget = title->widget;
return title;
}
/* title_destroy */
static void _title_destroy(Title * title)
{
if(title->source != 0)
g_signal_handler_disconnect(title->widget, title->source);
title->source = 0;
if(title->root != NULL)
gdk_window_remove_filter(title->root, _title_on_filter, title);
gtk_widget_destroy(title->widget);
free(title);
}
/* accessors */
/* title_get_window_property */
static int _title_get_window_property(Title * title, Window window,
Atom property, Atom atom, unsigned long * cnt,
unsigned char ** ret)
{
int res;
Atom type;
int format;
unsigned long bytes;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(title, window, %lu, %lu)\n", __func__,
property, atom);
#endif
gdk_error_trap_push();
res = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(title->display), window,
property, 0, G_MAXLONG, False, atom, &type, &format,
cnt, &bytes, ret);
if(gdk_error_trap_pop() != 0 || res != Success)
return 1;
if(type != atom)
{
if(*ret != NULL)
XFree(*ret);
*ret = NULL;
return 1;
}
return 0;
}
/* title_get_text_property */
static int _title_get_text_property(Title * title, Window window, Atom property,
char ** ret)
{
int res;
XTextProperty text;
GdkAtom atom;
int cnt;
char ** list;
int i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(title, window, %lu)\n", __func__, property);
#endif
gdk_error_trap_push();
res = XGetTextProperty(GDK_DISPLAY_XDISPLAY(title->display), window,
&text, property);
if(gdk_error_trap_pop() != 0 || res == 0)
return 1;
atom = gdk_x11_xatom_to_atom(text.encoding);
#if GTK_CHECK_VERSION(2, 24, 0)
cnt = gdk_x11_display_text_property_to_text_list(title->display,
atom, text.format, text.value, text.nitems, &list);
#else
cnt = gdk_text_property_to_utf8_list(atom, text.format, text.value,
text.nitems, &list);
#endif
if(cnt > 0)
{
*ret = list[0];
for(i = 1; i < cnt; i++)
g_free(list[i]);
g_free(list);
}
else
*ret = NULL;
if(text.value != NULL)
XFree(text.value);
return 0;
}
/* title_do */
static char * _do_name(Title * title, Window window);
static char * _do_name_text(Title * title, Window window, Atom property);
static char * _do_name_utf8(Title * title, Window window, Atom property);
static void _title_do(Title * title)
{
unsigned long cnt = 0;
Window * window;
char * name;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(_title_get_window_property(title, GDK_WINDOW_XID(title->root),
title->atom_active, XA_WINDOW, &cnt,
(void*)&window) != 0 || cnt != 1)
{
gtk_label_set_text(GTK_LABEL(title->widget), "");
return;
}
name = _do_name(title, *window);
XFree(window);
gtk_label_set_text(GTK_LABEL(title->widget), (name != NULL)
? name : "");
free(name);
}
static char * _do_name(Title * title, Window window)
{
char * ret;
if((ret = _do_name_utf8(title, window, title->atom_visible_name))
!= NULL)
return ret;
if((ret = _do_name_utf8(title, window, title->atom_name)) != NULL)
return ret;
if((ret = _do_name_text(title, window, XA_WM_NAME)) != NULL)
return ret;
return g_strdup(_("(Untitled)"));
}
static char * _do_name_text(Title * title, Window window, Atom property)
{
char * ret = NULL;
if(_title_get_text_property(title, window, property, (void*)&ret) != 0)
return NULL;
return ret;
}
static char * _do_name_utf8(Title * title, Window window, Atom property)
{
char * ret = NULL;
char * str = NULL;
unsigned long cnt = 0;
if(_title_get_window_property(title, window, property,
title->atom_utf8_string, &cnt, (void*)&str)
!= 0)
return NULL;
if(g_utf8_validate(str, cnt, NULL))
ret = g_strndup(str, cnt);
XFree(str);
return ret;
}
/* callbacks */
/* title_on_filter */
static GdkFilterReturn _title_on_filter(GdkXEvent * xevent, GdkEvent * event,
gpointer data)
{
Title * title = data;
XEvent * xev = xevent;
(void) event;
if(xev->type != PropertyNotify)
return GDK_FILTER_CONTINUE;
if(xev->xproperty.atom != title->atom_active)
return GDK_FILTER_CONTINUE;
_title_do(title);
return GDK_FILTER_CONTINUE;
}
/* title_on_screen_changed */
static void _title_on_screen_changed(GtkWidget * widget, GdkScreen * previous,
gpointer data)
{
Title * title = data;
GdkEventMask events;
(void) previous;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(title->root != NULL)
gdk_window_remove_filter(title->root, _title_on_filter, title);
title->screen = gtk_widget_get_screen(widget);
title->display = gdk_screen_get_display(title->screen);
title->root = gdk_screen_get_root_window(title->screen);
events = gdk_window_get_events(title->root);
gdk_window_set_events(title->root, events
| GDK_PROPERTY_CHANGE_MASK);
gdk_window_add_filter(title->root, _title_on_filter, title);
title->atom_active = gdk_x11_get_xatom_by_name_for_display(
title->display, "_NET_ACTIVE_WINDOW");
title->atom_name = gdk_x11_get_xatom_by_name_for_display(
title->display, "_NET_WM_NAME");
title->atom_utf8_string = gdk_x11_get_xatom_by_name_for_display(
title->display, "UTF8_STRING");
title->atom_visible_name = gdk_x11_get_xatom_by_name_for_display(
title->display, "_NET_WM_VISIBLE_NAME");
_title_do(title);
}