/* 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 #include "xprops.h" #include "errors.h" #include "util.h" #include "async-getprop.h" #include #include 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); }