/* 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 "ui.h" #include "metacity-Xatomtype.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 gboolean text_property_from_results (GetPropertyResults *results, char **utf8_str_p) { XTextProperty tp; *utf8_str_p = NULL; tp.value = results->prop; results->prop = NULL; tp.encoding = results->type; tp.format = results->format; tp.nitems = results->n_items; *utf8_str_p = meta_text_property_to_utf8 (results->display->xdisplay, &tp); if (tp.value != NULL) XFree (tp.value); return *utf8_str_p != NULL; } gboolean meta_prop_get_text_property (MetaDisplay *display, Window xwindow, Atom xatom, char **utf8_str_p) { GetPropertyResults results; if (!get_property (display, xwindow, xatom, AnyPropertyType, &results)) return FALSE; return text_property_from_results (&results, utf8_str_p); } /* From Xmd.h */ #ifndef cvtINT32toInt #if defined(WORD64) && defined(UNSIGNEDBITFIELDS) #define cvtINT8toInt(val) (((val) & 0x00000080) ? ((val) | 0xffffffffffffff00) : (val)) #define cvtINT16toInt(val) (((val) & 0x00008000) ? ((val) | 0xffffffffffff0000) : (val)) #define cvtINT32toInt(val) (((val) & 0x80000000) ? ((val) | 0xffffffff00000000) : (val)) #define cvtINT8toShort(val) cvtINT8toInt(val) #define cvtINT16toShort(val) cvtINT16toInt(val) #define cvtINT32toShort(val) cvtINT32toInt(val) #define cvtINT8toLong(val) cvtINT8toInt(val) #define cvtINT16toLong(val) cvtINT16toInt(val) #define cvtINT32toLong(val) cvtINT32toInt(val) #else #define cvtINT8toInt(val) (val) #define cvtINT16toInt(val) (val) #define cvtINT32toInt(val) (val) #define cvtINT8toShort(val) (val) #define cvtINT16toShort(val) (val) #define cvtINT32toShort(val) (val) #define cvtINT8toLong(val) (val) #define cvtINT16toLong(val) (val) #define cvtINT32toLong(val) (val) #endif /* WORD64 and UNSIGNEDBITFIELDS */ #endif /* cvtINT32toInt() */ static gboolean wm_hints_from_results (GetPropertyResults *results, XWMHints **hints_p) { XWMHints *hints; xPropWMHints *raw; *hints_p = NULL; if (!validate_or_free_results (results, 32, XA_WM_HINTS, TRUE)) return FALSE; /* pre-R3 bogusly truncated window_group, don't fail on them */ if (results->n_items < (NumPropWMHintsElements - 1)) { meta_verbose ("WM_HINTS property too short: %d should be %d\n", (int) results->n_items, NumPropWMHintsElements - 1); if (results->prop) { XFree (results->prop); results->prop = NULL; } return FALSE; } hints = ag_Xmalloc0 (sizeof (XWMHints)); raw = (xPropWMHints*) results->prop; hints->flags = raw->flags; hints->input = (raw->input ? True : False); hints->initial_state = cvtINT32toInt (raw->initialState); hints->icon_pixmap = raw->iconPixmap; hints->icon_window = raw->iconWindow; hints->icon_x = cvtINT32toInt (raw->iconX); hints->icon_y = cvtINT32toInt (raw->iconY); hints->icon_mask = raw->iconMask; if (results->n_items >= NumPropWMHintsElements) hints->window_group = raw->windowGroup; else hints->window_group = 0; if (results->prop) { XFree (results->prop); results->prop = NULL; } *hints_p = hints; return TRUE; } gboolean meta_prop_get_wm_hints (MetaDisplay *display, Window xwindow, Atom xatom, XWMHints **hints_p) { GetPropertyResults results; *hints_p = NULL; if (!get_property (display, xwindow, xatom, XA_WM_HINTS, &results)) return FALSE; return wm_hints_from_results (&results, hints_p); } static gboolean class_hint_from_results (GetPropertyResults *results, XClassHint *class_hint) { int len_name, len_class; class_hint->res_class = NULL; class_hint->res_name = NULL; if (!validate_or_free_results (results, 8, XA_STRING, FALSE)) return FALSE; len_name = strlen ((char *) results->prop); if (! (class_hint->res_name = ag_Xmalloc (len_name+1))) { XFree (results->prop); results->prop = NULL; return FALSE; } strcpy (class_hint->res_name, results->prop); if (len_name == (int) results->n_items) len_name--; len_class = strlen (results->prop + len_name + 1); if (! (class_hint->res_class = ag_Xmalloc(len_class+1))) { XFree(class_hint->res_name); class_hint->res_name = NULL; XFree (results->prop); results->prop = NULL; return FALSE; } strcpy (class_hint->res_class, results->prop + len_name + 1); XFree (results->prop); results->prop = NULL; return TRUE; } gboolean meta_prop_get_class_hint (MetaDisplay *display, Window xwindow, Atom xatom, XClassHint *class_hint) { GetPropertyResults results; class_hint->res_class = NULL; class_hint->res_name = NULL; if (!get_property (display, xwindow, xatom, XA_STRING, &results)) return FALSE; return class_hint_from_results (&results, class_hint); } 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; case META_PROP_VALUE_TEXT_PROPERTY: values[i].required_type = AnyPropertyType; break; case META_PROP_VALUE_WM_HINTS: values[i].required_type = XA_WM_HINTS; break; case META_PROP_VALUE_CLASS_HINT: values[i].required_type = XA_STRING; 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; case META_PROP_VALUE_TEXT_PROPERTY: if (!text_property_from_results (&results, &values[i].v.str)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_WM_HINTS: if (!wm_hints_from_results (&results, &values[i].v.wm_hints)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_CLASS_HINT: if (!class_hint_from_results (&results, &values[i].v.class_hint)) values[i].type = META_PROP_VALUE_INVALID; break; } next: ++i; } g_free (tasks); }