X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=obt%2Fprop.c;h=f7919d6c380a64d919d2d909a5f6cede435f5490;hb=6eb740cf119b14903afa3028e108dd98c57ff926;hp=7beb9a91f6fb0981cc908ff5b7d8c432b1c3afc8;hpb=c22e6012d11e7316953c7a71222f11337d8c4868;p=chaz%2Fopenbox diff --git a/obt/prop.c b/obt/prop.c index 7beb9a91..f7919d6c 100644 --- a/obt/prop.c +++ b/obt/prop.c @@ -28,99 +28,106 @@ Atom prop_atoms[OBT_PROP_NUM_ATOMS]; gboolean prop_started = FALSE; -#define CREATE(var, name) (prop_atoms[OBT_PROP_##var] = \ - XInternAtom((obt_display), (name), FALSE)) +#define CREATE_NAME(var, name) (prop_atoms[OBT_PROP_##var] = \ + XInternAtom((obt_display), (name), FALSE)) +#define CREATE(var) CREATE_NAME(var, #var) +#define CREATE_(var) CREATE_NAME(var, "_" #var) -void obt_prop_startup() +void obt_prop_startup(void) { if (prop_started) return; prop_started = TRUE; g_assert(obt_display); - CREATE(CARDINAL, "CARDINAL"); - CREATE(WINDOW, "WINDOW"); - CREATE(PIXMAP, "PIXMAP"); - CREATE(ATOM, "ATOM"); - CREATE(STRING, "STRING"); - CREATE(UTF8, "UTF8_STRING"); - - CREATE(MANAGER, "MANAGER"); - - CREATE(WM_COLORMAP_WINDOWS, "WM_COLORMAP_WINDOWS"); - CREATE(WM_PROTOCOLS, "WM_PROTOCOLS"); - CREATE(WM_STATE, "WM_STATE"); - CREATE(WM_CHANGE_STATE, "WM_CHANGE_STATE"); - CREATE(WM_DELETE_WINDOW, "WM_DELETE_WINDOW"); - CREATE(WM_TAKE_FOCUS, "WM_TAKE_FOCUS"); - CREATE(WM_NAME, "WM_NAME"); - CREATE(WM_ICON_NAME, "WM_ICON_NAME"); - CREATE(WM_CLASS, "WM_CLASS"); - CREATE(WM_WINDOW_ROLE, "WM_WINDOW_ROLE"); - CREATE(WM_CLIENT_MACHINE, "WM_CLIENT_MACHINE"); - CREATE(WM_COMMAND, "WM_COMMAND"); - CREATE(WM_CLIENT_LEADER, "WM_CLIENT_LEADER"); - CREATE(MOTIF_WM_HINTS, "_MOTIF_WM_HINTS"); - - CREATE(SM_CLIENT_ID, "SM_CLIENT_ID"); - - CREATE(NET_WM_FULL_PLACEMENT, "_NET_WM_FULL_PLACEMENT"); - - CREATE(NET_SUPPORTED, "_NET_SUPPORTED"); - CREATE(NET_CLIENT_LIST, "_NET_CLIENT_LIST"); - CREATE(NET_CLIENT_LIST_STACKING, "_NET_CLIENT_LIST_STACKING"); - CREATE(NET_NUMBER_OF_DESKTOPS, "_NET_NUMBER_OF_DESKTOPS"); - CREATE(NET_DESKTOP_GEOMETRY, "_NET_DESKTOP_GEOMETRY"); - CREATE(NET_DESKTOP_VIEWPORT, "_NET_DESKTOP_VIEWPORT"); - CREATE(NET_CURRENT_DESKTOP, "_NET_CURRENT_DESKTOP"); - CREATE(NET_DESKTOP_NAMES, "_NET_DESKTOP_NAMES"); - CREATE(NET_ACTIVE_WINDOW, "_NET_ACTIVE_WINDOW"); -/* CREATE(NET_RESTACK_WINDOW, "_NET_RESTACK_WINDOW");*/ - CREATE(NET_WORKAREA, "_NET_WORKAREA"); - CREATE(NET_SUPPORTING_WM_CHECK, "_NET_SUPPORTING_WM_CHECK"); - CREATE(NET_DESKTOP_LAYOUT, "_NET_DESKTOP_LAYOUT"); - CREATE(NET_SHOWING_DESKTOP, "_NET_SHOWING_DESKTOP"); - - CREATE(NET_CLOSE_WINDOW, "_NET_CLOSE_WINDOW"); - CREATE(NET_WM_MOVERESIZE, "_NET_WM_MOVERESIZE"); - CREATE(NET_MOVERESIZE_WINDOW, "_NET_MOVERESIZE_WINDOW"); - CREATE(NET_REQUEST_FRAME_EXTENTS, "_NET_REQUEST_FRAME_EXTENTS"); - CREATE(NET_RESTACK_WINDOW, "_NET_RESTACK_WINDOW"); - - CREATE(NET_STARTUP_ID, "_NET_STARTUP_ID"); - - CREATE(NET_WM_NAME, "_NET_WM_NAME"); - CREATE(NET_WM_VISIBLE_NAME, "_NET_WM_VISIBLE_NAME"); - CREATE(NET_WM_ICON_NAME, "_NET_WM_ICON_NAME"); - CREATE(NET_WM_VISIBLE_ICON_NAME, "_NET_WM_VISIBLE_ICON_NAME"); - CREATE(NET_WM_DESKTOP, "_NET_WM_DESKTOP"); - CREATE(NET_WM_WINDOW_TYPE, "_NET_WM_WINDOW_TYPE"); - CREATE(NET_WM_STATE, "_NET_WM_STATE"); - CREATE(NET_WM_STRUT, "_NET_WM_STRUT"); - CREATE(NET_WM_STRUT_PARTIAL, "_NET_WM_STRUT_PARTIAL"); - CREATE(NET_WM_ICON, "_NET_WM_ICON"); - CREATE(NET_WM_ICON_GEOMETRY, "_NET_WM_ICON_GEOMETRY"); - CREATE(NET_WM_PID, "_NET_WM_PID"); - CREATE(NET_WM_ALLOWED_ACTIONS, "_NET_WM_ALLOWED_ACTIONS"); - CREATE(NET_WM_USER_TIME, "_NET_WM_USER_TIME"); -/* CREATE(NET_WM_USER_TIME_WINDOW, "_NET_WM_USER_TIME_WINDOW"); */ - CREATE(KDE_NET_WM_FRAME_STRUT, "_KDE_NET_WM_FRAME_STRUT"); - CREATE(NET_FRAME_EXTENTS, "_NET_FRAME_EXTENTS"); - - CREATE(NET_WM_PING, "_NET_WM_PING"); + CREATE(CARDINAL); + CREATE(WINDOW); + CREATE(PIXMAP); + CREATE(ATOM); + CREATE(STRING); + CREATE(COMPOUND_TEXT); + CREATE(UTF8_STRING); + + CREATE(MANAGER); + + CREATE(WM_COLORMAP_WINDOWS); + CREATE(WM_PROTOCOLS); + CREATE(WM_STATE); + CREATE(WM_CHANGE_STATE); + CREATE(WM_DELETE_WINDOW); + CREATE(WM_TAKE_FOCUS); + CREATE(WM_NAME); + CREATE(WM_ICON_NAME); + CREATE(WM_CLASS); + CREATE(WM_WINDOW_ROLE); + CREATE(WM_CLIENT_MACHINE); + CREATE(WM_COMMAND); + CREATE(WM_CLIENT_LEADER); + CREATE(WM_TRANSIENT_FOR); + CREATE_(MOTIF_WM_HINTS); + CREATE_(MOTIF_WM_INFO); + + CREATE(SM_CLIENT_ID); + + CREATE_(NET_WM_FULL_PLACEMENT); + + CREATE_(NET_SUPPORTED); + CREATE_(NET_CLIENT_LIST); + CREATE_(NET_CLIENT_LIST_STACKING); + CREATE_(NET_NUMBER_OF_DESKTOPS); + CREATE_(NET_DESKTOP_GEOMETRY); + CREATE_(NET_DESKTOP_VIEWPORT); + CREATE_(NET_CURRENT_DESKTOP); + CREATE_(NET_DESKTOP_NAMES); + CREATE_(NET_ACTIVE_WINDOW); +/* CREATE_(NET_RESTACK_WINDOW);*/ + CREATE_(NET_WORKAREA); + CREATE_(NET_SUPPORTING_WM_CHECK); + CREATE_(NET_DESKTOP_LAYOUT); + CREATE_(NET_SHOWING_DESKTOP); + + CREATE_(NET_CLOSE_WINDOW); + CREATE_(NET_WM_MOVERESIZE); + CREATE_(NET_MOVERESIZE_WINDOW); + CREATE_(NET_REQUEST_FRAME_EXTENTS); + CREATE_(NET_RESTACK_WINDOW); + + CREATE_(NET_STARTUP_ID); + + CREATE_(NET_WM_NAME); + CREATE_(NET_WM_VISIBLE_NAME); + CREATE_(NET_WM_ICON_NAME); + CREATE_(NET_WM_VISIBLE_ICON_NAME); + CREATE_(NET_WM_DESKTOP); + CREATE_(NET_WM_WINDOW_TYPE); + CREATE_(NET_WM_STATE); + CREATE_(NET_WM_STRUT); + CREATE_(NET_WM_STRUT_PARTIAL); + CREATE_(NET_WM_ICON); + CREATE_(NET_WM_ICON_GEOMETRY); + CREATE_(NET_WM_PID); + CREATE_(NET_WM_ALLOWED_ACTIONS); + CREATE_(NET_WM_WINDOW_OPACITY); + CREATE_(NET_WM_USER_TIME); +/* CREATE_(NET_WM_USER_TIME_WINDOW); */ + CREATE_(KDE_NET_WM_FRAME_STRUT); + CREATE_(NET_FRAME_EXTENTS); + + CREATE_(NET_WM_PING); #ifdef SYNC - CREATE(NET_WM_SYNC_REQUEST, "_NET_WM_SYNC_REQUEST"); - CREATE(NET_WM_SYNC_REQUEST_COUNTER, "_NET_WM_SYNC_REQUEST_COUNTER"); + CREATE_(NET_WM_SYNC_REQUEST); + CREATE_(NET_WM_SYNC_REQUEST_COUNTER); #endif - CREATE(NET_WM_WINDOW_TYPE_DESKTOP, "_NET_WM_WINDOW_TYPE_DESKTOP"); - CREATE(NET_WM_WINDOW_TYPE_DOCK, "_NET_WM_WINDOW_TYPE_DOCK"); - CREATE(NET_WM_WINDOW_TYPE_TOOLBAR, "_NET_WM_WINDOW_TYPE_TOOLBAR"); - CREATE(NET_WM_WINDOW_TYPE_MENU, "_NET_WM_WINDOW_TYPE_MENU"); - CREATE(NET_WM_WINDOW_TYPE_UTILITY, "_NET_WM_WINDOW_TYPE_UTILITY"); - CREATE(NET_WM_WINDOW_TYPE_SPLASH, "_NET_WM_WINDOW_TYPE_SPLASH"); - CREATE(NET_WM_WINDOW_TYPE_DIALOG, "_NET_WM_WINDOW_TYPE_DIALOG"); - CREATE(NET_WM_WINDOW_TYPE_NORMAL, "_NET_WM_WINDOW_TYPE_NORMAL"); + CREATE_(NET_WM_WINDOW_TYPE_DESKTOP); + CREATE_(NET_WM_WINDOW_TYPE_DOCK); + CREATE_(NET_WM_WINDOW_TYPE_TOOLBAR); + CREATE_(NET_WM_WINDOW_TYPE_MENU); + CREATE_(NET_WM_WINDOW_TYPE_UTILITY); + CREATE_(NET_WM_WINDOW_TYPE_SPLASH); + CREATE_(NET_WM_WINDOW_TYPE_DIALOG); + CREATE_(NET_WM_WINDOW_TYPE_NORMAL); + CREATE_(NET_WM_WINDOW_TYPE_POPUP_MENU); prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT] = 0; prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP] = 1; @@ -135,30 +142,30 @@ void obt_prop_startup() prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD] = 10; prop_atoms[OBT_PROP_NET_WM_MOVERESIZE_CANCEL] = 11; - CREATE(NET_WM_ACTION_MOVE, "_NET_WM_ACTION_MOVE"); - CREATE(NET_WM_ACTION_RESIZE, "_NET_WM_ACTION_RESIZE"); - CREATE(NET_WM_ACTION_MINIMIZE, "_NET_WM_ACTION_MINIMIZE"); - CREATE(NET_WM_ACTION_SHADE, "_NET_WM_ACTION_SHADE"); - CREATE(NET_WM_ACTION_MAXIMIZE_HORZ, "_NET_WM_ACTION_MAXIMIZE_HORZ"); - CREATE(NET_WM_ACTION_MAXIMIZE_VERT, "_NET_WM_ACTION_MAXIMIZE_VERT"); - CREATE(NET_WM_ACTION_FULLSCREEN, "_NET_WM_ACTION_FULLSCREEN"); - CREATE(NET_WM_ACTION_CHANGE_DESKTOP, "_NET_WM_ACTION_CHANGE_DESKTOP"); - CREATE(NET_WM_ACTION_CLOSE, "_NET_WM_ACTION_CLOSE"); - CREATE(NET_WM_ACTION_ABOVE, "_NET_WM_ACTION_ABOVE"); - CREATE(NET_WM_ACTION_BELOW, "_NET_WM_ACTION_BELOW"); - - CREATE(NET_WM_STATE_MODAL, "_NET_WM_STATE_MODAL"); -/* CREATE(NET_WM_STATE_STICKY, "_NET_WM_STATE_STICKY");*/ - CREATE(NET_WM_STATE_MAXIMIZED_VERT, "_NET_WM_STATE_MAXIMIZED_VERT"); - CREATE(NET_WM_STATE_MAXIMIZED_HORZ, "_NET_WM_STATE_MAXIMIZED_HORZ"); - CREATE(NET_WM_STATE_SHADED, "_NET_WM_STATE_SHADED"); - CREATE(NET_WM_STATE_SKIP_TASKBAR, "_NET_WM_STATE_SKIP_TASKBAR"); - CREATE(NET_WM_STATE_SKIP_PAGER, "_NET_WM_STATE_SKIP_PAGER"); - CREATE(NET_WM_STATE_HIDDEN, "_NET_WM_STATE_HIDDEN"); - CREATE(NET_WM_STATE_FULLSCREEN, "_NET_WM_STATE_FULLSCREEN"); - CREATE(NET_WM_STATE_ABOVE, "_NET_WM_STATE_ABOVE"); - CREATE(NET_WM_STATE_BELOW, "_NET_WM_STATE_BELOW"); - CREATE(NET_WM_STATE_DEMANDS_ATTENTION, "_NET_WM_STATE_DEMANDS_ATTENTION"); + CREATE_(NET_WM_ACTION_MOVE); + CREATE_(NET_WM_ACTION_RESIZE); + CREATE_(NET_WM_ACTION_MINIMIZE); + CREATE_(NET_WM_ACTION_SHADE); + CREATE_(NET_WM_ACTION_MAXIMIZE_HORZ); + CREATE_(NET_WM_ACTION_MAXIMIZE_VERT); + CREATE_(NET_WM_ACTION_FULLSCREEN); + CREATE_(NET_WM_ACTION_CHANGE_DESKTOP); + CREATE_(NET_WM_ACTION_CLOSE); + CREATE_(NET_WM_ACTION_ABOVE); + CREATE_(NET_WM_ACTION_BELOW); + + CREATE_(NET_WM_STATE_MODAL); +/* CREATE_(NET_WM_STATE_STICKY);*/ + CREATE_(NET_WM_STATE_MAXIMIZED_VERT); + CREATE_(NET_WM_STATE_MAXIMIZED_HORZ); + CREATE_(NET_WM_STATE_SHADED); + CREATE_(NET_WM_STATE_SKIP_TASKBAR); + CREATE_(NET_WM_STATE_SKIP_PAGER); + CREATE_(NET_WM_STATE_HIDDEN); + CREATE_(NET_WM_STATE_FULLSCREEN); + CREATE_(NET_WM_STATE_ABOVE); + CREATE_(NET_WM_STATE_BELOW); + CREATE_(NET_WM_STATE_DEMANDS_ATTENTION); prop_atoms[OBT_PROP_NET_WM_STATE_ADD] = 1; prop_atoms[OBT_PROP_NET_WM_STATE_REMOVE] = 0; @@ -171,19 +178,28 @@ void obt_prop_startup() prop_atoms[OBT_PROP_NET_WM_BOTTOMRIGHT] = 2; prop_atoms[OBT_PROP_NET_WM_BOTTOMLEFT] = 3; - CREATE(KDE_WM_CHANGE_STATE, "_KDE_WM_CHANGE_STATE"); - CREATE(KDE_NET_WM_WINDOW_TYPE_OVERRIDE,"_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"); + CREATE_(KDE_WM_CHANGE_STATE); + CREATE_(KDE_NET_WM_WINDOW_TYPE_OVERRIDE); /* - CREATE(ROOTPMAPId, "_XROOTPMAP_ID"); - CREATE(ESETROOTId, "ESETROOT_PMAP_ID"); + CREATE_NAME(ROOTPMAPId, "_XROOTPMAP_ID"); + CREATE_NAME(ESETROOTId, "ESETROOT_PMAP_ID"); */ - CREATE(OPENBOX_PID, "_OPENBOX_PID"); - CREATE(OB_THEME, "_OB_THEME"); - CREATE(OB_WM_ACTION_UNDECORATE, "_OB_WM_ACTION_UNDECORATE"); - CREATE(OB_WM_STATE_UNDECORATED, "_OB_WM_STATE_UNDECORATED"); - CREATE(OB_CONTROL, "_OB_CONTROL"); + CREATE_(OPENBOX_PID); + CREATE_(OB_THEME); + CREATE_(OB_CONFIG_FILE); + CREATE_(OB_WM_ACTION_UNDECORATE); + CREATE_(OB_WM_STATE_UNDECORATED); + CREATE_(OB_CONTROL); + CREATE_(OB_VERSION); + CREATE_(OB_APP_ROLE); + CREATE_(OB_APP_TITLE); + CREATE_(OB_APP_NAME); + CREATE_(OB_APP_CLASS); + CREATE_(OB_APP_GROUP_NAME); + CREATE_(OB_APP_GROUP_CLASS); + CREATE_(OB_APP_TYPE); } Atom obt_prop_atom(ObtPropAtom a) @@ -271,128 +287,236 @@ static gboolean get_all(Window win, Atom prop, Atom type, gint size, return ret; } -static gboolean get_stringlist(Window win, Atom prop, gchar ***list, gint *nstr) +/*! Get a text property from a window, and fill out the XTextProperty with it. + @param win The window to read the property from. + @param prop The atom of the property to read off the window. + @param tprop The XTextProperty to fill out. + @param type 0 to get text of any type, or a value from + ObtPropTextType to restrict the value to a specific type. + @return TRUE if the text was read and validated against the @type, and FALSE + otherwise. +*/ +static gboolean get_text_property(Window win, Atom prop, + XTextProperty *tprop, ObtPropTextType type) { - XTextProperty tprop; - gboolean ret = FALSE; - - if (XGetTextProperty(obt_display, win, &tprop, prop) && tprop.nitems) { - if (XTextPropertyToStringList(&tprop, list, nstr)) - ret = TRUE; - XFree(tprop.value); + if (!(XGetTextProperty(obt_display, win, tprop, prop) && tprop->nitems)) + return FALSE; + if (!type) + return TRUE; /* no type checking */ + switch (type) { + case OBT_PROP_TEXT_STRING: + case OBT_PROP_TEXT_STRING_XPCS: + case OBT_PROP_TEXT_STRING_NO_CC: + return tprop->encoding == OBT_PROP_ATOM(STRING); + case OBT_PROP_TEXT_COMPOUND_TEXT: + return tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT); + case OBT_PROP_TEXT_UTF8_STRING: + return tprop->encoding == OBT_PROP_ATOM(UTF8_STRING); + default: + g_assert_not_reached(); } - return ret; -} - -gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret) -{ - return get_prealloc(win, prop, type, 32, (guchar*)ret, 1); } -gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret, - guint *nret) +/*! Returns one or more UTF-8 encoded strings from the text property. + @param tprop The XTextProperty to convert into UTF-8 string(s). + @param type The type which specifies the format that the text must meet, or + 0 to allow any valid characters that can be converted to UTF-8 through. + @param max The maximum number of strings to return. -1 to return them all. + @return If max is 1, then this returns a gchar* with the single string. + Otherwise, this returns a gchar** of no more than max strings (or all + strings read, if max is negative). If an error occurs, NULL is returned. + */ +static void* convert_text_property(XTextProperty *tprop, + ObtPropTextType type, gint max) { - return get_all(win, prop, type, 32, (guchar**)ret, nret); -} - -gboolean obt_prop_get_string_locale(Window win, Atom prop, gchar **ret) -{ - gchar **list; - gint nstr; - gchar *s; - - if (get_stringlist(win, prop, &list, &nstr) && nstr) { - s = g_locale_to_utf8(list[0], -1, NULL, NULL, NULL); - XFreeStringList(list); - if (s) { - *ret = s; - return TRUE; + enum { + LATIN1, + UTF8, + LOCALE + } encoding; + const gboolean return_single = (max == 1); + gboolean ok = FALSE; + gchar **strlist = NULL; + gchar *single[1] = { NULL }; + gchar **retlist = single; /* single is used when max == 1 */ + gint i, n_strs; + + /* Read each string in the text property and store a pointer to it in + retlist. These pointers point into the X data structures directly. + + Then we will convert them to UTF-8, and replace the retlist pointer with + a new one. + */ + if (tprop->encoding == OBT_PROP_ATOM(COMPOUND_TEXT)) + { + encoding = LOCALE; + ok = (XmbTextPropertyToTextList( + obt_display, tprop, &strlist, &n_strs) == Success); + if (ok) { + if (max >= 0) + n_strs = MIN(max, n_strs); + if (!return_single) + retlist = g_new0(gchar*, n_strs+1); + if (retlist) + for (i = 0; i < n_strs; ++i) + retlist[i] = strlist[i]; } } - return FALSE; -} - -gboolean obt_prop_get_strings_locale(Window win, Atom prop, gchar ***ret) -{ - GSList *strs = NULL, *it; - gchar *raw, *p; - guint num, i, count = 0; - - if (get_all(win, prop, OBT_PROP_ATOM(STRING), 8, - (guchar**)&raw, &num)) + else if (tprop->encoding == OBT_PROP_ATOM(UTF8_STRING) || + tprop->encoding == OBT_PROP_ATOM(STRING)) { - p = raw; - while (p < raw + num) { - ++count; - strs = g_slist_append(strs, p); + gchar *p; /* iterator */ + + if (tprop->encoding == OBT_PROP_ATOM(STRING)) + encoding = LATIN1; + else + encoding = UTF8; + ok = TRUE; + + /* First, count the number of strings. Then make a structure for them + and copy pointers to them into it. */ + p = (gchar*)tprop->value; + n_strs = 0; + while (p < (gchar*)tprop->value + tprop->nitems) { p += strlen(p) + 1; /* next string */ + ++n_strs; } - *ret = g_new0(gchar*, count + 1); - (*ret)[count] = NULL; /* null terminated list */ + if (max >= 0) + n_strs = MIN(max, n_strs); + if (!return_single) + retlist = g_new0(gchar*, n_strs+1); + if (retlist) { + p = (gchar*)tprop->value; + for (i = 0; i < n_strs; ++i) { + retlist[i] = p; + p += strlen(p) + 1; /* next string */ + } + } + } + + if (!(ok && retlist)) { + if (strlist) XFreeStringList(strlist); + return NULL; + } + + /* convert each element in retlist to UTF-8, and replace it. */ + for (i = 0; i < n_strs; ++i) { + if (encoding == UTF8) { + const gchar *end; /* the first byte past the valid data */ - for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) { - (*ret)[i] = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL); - /* make sure translation did not fail */ - if (!(*ret)[i]) - (*ret)[i] = g_strdup(""); + g_utf8_validate(retlist[i], -1, &end); + retlist[i] = g_strndup(retlist[i], end-retlist[i]); + } + else if (encoding == LOCALE) { + gsize nvalid; /* the number of valid bytes at the front of the + string */ + gchar *utf; /* the string converted into utf8 */ + + utf = g_locale_to_utf8(retlist[i], -1, &nvalid, NULL, NULL); + if (!utf) + utf = g_locale_to_utf8(retlist[i], nvalid, NULL, NULL, NULL); + g_assert(utf); + retlist[i] = utf; + } + else { /* encoding == LATIN1 */ + gsize nvalid; /* the number of valid bytes at the front of the + string */ + gchar *utf; /* the string converted into utf8 */ + gchar *p; /* iterator */ + + /* look for invalid characters */ + for (p = retlist[i], nvalid = 0; *p; ++p, ++nvalid) { + /* The only valid control characters are TAB(HT)=9 and + NEWLINE(LF)=10. + This is defined in ICCCM section 2: + http://tronche.com/gui/x/icccm/sec-2.html. + See a definition of the latin1 codepage here: + http://en.wikipedia.org/wiki/ISO/IEC_8859-1. + The above page includes control characters in the table, + which we must explicitly exclude, as the g_convert function + will happily take them. + */ + const register guchar c = (guchar)*p; /* unsigned value at p */ + if ((c < 32 && c != 9 && c != 10) || (c >= 127 && c <= 160)) + break; /* found a control character that isn't allowed */ + + if (type == OBT_PROP_TEXT_STRING_NO_CC && c < 32) + break; /* absolutely no control characters are allowed */ + + if (type == OBT_PROP_TEXT_STRING_XPCS) { + const gboolean valid = ( + (c >= 32 && c < 128) || c == 9 || c == 10); + if (!valid) + break; /* strict whitelisting for XPCS */ + } + } + /* look for invalid latin1 characters */ + utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1", + &nvalid, NULL, NULL); + if (!utf) + utf = g_convert(retlist[i], nvalid, "utf-8", "iso-8859-1", + NULL, NULL, NULL); + g_assert(utf); + retlist[i] = utf; } - g_free(raw); - g_slist_free(strs); - return TRUE; } - return FALSE; + + if (strlist) XFreeStringList(strlist); + if (return_single) + return retlist[0]; + else + return retlist; } -gboolean obt_prop_get_string_utf8(Window win, Atom prop, gchar **ret) +gboolean obt_prop_get32(Window win, Atom prop, Atom type, guint32 *ret) { - gchar *raw; + return get_prealloc(win, prop, type, 32, (guchar*)ret, 1); +} + +gboolean obt_prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret, + guint *nret) +{ + return get_all(win, prop, type, 32, (guchar**)ret, nret); +} + +gboolean obt_prop_get_text(Window win, Atom prop, ObtPropTextType type, + gchar **ret_string) +{ + XTextProperty tprop; gchar *str; - guint num; + gboolean ret = FALSE; - if (get_all(win, prop, OBT_PROP_ATOM(UTF8), 8, - (guchar**)&raw, &num)) - { - str = g_strndup(raw, num); /* grab the first string from the list */ - g_free(raw); - if (g_utf8_validate(str, -1, NULL)) { - *ret = str; - return TRUE; + if (get_text_property(win, prop, &tprop, type)) { + str = (gchar*)convert_text_property(&tprop, type, 1); + + if (str) { + *ret_string = str; + ret = TRUE; } - g_free(str); } - return FALSE; + XFree(tprop.value); + return ret; } -gboolean obt_prop_get_strings_utf8(Window win, Atom prop, gchar ***ret) +gboolean obt_prop_get_array_text(Window win, Atom prop, + ObtPropTextType type, + gchar ***ret_strings) { - GSList *strs = NULL, *it; - gchar *raw, *p; - guint num, i, count = 0; - - if (get_all(win, prop, OBT_PROP_ATOM(UTF8), 8, - (guchar**)&raw, &num)) - { - p = raw; - while (p < raw + num) { - ++count; - strs = g_slist_append(strs, p); - p += strlen(p) + 1; /* next string */ - } + XTextProperty tprop; + gchar **strs; + gboolean ret = FALSE; - *ret = g_new0(gchar*, count + 1); + if (get_text_property(win, prop, &tprop, type)) { + strs = (gchar**)convert_text_property(&tprop, type, -1); - for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) { - if (g_utf8_validate(it->data, -1, NULL)) - (*ret)[i] = g_strdup(it->data); - else - (*ret)[i] = g_strdup(""); + if (strs) { + *ret_strings = strs; + ret = TRUE; } - g_free(raw); - g_slist_free(strs); - return TRUE; } - return FALSE; + XFree(tprop.value); + return ret; } void obt_prop_set32(Window win, Atom prop, Atom type, gulong val) @@ -408,53 +532,23 @@ void obt_prop_set_array32(Window win, Atom prop, Atom type, gulong *val, (guchar*)val, num); } -void obt_prop_set_string_locale(Window win, Atom prop, const gchar *val) -{ - gchar const *s[2] = { val, NULL }; - obt_prop_set_strings_locale(win, prop, s); -} - -void obt_prop_set_strings_locale(Window win, Atom prop, const gchar **strs) -{ - gint i, count; - gchar **lstrs; - XTextProperty tprop; - - /* count the strings in strs, and convert them to the locale format */ - for (count = 0; strs[count]; ++count); - lstrs = g_new0(char*, count); - for (i = 0; i < count; ++i) { - lstrs[i] = g_locale_from_utf8(strs[i], -1, NULL, NULL, NULL); - if (!lstrs[i]) { - lstrs[i] = g_strdup(""); /* make it an empty string */ - g_warning("Unable to translate string '%s' from UTF8 to locale " - "format", strs[i]); - } - } - - - XStringListToTextProperty(lstrs, count, &tprop); - XSetTextProperty(obt_display, win, &tprop, prop); - XFree(tprop.value); -} - -void obt_prop_set_string_utf8(Window win, Atom prop, const gchar *val) +void obt_prop_set_text(Window win, Atom prop, const gchar *val) { - XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8), 8, + XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8, PropModeReplace, (const guchar*)val, strlen(val)); } -void obt_prop_set_strings_utf8(Window win, Atom prop, const gchar **strs) +void obt_prop_set_array_text(Window win, Atom prop, const gchar *const *strs) { GString *str; - gchar const **s; + gchar const *const *s; str = g_string_sized_new(0); for (s = strs; *s; ++s) { str = g_string_append(str, *s); str = g_string_append_c(str, '\0'); } - XChangeProperty(obt_display, win, prop, obt_prop_atom(OBT_PROP_UTF8), 8, + XChangeProperty(obt_display, win, prop, OBT_PROP_ATOM(UTF8_STRING), 8, PropModeReplace, (guchar*)str->str, str->len); g_string_free(str, TRUE); } @@ -468,7 +562,7 @@ void obt_prop_message(gint screen, Window about, Atom messagetype, glong data0, glong data1, glong data2, glong data3, glong data4, glong mask) { - obt_prop_message_to(RootWindow(obt_display, screen), about, messagetype, + obt_prop_message_to(obt_root(screen), about, messagetype, data0, data1, data2, data3, data4, mask); }