~ctwm/ctwm/trunk

« back to all changes in this revision

Viewing changes to win_utils.c

  • Committer: Matthew Fuller
  • Date: 2018-08-19 21:35:15 UTC
  • mfrom: (615.1.56 winnames)
  • Revision ID: fullermd@over-yonder.net-20180819213515-iauyfvvadd8on55i
Merge support for EWMH _NET_WM{_ICON,}_NAME, and our own overriding
CTWM_WM{_ICON,}_NAME.

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
#include "ctwm_atoms.h"
14
14
#include "drawing.h"
15
15
#include "events.h"
 
16
#include "event_internal.h" // Temp?
 
17
#ifdef EWMH
 
18
# include "ewmh_atoms.h"
 
19
#endif
16
20
#include "icons.h"
 
21
#include "occupation.h"
 
22
#include "otp.h"
17
23
#include "screen.h"
18
24
#include "util.h"
19
25
#include "win_decorations.h"
215
221
        char                *stringptr;
216
222
 
217
223
        XGetTextProperty(dpy, w, &text_prop, prop);
218
 
        if(text_prop.value != NULL) {
 
224
        if(text_prop.value == NULL) {
 
225
                return NULL;
 
226
        }
 
227
 
 
228
        if(text_prop.encoding == XA_STRING
 
229
                        || text_prop.encoding == XA_UTF8_STRING
 
230
                        || text_prop.encoding == XA_COMPOUND_TEXT) {
 
231
                /* property is encoded as compound text - convert to locale string */
219
232
                char **text_list;
220
233
                int  text_list_count;
221
 
 
222
 
                if(text_prop.encoding == XA_STRING
223
 
                                || text_prop.encoding == XA_COMPOUND_TEXT) {
224
 
                        /* property is encoded as compound text - convert to locale string */
225
 
                        int status = XmbTextPropertyToTextList(dpy, &text_prop, &text_list,
226
 
                                                               &text_list_count);
227
 
                        if(text_list_count == 0) {
228
 
                                stringptr = NULL;
229
 
                        }
230
 
                        else if(text_list == NULL) {
231
 
                                stringptr = NULL;
232
 
                        }
233
 
                        else if(text_list [0] == NULL) {
234
 
                                stringptr = NULL;
235
 
                        }
236
 
                        else if(status < 0 || text_list_count < 0) {
237
 
                                switch(status) {
238
 
                                        case XConverterNotFound:
239
 
                                                fprintf(stderr,
240
 
                                                        "%s: Converter not found; unable to convert property %s of window ID %lx.\n",
241
 
                                                        ProgramName, XGetAtomName(dpy, prop), w);
242
 
                                                break;
243
 
                                        case XNoMemory:
244
 
                                                fprintf(stderr,
245
 
                                                        "%s: Insufficient memory; unable to convert property %s of window ID %lx.\n",
246
 
                                                        ProgramName, XGetAtomName(dpy, prop), w);
247
 
                                                break;
248
 
                                        case XLocaleNotSupported:
249
 
                                                fprintf(stderr,
250
 
                                                        "%s: Locale not supported; unable to convert property %s of window ID %lx.\n",
251
 
                                                        ProgramName, XGetAtomName(dpy, prop), w);
252
 
                                                break;
253
 
                                }
254
 
                                stringptr = NULL;
255
 
                                /*
256
 
                                   don't call XFreeStringList - text_list appears to have
257
 
                                   invalid address if status is bad
258
 
                                   XFreeStringList(text_list);
259
 
                                */
260
 
                        }
261
 
                        else {
262
 
                                stringptr = strdup(text_list[0]);
263
 
                                XFreeStringList(text_list);
264
 
                        }
 
234
                int status;
 
235
 
 
236
                /* Check historical strictness */
 
237
                if(Scr->StrictWinNameEncoding) {
 
238
                        bool fail = false;
 
239
 
 
240
                        if((prop == XA_WM_NAME || prop == XA_WM_ICON_NAME)
 
241
                                        && text_prop.encoding != XA_STRING
 
242
                                        && text_prop.encoding != XA_COMPOUND_TEXT) {
 
243
                                fail = true;
 
244
                        }
 
245
 
 
246
#ifdef EWMH
 
247
                        if((prop == XA__NET_WM_NAME || prop == XA__NET_WM_ICON_NAME)
 
248
                                        && text_prop.encoding != XA_UTF8_STRING) {
 
249
                                fail = true;
 
250
                        }
 
251
#endif // EWMH
 
252
 
 
253
                        if(fail) {
 
254
                                fprintf(stderr, "%s: Invalid encoding for property %s "
 
255
                                        "of window 0x%lx\n", ProgramName,
 
256
                                        XGetAtomName(dpy, prop), w);
 
257
                                XFree(text_prop.value);
 
258
                                return NULL;
 
259
                        }
 
260
                }
 
261
 
 
262
 
 
263
                status = XmbTextPropertyToTextList(dpy, &text_prop, &text_list,
 
264
                                                   &text_list_count);
 
265
                if(text_list_count == 0
 
266
                                || text_list == NULL
 
267
                                || text_list[0] == NULL) {
 
268
                        // Got nothing
 
269
                        XFree(text_prop.value);
 
270
                        return NULL;
 
271
                }
 
272
                else if(status < 0 || text_list_count < 0) {
 
273
                        // Got an error statuf
 
274
                        switch(status) {
 
275
                                case XConverterNotFound:
 
276
                                        fprintf(stderr,
 
277
                                                "%s: Converter not found; unable to convert property %s of window ID %lx.\n",
 
278
                                                ProgramName, XGetAtomName(dpy, prop), w);
 
279
                                        break;
 
280
                                case XNoMemory:
 
281
                                        fprintf(stderr,
 
282
                                                "%s: Insufficient memory; unable to convert property %s of window ID %lx.\n",
 
283
                                                ProgramName, XGetAtomName(dpy, prop), w);
 
284
                                        break;
 
285
                                case XLocaleNotSupported:
 
286
                                        fprintf(stderr,
 
287
                                                "%s: Locale not supported; unable to convert property %s of window ID %lx.\n",
 
288
                                                ProgramName, XGetAtomName(dpy, prop), w);
 
289
                                        break;
 
290
                        }
 
291
                        stringptr = NULL;
 
292
                        /*
 
293
                           don't call XFreeStringList - text_list appears to have
 
294
                           invalid address if status is bad
 
295
                           XFreeStringList(text_list);
 
296
                        */
265
297
                }
266
298
                else {
267
 
                        /* property is encoded in a format we don't understand */
268
 
                        fprintf(stderr,
269
 
                                "%s: Encoding not STRING or COMPOUND_TEXT; unable to decode property %s of window ID %lx.\n",
270
 
                                ProgramName, XGetAtomName(dpy, prop), w);
271
 
                        stringptr = NULL;
 
299
                        // Actually got the data!
 
300
                        stringptr = strdup(text_list[0]);
 
301
                        XFreeStringList(text_list);
272
302
                }
273
 
                XFree(text_prop.value);
274
303
        }
275
304
        else {
 
305
                /* property is encoded in a format we don't understand */
 
306
                fprintf(stderr,
 
307
                        "%s: Encoding not STRING or COMPOUND_TEXT; unable to decode property %s of window ID %lx.\n",
 
308
                        ProgramName, XGetAtomName(dpy, prop), w);
276
309
                stringptr = NULL;
277
310
        }
 
311
        XFree(text_prop.value);
278
312
 
279
313
        return stringptr;
280
314
}
902
936
 
903
937
        return hints;
904
938
}
 
939
 
 
940
 
 
941
/**
 
942
 * [Re]set a window's name.  This goes over the available naming sources
 
943
 * for the window and points the TwmWindow::name at the appropriate one.
 
944
 * It may also set a property to signal other EWMH-aware clients when
 
945
 * we're naming it a way they can't see themselves.
 
946
 *
 
947
 * \note This should rarely be called directly; apply_window_name()
 
948
 * should be used instead.  It's split out because we need to do this
 
949
 * step individually in AddWindow().
 
950
 *
 
951
 * \note Note also that we never need to worry about freeing the
 
952
 * TwmWindow::name; it always points to one of the TwmWindow::names
 
953
 * values (which are free'd by the event handler when they change) or to
 
954
 * NoName (which is static).  So we can just casually flip it around at
 
955
 * will.
 
956
 */
 
957
bool
 
958
set_window_name(TwmWindow *win)
 
959
{
 
960
        char *newname = NULL;
 
961
#define TRY(fld) { \
 
962
                if(newname == NULL && win->names.fld != NULL) { \
 
963
                        newname = win->names.fld; \
 
964
                } \
 
965
        }
 
966
        TRY(ctwm_wm_name)
 
967
#ifdef EWMH
 
968
        TRY(net_wm_name)
 
969
#endif
 
970
        TRY(wm_name)
 
971
#undef TRY
 
972
 
 
973
        if(newname == NULL) {
 
974
                newname = NoName;
 
975
        }
 
976
        if(win->name == newname) {
 
977
                return false; // Nothing to do
 
978
        }
 
979
 
 
980
        // Now we know what to call it
 
981
        win->name = newname;
 
982
 
 
983
#ifdef EWMH
 
984
        // EWMH says we set an additional property on any windows where what
 
985
        // we consider the name isn't what's in _NET_WM_NAME, so pagers etc
 
986
        // can call it the same as we do.
 
987
        //
 
988
        // The parts of the text describing it conflict a little; at one
 
989
        // place, it implies this should be set unless we're using
 
990
        // _NET_WM_NAME, in another it seems to suggest WM_NAME should be
 
991
        // considered applicable too.  I choose to implement it excluding
 
992
        // both, so this only gets set if we're overriding either standard
 
993
        // naming (probably rare).
 
994
        if(win->name != win->names.net_wm_name && win->name != win->names.wm_name) {
 
995
                // XXX We're not doing any checking of the encoding here...  I
 
996
                // don't see that Xlib helps us any, so we probably have to fall
 
997
                // back to iconv?  That came into the base in POSIX 2008, but was
 
998
                // in XSI back into the 90's I believe?
 
999
                XChangeProperty(dpy, win->w, XA__NET_WM_VISIBLE_NAME, XA_UTF8_STRING,
 
1000
                                8, PropModeReplace, (unsigned char *)win->name,
 
1001
                                strlen(win->name));
 
1002
        }
 
1003
        else {
 
1004
                XDeleteProperty(dpy, win->w, XA__NET_WM_VISIBLE_NAME);
 
1005
        }
 
1006
#endif // EWMH
 
1007
 
 
1008
        // We set a name
 
1009
        return true;
 
1010
}
 
1011
 
 
1012
 
 
1013
/**
 
1014
 * [Re]set and apply changes to a window's name.  This is called after
 
1015
 * we've received a new WM_NAME (or other name-setting) property, to
 
1016
 * update our titlebars, icon managers, etc.
 
1017
 */
 
1018
void
 
1019
apply_window_name(TwmWindow *win)
 
1020
{
 
1021
        /* [Re]set ->name */
 
1022
        if(set_window_name(win) == false) {
 
1023
                // No change
 
1024
                return;
 
1025
        }
 
1026
        win->nameChanged = true;
 
1027
 
 
1028
 
 
1029
        /* Update the active name */
 
1030
        {
 
1031
                XRectangle inc_rect;
 
1032
                XRectangle logical_rect;
 
1033
 
 
1034
                XmbTextExtents(Scr->TitleBarFont.font_set,
 
1035
                               win->name, strlen(win->name),
 
1036
                               &inc_rect, &logical_rect);
 
1037
                win->name_width = logical_rect.width;
 
1038
        }
 
1039
 
 
1040
        /* recompute the priority if necessary */
 
1041
        if(Scr->AutoPriority) {
 
1042
                OtpRecomputePrefs(win);
 
1043
        }
 
1044
 
 
1045
        SetupWindow(win, win->frame_x, win->frame_y,
 
1046
                    win->frame_width, win->frame_height, -1);
 
1047
 
 
1048
        if(win->title_w) {
 
1049
                XClearArea(dpy, win->title_w, 0, 0, 0, 0, True);
 
1050
        }
 
1051
        if(Scr->AutoOccupy) {
 
1052
                WmgrRedoOccupation(win);
 
1053
        }
 
1054
 
 
1055
#if 0
 
1056
        /* Experimental, not yet working. */
 
1057
        {
 
1058
                ColorPair cp;
 
1059
                int f, b;
 
1060
 
 
1061
                f = GetColorFromList(Scr->TitleForegroundL, win->name,
 
1062
                                     &win->class, &cp.fore);
 
1063
                b = GetColorFromList(Scr->TitleBackgroundL, win->name,
 
1064
                                     &win->class, &cp.back);
 
1065
                if(f || b) {
 
1066
                        if(Scr->use3Dtitles  && !Scr->BeNiceToColormap) {
 
1067
                                GetShadeColors(&cp);
 
1068
                        }
 
1069
                        win->title = cp;
 
1070
                }
 
1071
                f = GetColorFromList(Scr->BorderColorL, win->name,
 
1072
                                     &win->class, &cp.fore);
 
1073
                b = GetColorFromList(Scr->BorderColorL, win->name,
 
1074
                                     &win->class, &cp.back);
 
1075
                if(f || b) {
 
1076
                        if(Scr->use3Dborders && !Scr->BeNiceToColormap) {
 
1077
                                GetShadeColors(&cp);
 
1078
                        }
 
1079
                        win->borderC = cp;
 
1080
                }
 
1081
 
 
1082
                f = GetColorFromList(Scr->BorderTileForegroundL, win->name,
 
1083
                                     &win->class, &cp.fore);
 
1084
                b = GetColorFromList(Scr->BorderTileBackgroundL, win->name,
 
1085
                                     &win->class, &cp.back);
 
1086
                if(f || b) {
 
1087
                        if(Scr->use3Dborders && !Scr->BeNiceToColormap) {
 
1088
                                GetShadeColors(&cp);
 
1089
                        }
 
1090
                        win->border_tile = cp;
 
1091
                }
 
1092
        }
 
1093
#endif
 
1094
 
 
1095
        /*
 
1096
         * If we haven't set a separate icon name, we use the window name, so
 
1097
         * we need to update it.
 
1098
         */
 
1099
        if(win->names.icon_set == false) {
 
1100
                apply_window_icon_name(win);
 
1101
        }
 
1102
        AutoPopupMaybe(win);
 
1103
 
 
1104
        return;
 
1105
}
 
1106
 
 
1107
 
 
1108
/**
 
1109
 * [Re]set a window's icon name.  As with the window name version in
 
1110
 * set_window_name(), this is mostly separate so the AddWindow() process
 
1111
 * can call it.
 
1112
 *
 
1113
 * \note As with TwmWindow::name, we never want to try free()'ing or the
 
1114
 * like TwmWindow::icon_name.
 
1115
 *
 
1116
 * \sa set_window_name() for details; this is just the icon name
 
1117
 * equivalent of it.
 
1118
 */
 
1119
bool
 
1120
set_window_icon_name(TwmWindow *win)
 
1121
{
 
1122
        char *newname = NULL;
 
1123
#define TRY(fld) { \
 
1124
                if(newname == NULL && win->names.fld != NULL) { \
 
1125
                        newname = win->names.fld; \
 
1126
                        win->names.icon_set = true; \
 
1127
                } \
 
1128
        }
 
1129
        TRY(ctwm_wm_icon_name)
 
1130
#ifdef EWMH
 
1131
        TRY(net_wm_icon_name)
 
1132
#endif
 
1133
        TRY(wm_icon_name)
 
1134
#undef TRY
 
1135
 
 
1136
        // Our fallback for icon names is the window name.  Flag when we're
 
1137
        // doing that, so the window name handler can know when it needs to
 
1138
        // call us.
 
1139
        if(newname == NULL) {
 
1140
                newname = win->name;
 
1141
                win->names.icon_set = false;
 
1142
        }
 
1143
        if(win->icon_name == newname) {
 
1144
                return false; // Nothing to do
 
1145
        }
 
1146
 
 
1147
        // A name is chosen
 
1148
        win->icon_name = newname;
 
1149
 
 
1150
#ifdef EWMH
 
1151
        // EWMH asks for _NET_WM_VISIBLE_ICON_NAME in various cases where
 
1152
        // we're not using 'standard' properties' values.  x-ref comments above in
 
1153
        // set_window_name() about the parallel property for the window name
 
1154
        // for various caveats.
 
1155
        if(win->icon_name != win->names.net_wm_icon_name
 
1156
                        && win->icon_name != win->names.wm_icon_name) {
 
1157
                // XXX Still encoding questionable; x-ref above.
 
1158
                XChangeProperty(dpy, win->w, XA__NET_WM_VISIBLE_ICON_NAME,
 
1159
                                XA_UTF8_STRING,
 
1160
                                8, PropModeReplace, (unsigned char *)win->icon_name,
 
1161
                                strlen(win->icon_name));
 
1162
        }
 
1163
        else {
 
1164
                XDeleteProperty(dpy, win->w, XA__NET_WM_VISIBLE_ICON_NAME);
 
1165
        }
 
1166
#endif // EWMH
 
1167
 
 
1168
        // Did it
 
1169
        return true;
 
1170
}
 
1171
 
 
1172
 
 
1173
/**
 
1174
 * [Re]set and apply changes to a window's icon name.  This is called
 
1175
 * after we've received a new WM_ICON_NAME (or other name-setting)
 
1176
 * property, to update our titlebars, icon managers, etc.
 
1177
 *
 
1178
 * \sa apply_window_name() which does the same for the window title.
 
1179
 */
 
1180
void
 
1181
apply_window_icon_name(TwmWindow *win)
 
1182
{
 
1183
        /* [Re]set ->icon_name */
 
1184
        if(set_window_icon_name(win) == false) {
 
1185
                // No change
 
1186
                return;
 
1187
        }
 
1188
 
 
1189
 
 
1190
        /* Lot less to do for icons... */
 
1191
        RedoIcon(Tmp_win);
 
1192
        AutoPopupMaybe(Tmp_win);
 
1193
 
 
1194
        return;
 
1195
}