mutter/src/xprops.c

684 lines
19 KiB
C
Raw Normal View History

/* Metacity X property convenience routines */
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2002 Red Hat Inc.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <config.h>
#include "xprops.h"
#include "errors.h"
#include "util.h"
#include "async-getprop.h"
#include <X11/Xatom.h>
#include <string.h>
typedef struct
{
MetaDisplay *display;
Window xwindow;
Atom xatom;
Atom type;
int format;
unsigned long n_items;
unsigned long bytes_after;
unsigned char *prop;
} GetPropertyResults;
static gboolean
validate_or_free_results (GetPropertyResults *results,
int expected_format,
Atom expected_type,
gboolean must_have_items)
{
char *type_name;
char *expected_name;
char *prop_name;
if (expected_format == results->format &&
expected_type == results->type &&
(!must_have_items || results->n_items > 0))
return TRUE;
meta_error_trap_push (results->display);
type_name = XGetAtomName (results->display->xdisplay, results->type);
expected_name = XGetAtomName (results->display->xdisplay, expected_type);
prop_name = XGetAtomName (results->display->xdisplay, results->xatom);
meta_error_trap_pop (results->display, TRUE);
meta_warning (_("Window 0x%lx has property %s that was expected to have type %s format %d and actually has type %s format %d n_items %d\n"),
results->xwindow,
prop_name ? prop_name : "(bad atom)",
expected_name ? expected_name : "(bad atom)",
expected_format,
type_name ? type_name : "(bad atom)",
results->format, (int) results->n_items);
if (type_name)
XFree (type_name);
if (expected_name)
XFree (expected_name);
if (prop_name)
XFree (prop_name);
if (results->prop)
{
XFree (results->prop);
results->prop = NULL;
}
return FALSE;
}
static gboolean
get_property (MetaDisplay *display,
Window xwindow,
Atom xatom,
Atom req_type,
GetPropertyResults *results)
{
results->display = display;
results->xwindow = xwindow;
results->xatom = xatom;
results->prop = NULL;
results->n_items = 0;
results->type = None;
results->bytes_after = 0;
results->format = 0;
meta_error_trap_push_with_return (display);
if (XGetWindowProperty (display->xdisplay, xwindow, xatom,
0, G_MAXLONG,
False, req_type, &results->type, &results->format,
&results->n_items,
&results->bytes_after,
(guchar **)&results->prop) != Success ||
results->type == None)
{
if (results->prop)
XFree (results->prop);
meta_error_trap_pop_with_return (display, TRUE);
return FALSE;
}
if (meta_error_trap_pop_with_return (display, TRUE) != Success)
{
if (results->prop)
XFree (results->prop);
return FALSE;
}
return TRUE;
}
static gboolean
atom_list_from_results (GetPropertyResults *results,
Atom **atoms_p,
int *n_atoms_p)
{
if (!validate_or_free_results (results, 32, XA_ATOM, FALSE))
return FALSE;
*atoms_p = (Atom*) results->prop;
*n_atoms_p = results->n_items;
results->prop = NULL;
return TRUE;
}
gboolean
meta_prop_get_atom_list (MetaDisplay *display,
Window xwindow,
Atom xatom,
Atom **atoms_p,
int *n_atoms_p)
{
GetPropertyResults results;
*atoms_p = NULL;
*n_atoms_p = 0;
if (!get_property (display, xwindow, xatom, XA_ATOM,
&results))
return FALSE;
return atom_list_from_results (&results, atoms_p, n_atoms_p);
}
static gboolean
cardinal_list_from_results (GetPropertyResults *results,
gulong **cardinals_p,
int *n_cardinals_p)
{
if (!validate_or_free_results (results, 32, XA_CARDINAL, FALSE))
return FALSE;
*cardinals_p = (gulong*) results->prop;
*n_cardinals_p = results->n_items;
results->prop = NULL;
return TRUE;
}
gboolean
meta_prop_get_cardinal_list (MetaDisplay *display,
Window xwindow,
Atom xatom,
gulong **cardinals_p,
int *n_cardinals_p)
{
GetPropertyResults results;
*cardinals_p = NULL;
*n_cardinals_p = 0;
if (!get_property (display, xwindow, xatom, XA_CARDINAL,
&results))
return FALSE;
return cardinal_list_from_results (&results, cardinals_p, n_cardinals_p);
}
static gboolean
motif_hints_from_results (GetPropertyResults *results,
MotifWmHints **hints_p)
{
int real_size, max_size;
#define MAX_ITEMS sizeof (MotifWmHints)/sizeof (gulong)
*hints_p = NULL;
if (results->type == None || results->n_items <= 0)
{
meta_verbose ("Motif hints had unexpected type or n_items\n");
if (results->prop)
{
XFree (results->prop);
results->prop = NULL;
}
return FALSE;
}
/* The issue here is that some old crufty code will set a smaller
* MotifWmHints than the one we expect, apparently. I'm not sure of
* the history behind it. See bug #89841 for example.
*/
*hints_p = ag_Xmalloc (sizeof (MotifWmHints));
if (*hints_p == NULL)
{
if (results->prop)
{
XFree (results->prop);
results->prop = NULL;
}
return FALSE;
}
real_size = results->n_items * sizeof (gulong);
max_size = MAX_ITEMS * sizeof (gulong);
memcpy (*hints_p, results->prop, MIN (real_size, max_size));
if (results->prop)
{
XFree (results->prop);
results->prop = NULL;
}
return TRUE;
}
gboolean
meta_prop_get_motif_hints (MetaDisplay *display,
Window xwindow,
Atom xatom,
MotifWmHints **hints_p)
{
GetPropertyResults results;
*hints_p = NULL;
if (!get_property (display, xwindow, xatom, AnyPropertyType,
&results))
return FALSE;
return motif_hints_from_results (&results, hints_p);
}
static gboolean
latin1_string_from_results (GetPropertyResults *results,
char **str_p)
{
*str_p = NULL;
if (!validate_or_free_results (results, 8, XA_STRING, FALSE))
return FALSE;
*str_p = (char*) results->prop;
results->prop = NULL;
return TRUE;
}
gboolean
meta_prop_get_latin1_string (MetaDisplay *display,
Window xwindow,
Atom xatom,
char **str_p)
{
GetPropertyResults results;
*str_p = NULL;
if (!get_property (display, xwindow, xatom, XA_STRING,
&results))
return FALSE;
return latin1_string_from_results (&results, str_p);
}
static gboolean
utf8_string_from_results (GetPropertyResults *results,
char **str_p)
{
*str_p = NULL;
if (!validate_or_free_results (results, 8,
results->display->atom_utf8_string, FALSE))
return FALSE;
if (results->n_items > 0 &&
!g_utf8_validate (results->prop, results->n_items, NULL))
{
char *name;
name = XGetAtomName (results->display->xdisplay, results->xatom);
meta_warning (_("Property %s on window 0x%lx contained invalid UTF-8\n"),
name, results->xwindow);
meta_XFree (name);
XFree (results->prop);
results->prop = NULL;
return FALSE;
}
*str_p = (char*) results->prop;
results->prop = NULL;
return TRUE;
}
gboolean
meta_prop_get_utf8_string (MetaDisplay *display,
Window xwindow,
Atom xatom,
char **str_p)
{
GetPropertyResults results;
*str_p = NULL;
if (!get_property (display, xwindow, xatom,
display->atom_utf8_string,
&results))
return FALSE;
return utf8_string_from_results (&results, str_p);
}
/* this one freakishly returns g_malloc memory */
static gboolean
utf8_list_from_results (GetPropertyResults *results,
char ***str_p,
int *n_str_p)
{
int i;
int n_strings;
char **retval;
const char *p;
*str_p = NULL;
*n_str_p = 0;
if (!validate_or_free_results (results, 8,
results->display->atom_utf8_string, FALSE))
return FALSE;
/* I'm not sure this is right, but I'm guessing the
* property is nul-separated
*/
i = 0;
n_strings = 1;
while (i < (int) results->n_items)
{
if (results->prop[i] == '\0')
++n_strings;
++i;
}
/* we're guaranteed that results->prop has a nul on the end
* by XGetWindowProperty
*/
retval = g_new0 (char*, n_strings + 1);
p = results->prop;
i = 0;
while (i < n_strings)
{
if (!g_utf8_validate (p, -1, NULL))
{
char *name;
meta_error_trap_push (results->display);
name = XGetAtomName (results->display->xdisplay, results->xatom);
meta_error_trap_pop (results->display, TRUE);
meta_warning (_("Property %s on window 0x%lx contained invalid UTF-8 for item %d in the list\n"),
name, results->xwindow, i);
meta_XFree (name);
meta_XFree (results->prop);
results->prop = NULL;
g_strfreev (retval);
return FALSE;
}
retval[i] = g_strdup (p);
p = p + strlen (p) + 1;
++i;
}
*str_p = retval;
*n_str_p = i;
meta_XFree (results->prop);
results->prop = NULL;
return TRUE;
}
/* returns g_malloc not Xmalloc memory */
gboolean
meta_prop_get_utf8_list (MetaDisplay *display,
Window xwindow,
Atom xatom,
char ***str_p,
int *n_str_p)
{
GetPropertyResults results;
*str_p = NULL;
if (!get_property (display, xwindow, xatom,
display->atom_utf8_string,
&results))
return FALSE;
return utf8_list_from_results (&results, str_p, n_str_p);
}
static gboolean
window_from_results (GetPropertyResults *results,
Window *window_p)
{
if (!validate_or_free_results (results, 32, XA_WINDOW, TRUE))
return FALSE;
*window_p = *(Window*) results->prop;
XFree (results->prop);
results->prop = NULL;
return TRUE;
}
gboolean
meta_prop_get_window (MetaDisplay *display,
Window xwindow,
Atom xatom,
Window *window_p)
{
GetPropertyResults results;
*window_p = None;
if (!get_property (display, xwindow, xatom, XA_WINDOW,
&results))
return FALSE;
return window_from_results (&results, window_p);
}
gboolean
meta_prop_get_cardinal (MetaDisplay *display,
Window xwindow,
Atom xatom,
gulong *cardinal_p)
{
return meta_prop_get_cardinal_with_atom_type (display, xwindow, xatom,
XA_CARDINAL, cardinal_p);
}
static gboolean
cardinal_with_atom_type_from_results (GetPropertyResults *results,
Atom prop_type,
gulong *cardinal_p)
{
if (!validate_or_free_results (results, 32, prop_type, TRUE))
return FALSE;
*cardinal_p = *(gulong*) results->prop;
XFree (results->prop);
results->prop = NULL;
return TRUE;
}
gboolean
meta_prop_get_cardinal_with_atom_type (MetaDisplay *display,
Window xwindow,
Atom xatom,
Atom prop_type,
gulong *cardinal_p)
{
GetPropertyResults results;
*cardinal_p = 0;
if (!get_property (display, xwindow, xatom, prop_type,
&results))
return FALSE;
return cardinal_with_atom_type_from_results (&results, prop_type, cardinal_p);
}
static AgGetPropertyTask*
get_task (MetaDisplay *display,
Window xwindow,
Atom xatom,
Atom req_type)
{
return ag_task_create (display->xdisplay,
xwindow,
xatom, 0, G_MAXLONG,
False, req_type);
}
void
meta_prop_get_values (MetaDisplay *display,
Window xwindow,
MetaPropValue *values,
int n_values)
{
int i;
AgGetPropertyTask **tasks;
meta_verbose ("Requesting %d properties of 0x%lx at once\n",
n_values, xwindow);
if (n_values == 0)
return;
tasks = g_new0 (AgGetPropertyTask*, n_values);
/* Start up tasks */
i = 0;
while (i < n_values)
{
if (values[i].required_type == None)
{
switch (values[i].type)
{
case META_PROP_VALUE_INVALID:
meta_bug ("META_PROP_VALUE_INVALID requested in %s\n", __FUNCTION__);
break;
case META_PROP_VALUE_UTF8_LIST:
case META_PROP_VALUE_UTF8:
values[i].required_type = display->atom_utf8_string;
break;
case META_PROP_VALUE_STRING:
values[i].required_type = XA_STRING;
break;
case META_PROP_VALUE_MOTIF_HINTS:
values[i].required_type = AnyPropertyType;
break;
case META_PROP_VALUE_CARDINAL_LIST:
case META_PROP_VALUE_CARDINAL:
values[i].required_type = XA_CARDINAL;
break;
case META_PROP_VALUE_WINDOW:
values[i].required_type = XA_WINDOW;
break;
case META_PROP_VALUE_ATOM_LIST:
values[i].required_type = XA_ATOM;
break;
}
}
tasks[i] = get_task (display, xwindow,
values[i].atom, values[i].required_type);
++i;
}
/* Get replies for all our tasks */
XSync (display->xdisplay, False);
/* Collect results, should arrive in order requested */
i = 0;
while (i < n_values)
{
AgGetPropertyTask *task;
GetPropertyResults results;
if (tasks[i] == NULL)
{
/* task creation failed for this property
* (doesn't actually happen I guess)
*/
values[i].type = META_PROP_VALUE_INVALID;
goto next;
}
task = ag_get_next_completed_task (display->xdisplay);
g_assert (task != NULL);
g_assert (ag_task_have_reply (task));
results.display = display;
results.xwindow = xwindow;
results.xatom = values[i].atom;
results.prop = NULL;
results.n_items = 0;
results.type = None;
results.bytes_after = 0;
results.format = 0;
if (ag_task_get_reply_and_free (task,
&results.type, &results.format,
&results.n_items,
&results.bytes_after,
(guchar **)&results.prop) != Success ||
results.type == None)
{
values[i].type = META_PROP_VALUE_INVALID;
if (results.prop)
{
XFree (results.prop);
results.prop = NULL;
}
goto next;
}
switch (values[i].type)
{
case META_PROP_VALUE_INVALID:
g_assert_not_reached ();
break;
case META_PROP_VALUE_UTF8_LIST:
if (!utf8_list_from_results (&results,
&values[i].v.string_list.strings,
&values[i].v.string_list.n_strings))
values[i].type = META_PROP_VALUE_INVALID;
break;
case META_PROP_VALUE_UTF8:
if (!utf8_string_from_results (&results,
&values[i].v.str))
values[i].type = META_PROP_VALUE_INVALID;
break;
case META_PROP_VALUE_STRING:
if (!latin1_string_from_results (&results,
&values[i].v.str))
values[i].type = META_PROP_VALUE_INVALID;
break;
case META_PROP_VALUE_MOTIF_HINTS:
if (!motif_hints_from_results (&results,
&values[i].v.motif_hints))
values[i].type = META_PROP_VALUE_INVALID;
break;
case META_PROP_VALUE_CARDINAL_LIST:
if (!cardinal_list_from_results (&results,
&values[i].v.cardinal_list.cardinals,
&values[i].v.cardinal_list.n_cardinals))
values[i].type = META_PROP_VALUE_INVALID;
break;
case META_PROP_VALUE_CARDINAL:
if (!cardinal_with_atom_type_from_results (&results,
values[i].required_type,
&values[i].v.cardinal))
values[i].type = META_PROP_VALUE_INVALID;
break;
case META_PROP_VALUE_WINDOW:
if (!window_from_results (&results,
&values[i].v.xwindow))
values[i].type = META_PROP_VALUE_INVALID;
break;
case META_PROP_VALUE_ATOM_LIST:
if (!atom_list_from_results (&results,
&values[i].v.atom_list.atoms,
&values[i].v.atom_list.n_atoms))
values[i].type = META_PROP_VALUE_INVALID;
break;
}
next:
++i;
}
g_free (tasks);
}