mirror of
https://github.com/brl/mutter.git
synced 2024-11-30 12:00:44 -05:00
5cfc626bfb
The meta_prop_get_motif_hints() function was only used in the old MetaUI frames code. The remaining code in mutter accesses directly the MetaPropValue when loading properties for a window, and does not use this API call. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2741>
1111 lines
32 KiB
C
1111 lines
32 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/* Mutter X property convenience routines */
|
|
|
|
/*
|
|
* Copyright (C) 2001 Havoc Pennington
|
|
* Copyright (C) 2002 Red Hat Inc.
|
|
*
|
|
* Some trivial property-unpacking code from Xlib:
|
|
* Copyright 1987, 1988, 1998 The Open Group
|
|
* Copyright 1988 by Wyse Technology, Inc., San Jose, Ca,
|
|
* Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/***********************************************************
|
|
Copyright 1988 by Wyse Technology, Inc., San Jose, Ca,
|
|
Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
|
|
|
|
All Rights Reserved
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
documentation for any purpose and without fee is hereby granted,
|
|
provided that the above copyright notice appear in all copies and that
|
|
both that copyright notice and this permission notice appear in
|
|
supporting documentation, and that the name Digital not be
|
|
used in advertising or publicity pertaining to distribution of the
|
|
software without specific, written prior permission.
|
|
|
|
DIGITAL AND WYSE DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
EVENT SHALL DIGITAL OR WYSE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
|
USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
******************************************************************/
|
|
|
|
/*
|
|
|
|
Copyright 1987, 1988, 1998 The Open Group
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
the above copyright notice appear in all copies and that both that
|
|
copyright notice and this permission notice appear in supporting
|
|
documentation.
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of The Open Group shall
|
|
not be used in advertising or otherwise to promote the sale, use or
|
|
other dealings in this Software without prior written authorization
|
|
from The Open Group.
|
|
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "x11/xprops.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlib-xcb.h>
|
|
|
|
#include "core/util-private.h"
|
|
#include "core/window-private.h"
|
|
#include "meta/meta-x11-errors.h"
|
|
#include "x11/meta-x11-display-private.h"
|
|
#include "x11/mutter-Xatomtype.h"
|
|
|
|
typedef struct
|
|
{
|
|
MetaX11Display *x11_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)
|
|
{
|
|
MetaX11Display *x11_display = results->x11_display;
|
|
char *type_name;
|
|
char *expected_name;
|
|
char *prop_name;
|
|
const char *title;
|
|
const char *res_class;
|
|
const char *res_name;
|
|
MetaWindow *w;
|
|
|
|
if (expected_format == results->format &&
|
|
expected_type == results->type &&
|
|
(!must_have_items || results->n_items > 0))
|
|
return TRUE;
|
|
|
|
meta_x11_error_trap_push (x11_display);
|
|
type_name = XGetAtomName (x11_display->xdisplay, results->type);
|
|
expected_name = XGetAtomName (x11_display->xdisplay, expected_type);
|
|
prop_name = XGetAtomName (x11_display->xdisplay, results->xatom);
|
|
meta_x11_error_trap_pop (x11_display);
|
|
|
|
w = meta_x11_display_lookup_x_window (x11_display, results->xwindow);
|
|
|
|
if (w != NULL)
|
|
{
|
|
title = w->title;
|
|
res_class = w->res_class;
|
|
res_name = w->res_name;
|
|
}
|
|
else
|
|
{
|
|
title = NULL;
|
|
res_class = NULL;
|
|
res_name = NULL;
|
|
}
|
|
|
|
if (title == NULL)
|
|
title = "unknown";
|
|
|
|
if (res_class == NULL)
|
|
res_class = "unknown";
|
|
|
|
if (res_name == NULL)
|
|
res_name = "unknown";
|
|
|
|
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. This is most likely an application bug, not a window manager bug. The window has title=\"%s\" class=\"%s\" name=\"%s\"",
|
|
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,
|
|
title, res_class, res_name);
|
|
|
|
meta_XFree (type_name);
|
|
meta_XFree (expected_name);
|
|
meta_XFree (prop_name);
|
|
|
|
if (results->prop)
|
|
{
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static xcb_get_property_cookie_t
|
|
async_get_property (xcb_connection_t *xcb_conn, Window xwindow,
|
|
Atom xatom, Atom required_type)
|
|
{
|
|
return xcb_get_property (xcb_conn, False, xwindow,
|
|
xatom, required_type, 0, G_MAXUINT32);
|
|
}
|
|
|
|
static gboolean
|
|
async_get_property_finish (xcb_connection_t *xcb_conn,
|
|
xcb_get_property_cookie_t cookie,
|
|
GetPropertyResults *results)
|
|
{
|
|
g_autofree xcb_get_property_reply_t *reply = NULL;
|
|
g_autofree xcb_generic_error_t *error = NULL;
|
|
int length;
|
|
|
|
reply = xcb_get_property_reply (xcb_conn, cookie, &error);
|
|
if (error || !reply)
|
|
return FALSE;
|
|
|
|
results->n_items = reply->value_len;
|
|
results->type = reply->type;
|
|
results->bytes_after = reply->bytes_after;
|
|
results->format = reply->format;
|
|
results->prop = NULL;
|
|
|
|
if (results->type != None)
|
|
{
|
|
length = xcb_get_property_value_length (reply);
|
|
/* Leave room for a trailing '\0' since xcb doesn't return null-terminated
|
|
* strings
|
|
*/
|
|
results->prop = g_malloc (length + 1);
|
|
memcpy (results->prop, xcb_get_property_value (reply), length);
|
|
results->prop[length] = '\0';
|
|
}
|
|
|
|
return (results->prop != NULL);
|
|
}
|
|
|
|
static gboolean
|
|
get_property (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom xatom,
|
|
Atom req_type,
|
|
GetPropertyResults *results)
|
|
{
|
|
xcb_get_property_cookie_t cookie;
|
|
xcb_connection_t *xcb_conn = XGetXCBConnection (x11_display->xdisplay);
|
|
|
|
results->x11_display = x11_display;
|
|
results->xwindow = xwindow;
|
|
results->xatom = xatom;
|
|
results->prop = NULL;
|
|
results->n_items = 0;
|
|
results->type = None;
|
|
results->bytes_after = 0;
|
|
results->format = 0;
|
|
|
|
cookie = async_get_property (xcb_conn, xwindow, xatom, req_type);
|
|
return async_get_property_finish (xcb_conn, cookie, results);
|
|
}
|
|
|
|
static gboolean
|
|
atom_list_from_results (GetPropertyResults *results,
|
|
uint32_t **atoms_p,
|
|
int *n_atoms_p)
|
|
{
|
|
if (!validate_or_free_results (results, 32, XA_ATOM, FALSE))
|
|
return FALSE;
|
|
|
|
*atoms_p = (uint32_t*) results->prop;
|
|
*n_atoms_p = results->n_items;
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
cardinal_list_from_results (GetPropertyResults *results,
|
|
uint32_t **cardinals_p,
|
|
int *n_cardinals_p)
|
|
{
|
|
if (!validate_or_free_results (results, 32, XA_CARDINAL, FALSE))
|
|
return FALSE;
|
|
|
|
*cardinals_p = (uint32_t *) results->prop;
|
|
*n_cardinals_p = results->n_items;
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_prop_get_cardinal_list (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom xatom,
|
|
uint32_t **cardinals_p,
|
|
int *n_cardinals_p)
|
|
{
|
|
GetPropertyResults results;
|
|
|
|
*cardinals_p = NULL;
|
|
*n_cardinals_p = 0;
|
|
|
|
if (!get_property (x11_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)
|
|
{
|
|
*hints_p = NULL;
|
|
|
|
if (results->type == None || results->n_items <= 0)
|
|
{
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
meta_verbose ("Motif hints had unexpected type or n_items");
|
|
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 = g_new0 (MotifWmHints, 1);
|
|
memcpy(*hints_p, results->prop, MIN (sizeof (MotifWmHints),
|
|
results->n_items * sizeof (uint32_t)));
|
|
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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 = g_strndup ((char *) results->prop, results->n_items);
|
|
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_prop_get_latin1_string (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom xatom,
|
|
char **str_p)
|
|
{
|
|
GetPropertyResults results;
|
|
|
|
*str_p = NULL;
|
|
|
|
if (!get_property (x11_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->x11_display->atom_UTF8_STRING, FALSE))
|
|
return FALSE;
|
|
|
|
if (results->n_items > 0 &&
|
|
!g_utf8_validate ((gchar *)results->prop, results->n_items, NULL))
|
|
{
|
|
char *name;
|
|
|
|
name = XGetAtomName (results->x11_display->xdisplay, results->xatom);
|
|
meta_warning ("Property %s on window 0x%lx contained invalid UTF-8",
|
|
name, results->xwindow);
|
|
meta_XFree (name);
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
*str_p = g_strndup ((char *) results->prop, results->n_items);
|
|
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* 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->x11_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 = 0;
|
|
while (i < (int) results->n_items)
|
|
{
|
|
if (results->prop[i] == '\0')
|
|
++n_strings;
|
|
++i;
|
|
}
|
|
|
|
if (results->prop[results->n_items - 1] != '\0')
|
|
++n_strings;
|
|
|
|
/* we're guaranteed that results->prop has a nul on the end
|
|
* by XGetWindowProperty
|
|
*/
|
|
|
|
retval = g_new0 (char*, n_strings + 1);
|
|
|
|
p = (char *)results->prop;
|
|
i = 0;
|
|
while (i < n_strings)
|
|
{
|
|
if (!g_utf8_validate (p, -1, NULL))
|
|
{
|
|
char *name;
|
|
|
|
meta_x11_error_trap_push (results->x11_display);
|
|
name = XGetAtomName (results->x11_display->xdisplay, results->xatom);
|
|
meta_x11_error_trap_pop (results->x11_display);
|
|
meta_warning ("Property %s on window 0x%lx contained invalid UTF-8 for item %d in the list",
|
|
name, results->xwindow, i);
|
|
meta_XFree (name);
|
|
g_free (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;
|
|
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* returns g_malloc not Xmalloc memory */
|
|
gboolean
|
|
meta_prop_get_utf8_list (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom xatom,
|
|
char ***str_p,
|
|
int *n_str_p)
|
|
{
|
|
GetPropertyResults results;
|
|
|
|
*str_p = NULL;
|
|
|
|
if (!get_property (x11_display, xwindow, xatom,
|
|
x11_display->atom_UTF8_STRING,
|
|
&results))
|
|
return FALSE;
|
|
|
|
return utf8_list_from_results (&results, str_p, n_str_p);
|
|
}
|
|
|
|
void
|
|
meta_prop_set_utf8_string_hint (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom atom,
|
|
const char *val)
|
|
{
|
|
meta_x11_error_trap_push (x11_display);
|
|
XChangeProperty (x11_display->xdisplay,
|
|
xwindow, atom,
|
|
x11_display->atom_UTF8_STRING,
|
|
8, PropModeReplace, (guchar*) val, strlen (val));
|
|
meta_x11_error_trap_pop (x11_display);
|
|
}
|
|
|
|
static gboolean
|
|
window_from_results (GetPropertyResults *results,
|
|
Window *window_p)
|
|
{
|
|
if (!validate_or_free_results (results, 32, XA_WINDOW, TRUE))
|
|
return FALSE;
|
|
|
|
*window_p = *(uint32_t *) results->prop;
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
counter_from_results (GetPropertyResults *results,
|
|
XSyncCounter *counter_p)
|
|
{
|
|
if (!validate_or_free_results (results, 32,
|
|
XA_CARDINAL,
|
|
TRUE))
|
|
return FALSE;
|
|
|
|
*counter_p = *(uint32_t *) results->prop;
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
counter_list_from_results (GetPropertyResults *results,
|
|
uint32_t **counters_p,
|
|
int *n_counters_p)
|
|
{
|
|
if (!validate_or_free_results (results, 32,
|
|
XA_CARDINAL,
|
|
FALSE))
|
|
return FALSE;
|
|
|
|
*counters_p = (uint32_t *) results->prop;
|
|
*n_counters_p = results->n_items;
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_prop_get_window (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom xatom,
|
|
Window *window_p)
|
|
{
|
|
GetPropertyResults results;
|
|
|
|
*window_p = None;
|
|
|
|
if (!get_property (x11_display, xwindow, xatom, XA_WINDOW,
|
|
&results))
|
|
return FALSE;
|
|
|
|
return window_from_results (&results, window_p);
|
|
}
|
|
|
|
gboolean
|
|
meta_prop_get_cardinal (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom xatom,
|
|
uint32_t *cardinal_p)
|
|
{
|
|
return meta_prop_get_cardinal_with_atom_type (x11_display, xwindow, xatom,
|
|
XA_CARDINAL, cardinal_p);
|
|
}
|
|
|
|
static gboolean
|
|
cardinal_with_atom_type_from_results (GetPropertyResults *results,
|
|
Atom prop_type,
|
|
uint32_t *cardinal_p)
|
|
{
|
|
if (!validate_or_free_results (results, 32, prop_type, TRUE))
|
|
return FALSE;
|
|
|
|
*cardinal_p = *((uint32_t *) results->prop);
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
meta_prop_get_cardinal_with_atom_type (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
Atom xatom,
|
|
Atom prop_type,
|
|
uint32_t *cardinal_p)
|
|
{
|
|
GetPropertyResults results;
|
|
|
|
*cardinal_p = 0;
|
|
|
|
if (!get_property (x11_display, xwindow, xatom, prop_type,
|
|
&results))
|
|
return FALSE;
|
|
|
|
return cardinal_with_atom_type_from_results (&results, prop_type, cardinal_p);
|
|
}
|
|
|
|
static char *
|
|
text_property_to_utf8 (GetPropertyResults *results,
|
|
const XTextProperty *prop)
|
|
{
|
|
char *ret = NULL;
|
|
char **local_list = NULL;
|
|
const char *charset = NULL;
|
|
int count = 0;
|
|
int res;
|
|
|
|
res = XmbTextPropertyToTextList (results->x11_display->xdisplay, prop,
|
|
&local_list, &count);
|
|
if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound)
|
|
goto out;
|
|
|
|
if (count == 0)
|
|
goto out;
|
|
|
|
if (g_get_charset (&charset))
|
|
{
|
|
if (!g_utf8_validate (local_list[0], -1, NULL))
|
|
{
|
|
char *name;
|
|
|
|
meta_x11_error_trap_push (results->x11_display);
|
|
name = XGetAtomName (results->x11_display->xdisplay, results->xatom);
|
|
meta_x11_error_trap_pop (results->x11_display);
|
|
meta_warning ("Property %s on window 0x%lx contained invalid UTF-8",
|
|
name, results->xwindow);
|
|
meta_XFree (name);
|
|
|
|
goto out;
|
|
}
|
|
ret = g_strdup (local_list[0]);
|
|
}
|
|
else
|
|
{
|
|
ret = g_convert (local_list[0], -1, "UTF-8", charset, NULL, NULL, NULL);
|
|
}
|
|
|
|
out:
|
|
XFreeStringList (local_list);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
text_property_from_results (GetPropertyResults *results,
|
|
char **utf8_str_p)
|
|
{
|
|
XTextProperty tp;
|
|
|
|
*utf8_str_p = NULL;
|
|
|
|
tp.value = results->prop;
|
|
tp.encoding = results->type;
|
|
tp.format = results->format;
|
|
tp.nitems = results->n_items;
|
|
|
|
*utf8_str_p = text_property_to_utf8 (results, &tp);
|
|
|
|
g_clear_pointer (&results->prop, g_free);
|
|
|
|
return *utf8_str_p != NULL;
|
|
}
|
|
|
|
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",
|
|
(int) results->n_items, NumPropWMHintsElements - 1);
|
|
if (results->prop)
|
|
{
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
hints = g_new0 (XWMHints, 1);
|
|
|
|
raw = (xPropWMHints*) results->prop;
|
|
|
|
hints->flags = raw->flags;
|
|
hints->input = (raw->input ? True : False);
|
|
hints->initial_state = raw->initialState;
|
|
hints->icon_pixmap = raw->iconPixmap;
|
|
hints->icon_window = raw->iconWindow;
|
|
hints->icon_x = raw->iconX;
|
|
hints->icon_y = 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)
|
|
{
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
}
|
|
|
|
*hints_p = hints;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
class_hint_from_results (GetPropertyResults *results,
|
|
XClassHint *class_hint)
|
|
{
|
|
int len_name;
|
|
|
|
class_hint->res_class = NULL;
|
|
class_hint->res_name = NULL;
|
|
|
|
if (!validate_or_free_results (results, 8, XA_STRING, FALSE))
|
|
return FALSE;
|
|
|
|
class_hint->res_name = g_strdup ((char *) results->prop);
|
|
|
|
len_name = strlen (class_hint->res_name);
|
|
if (len_name == (int) results->n_items)
|
|
class_hint->res_class = g_strdup ("");
|
|
else
|
|
class_hint->res_class = g_strdup ((char *) results->prop + len_name + 1);
|
|
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
size_hints_from_results (GetPropertyResults *results,
|
|
XSizeHints **hints_p,
|
|
gulong *flags_p)
|
|
{
|
|
xPropSizeHints *raw;
|
|
XSizeHints *hints;
|
|
|
|
*hints_p = NULL;
|
|
*flags_p = 0;
|
|
|
|
if (!validate_or_free_results (results, 32, XA_WM_SIZE_HINTS, FALSE))
|
|
return FALSE;
|
|
|
|
if (results->n_items < OldNumPropSizeElements)
|
|
{
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
raw = (xPropSizeHints*) results->prop;
|
|
|
|
hints = g_new0 (XSizeHints, 1);
|
|
|
|
hints->flags = raw->flags;
|
|
hints->x = raw->x;
|
|
hints->y = raw->y;
|
|
hints->width = raw->width;
|
|
hints->height = raw->height;
|
|
hints->min_width = raw->minWidth;
|
|
hints->min_height = raw->minHeight;
|
|
hints->max_width = raw->maxWidth;
|
|
hints->max_height = raw->maxHeight;
|
|
hints->width_inc = raw->widthInc;
|
|
hints->height_inc = raw->heightInc;
|
|
hints->min_aspect.x = raw->minAspectX;
|
|
hints->min_aspect.y = raw->minAspectY;
|
|
hints->max_aspect.x = raw->maxAspectX;
|
|
hints->max_aspect.y = raw->maxAspectY;
|
|
|
|
*flags_p = (USPosition | USSize | PAllHints);
|
|
if (results->n_items >= NumPropSizeElements)
|
|
{
|
|
hints->base_width = raw->baseWidth;
|
|
hints->base_height = raw->baseHeight;
|
|
hints->win_gravity = raw->winGravity;
|
|
*flags_p |= (PBaseSize | PWinGravity);
|
|
}
|
|
|
|
hints->flags &= (*flags_p); /* get rid of unwanted bits */
|
|
|
|
g_free (results->prop);
|
|
results->prop = NULL;
|
|
|
|
*hints_p = hints;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static char*
|
|
latin1_to_utf8 (const char *text)
|
|
{
|
|
GString *str;
|
|
const char *p;
|
|
|
|
str = g_string_new ("");
|
|
|
|
p = text;
|
|
while (*p)
|
|
{
|
|
g_string_append_unichar (str, *p);
|
|
++p;
|
|
}
|
|
|
|
return g_string_free (str, FALSE);
|
|
}
|
|
|
|
void
|
|
meta_prop_get_values (MetaX11Display *x11_display,
|
|
Window xwindow,
|
|
MetaPropValue *values,
|
|
int n_values)
|
|
{
|
|
int i;
|
|
xcb_get_property_cookie_t *tasks;
|
|
xcb_connection_t *xcb_conn = XGetXCBConnection (x11_display->xdisplay);
|
|
|
|
meta_verbose ("Requesting %d properties of 0x%lx at once",
|
|
n_values, xwindow);
|
|
|
|
if (n_values == 0)
|
|
return;
|
|
|
|
tasks = g_new0 (xcb_get_property_cookie_t, n_values);
|
|
|
|
/* Start up tasks. The "values" array can have values
|
|
* with atom == None, which means to ignore that element.
|
|
*/
|
|
i = 0;
|
|
while (i < n_values)
|
|
{
|
|
if (values[i].required_type == None)
|
|
{
|
|
switch (values[i].type)
|
|
{
|
|
case META_PROP_VALUE_INVALID:
|
|
/* This means we don't really want a value, e.g. got
|
|
* property notify on an atom we don't care about.
|
|
*/
|
|
if (values[i].atom != None)
|
|
meta_bug ("META_PROP_VALUE_INVALID requested in %s", G_STRFUNC);
|
|
break;
|
|
case META_PROP_VALUE_UTF8_LIST:
|
|
case META_PROP_VALUE_UTF8:
|
|
values[i].required_type = x11_display->atom_UTF8_STRING;
|
|
break;
|
|
case META_PROP_VALUE_STRING:
|
|
case META_PROP_VALUE_STRING_AS_UTF8:
|
|
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;
|
|
case META_PROP_VALUE_SIZE_HINTS:
|
|
values[i].required_type = XA_WM_SIZE_HINTS;
|
|
break;
|
|
case META_PROP_VALUE_SYNC_COUNTER:
|
|
case META_PROP_VALUE_SYNC_COUNTER_LIST:
|
|
values[i].required_type = XA_CARDINAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (values[i].atom != None)
|
|
tasks[i] = async_get_property (xcb_conn, xwindow, values[i].atom, values[i].required_type);
|
|
++i;
|
|
}
|
|
|
|
/* Get replies for all our tasks */
|
|
meta_topic (META_DEBUG_SYNC, "Syncing to get %d GetProperty replies in %s",
|
|
n_values, G_STRFUNC);
|
|
XSync (x11_display->xdisplay, False);
|
|
|
|
/* Collect results, should arrive in order requested */
|
|
i = 0;
|
|
while (i < n_values)
|
|
{
|
|
GetPropertyResults results;
|
|
|
|
/* We're relying on the fact that sequence numbers can never be zero
|
|
* in Xorg. This is a bit disgusting... */
|
|
if (tasks[i].sequence == 0)
|
|
{
|
|
/* Probably values[i].type was None, or ag_task_create()
|
|
* returned NULL.
|
|
*/
|
|
values[i].type = META_PROP_VALUE_INVALID;
|
|
goto next;
|
|
}
|
|
|
|
results.x11_display = x11_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 (!async_get_property_finish (xcb_conn, tasks[i], &results))
|
|
{
|
|
values[i].type = META_PROP_VALUE_INVALID;
|
|
goto next;
|
|
}
|
|
|
|
values[i].source_xwindow = xwindow;
|
|
|
|
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_STRING_AS_UTF8:
|
|
if (!latin1_string_from_results (&results,
|
|
&values[i].v.str))
|
|
values[i].type = META_PROP_VALUE_INVALID;
|
|
else
|
|
{
|
|
char *new_str;
|
|
new_str = latin1_to_utf8 (values[i].v.str);
|
|
g_free (values[i].v.str);
|
|
values[i].v.str = new_str;
|
|
}
|
|
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;
|
|
case META_PROP_VALUE_SIZE_HINTS:
|
|
if (!size_hints_from_results (&results,
|
|
&values[i].v.size_hints.hints,
|
|
&values[i].v.size_hints.flags))
|
|
values[i].type = META_PROP_VALUE_INVALID;
|
|
break;
|
|
case META_PROP_VALUE_SYNC_COUNTER:
|
|
if (!counter_from_results (&results,
|
|
&values[i].v.xcounter))
|
|
values[i].type = META_PROP_VALUE_INVALID;
|
|
break;
|
|
case META_PROP_VALUE_SYNC_COUNTER_LIST:
|
|
if (!counter_list_from_results (&results,
|
|
&values[i].v.xcounter_list.counters,
|
|
&values[i].v.xcounter_list.n_counters))
|
|
values[i].type = META_PROP_VALUE_INVALID;
|
|
break;
|
|
}
|
|
|
|
next:
|
|
++i;
|
|
}
|
|
|
|
g_free (tasks);
|
|
}
|
|
|
|
static void
|
|
free_value (MetaPropValue *value)
|
|
{
|
|
switch (value->type)
|
|
{
|
|
case META_PROP_VALUE_INVALID:
|
|
break;
|
|
case META_PROP_VALUE_UTF8:
|
|
case META_PROP_VALUE_STRING:
|
|
g_free (value->v.str);
|
|
break;
|
|
case META_PROP_VALUE_STRING_AS_UTF8:
|
|
g_free (value->v.str);
|
|
break;
|
|
case META_PROP_VALUE_MOTIF_HINTS:
|
|
g_free (value->v.motif_hints);
|
|
break;
|
|
case META_PROP_VALUE_CARDINAL:
|
|
break;
|
|
case META_PROP_VALUE_WINDOW:
|
|
break;
|
|
case META_PROP_VALUE_ATOM_LIST:
|
|
g_free (value->v.atom_list.atoms);
|
|
break;
|
|
case META_PROP_VALUE_TEXT_PROPERTY:
|
|
g_free (value->v.str);
|
|
break;
|
|
case META_PROP_VALUE_WM_HINTS:
|
|
g_free (value->v.wm_hints);
|
|
break;
|
|
case META_PROP_VALUE_CLASS_HINT:
|
|
g_free (value->v.class_hint.res_class);
|
|
g_free (value->v.class_hint.res_name);
|
|
break;
|
|
case META_PROP_VALUE_SIZE_HINTS:
|
|
g_free (value->v.size_hints.hints);
|
|
break;
|
|
case META_PROP_VALUE_UTF8_LIST:
|
|
g_strfreev (value->v.string_list.strings);
|
|
break;
|
|
case META_PROP_VALUE_CARDINAL_LIST:
|
|
g_free (value->v.cardinal_list.cardinals);
|
|
break;
|
|
case META_PROP_VALUE_SYNC_COUNTER:
|
|
break;
|
|
case META_PROP_VALUE_SYNC_COUNTER_LIST:
|
|
g_free (value->v.xcounter_list.counters);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_prop_free_values (MetaPropValue *values,
|
|
int n_values)
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
while (i < n_values)
|
|
{
|
|
free_value (&values[i]);
|
|
++i;
|
|
}
|
|
|
|
/* Zero the whole thing to quickly detect breakage */
|
|
memset (values, '\0', sizeof (MetaPropValue) * n_values);
|
|
}
|