misc.c revision d1603bab
1d1603babSmrg/* $XTermId: misc.c,v 1.1045 2023/03/31 23:03:37 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4d1603babSmrg * Copyright 1999-2022,2023 by Thomas E. Dickey
5d522f475Smrg *
6cd3331d0Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg#include <version.h>
56b7c89284Ssnj#include <main.h>
57d522f475Smrg#include <xterm.h>
58dfb07bc7Smrg#include <xterm_io.h>
59d522f475Smrg
60d522f475Smrg#include <sys/stat.h>
61d522f475Smrg#include <stdio.h>
623367019cSmrg#include <stdarg.h>
63d522f475Smrg#include <signal.h>
64d522f475Smrg#include <ctype.h>
65d522f475Smrg#include <pwd.h>
66d522f475Smrg#include <sys/wait.h>
67d522f475Smrg
68d522f475Smrg#include <X11/keysym.h>
69d522f475Smrg#include <X11/Xatom.h>
70d522f475Smrg
71d522f475Smrg#include <X11/Xmu/Error.h>
72d522f475Smrg#include <X11/Xmu/SysUtil.h>
73d522f475Smrg#include <X11/Xmu/WinUtil.h>
74d522f475Smrg#include <X11/Xmu/Xmu.h>
75d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
76d522f475Smrg#include <X11/Sunkeysym.h>
77d522f475Smrg#endif
78d522f475Smrg
793367019cSmrg#ifdef HAVE_LIBXPM
803367019cSmrg#include <X11/xpm.h>
813367019cSmrg#endif
823367019cSmrg
83d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
84d522f475Smrg#include <langinfo.h>
85d522f475Smrg#endif
86d522f475Smrg
87d522f475Smrg#include <xutf8.h>
88d522f475Smrg
89d522f475Smrg#include <data.h>
90d522f475Smrg#include <error.h>
91d522f475Smrg#include <menu.h>
92d522f475Smrg#include <fontutils.h>
93d522f475Smrg#include <xstrings.h>
94d522f475Smrg#include <xtermcap.h>
95d522f475Smrg#include <VTparse.h>
96fa3f02f3Smrg#include <graphics.h>
979a64e1c5Smrg#include <graphics_regis.h>
989a64e1c5Smrg#include <graphics_sixel.h>
99d522f475Smrg
100d522f475Smrg#include <assert.h>
101d522f475Smrg
102d1603babSmrg#ifdef HAVE_MKSTEMP
103d1603babSmrg#define MakeTemp(f) mkstemp(f)
104d1603babSmrg#else
105d1603babSmrg#define MakeTemp(f) mktemp(f)
106d1603babSmrg#endif
107d1603babSmrg
108d522f475Smrg#ifdef VMS
109d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
110d522f475Smrg#ifdef ALLOWLOGFILEEXEC
111d522f475Smrg#undef ALLOWLOGFILEEXEC
112d522f475Smrg#endif
113d522f475Smrg#endif /* VMS */
114d522f475Smrg
115d4fba8b9Smrg#if USE_DOUBLE_BUFFER
116d4fba8b9Smrg#include <X11/extensions/Xdbe.h>
117d4fba8b9Smrg#endif
118d4fba8b9Smrg
119d4fba8b9Smrg#if OPT_WIDE_CHARS
120d4fba8b9Smrg#include <wctype.h>
121d4fba8b9Smrg#endif
122d4fba8b9Smrg
123d522f475Smrg#if OPT_TEK4014
124d522f475Smrg#define OUR_EVENT(event,Type) \
125d522f475Smrg		(event.type == Type && \
126d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
127d522f475Smrg		    (tekWidget && \
128d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
129d522f475Smrg#else
130d522f475Smrg#define OUR_EVENT(event,Type) \
131d522f475Smrg		(event.type == Type && \
132d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
133d522f475Smrg#endif
134d522f475Smrg
135d4fba8b9Smrg#define VB_DELAY    screen->visualBellDelay
136d4fba8b9Smrg#define EVENT_DELAY TScreenOf(term)->nextEventDelay
137d4fba8b9Smrg
1383367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
139d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
140d522f475Smrg
1413367019cSmrgstatic char emptyString[] = "";
1423367019cSmrg
143d522f475Smrg#if OPT_EXEC_XTERM
144d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
145d522f475Smrg   error; adapted from libc docs */
146d522f475Smrgstatic char *
147d522f475SmrgReadlink(const char *filename)
148d522f475Smrg{
149d522f475Smrg    char *buf = NULL;
150cd3331d0Smrg    size_t size = 100;
151d522f475Smrg
152d522f475Smrg    for (;;) {
153037a25ddSmrg	int n;
154037a25ddSmrg	char *tmp = TypeRealloc(char, size, buf);
155037a25ddSmrg	if (tmp == NULL) {
156037a25ddSmrg	    free(buf);
157037a25ddSmrg	    return NULL;
158037a25ddSmrg	}
159037a25ddSmrg	buf = tmp;
160d522f475Smrg	memset(buf, 0, size);
161d522f475Smrg
162cd3331d0Smrg	n = (int) readlink(filename, buf, size);
163d522f475Smrg	if (n < 0) {
164d522f475Smrg	    free(buf);
165d522f475Smrg	    return NULL;
166d522f475Smrg	}
167d522f475Smrg
168d522f475Smrg	if ((unsigned) n < size) {
169d522f475Smrg	    return buf;
170d522f475Smrg	}
171d522f475Smrg
172d522f475Smrg	size *= 2;
173d522f475Smrg    }
174d522f475Smrg}
175d522f475Smrg#endif /* OPT_EXEC_XTERM */
176d522f475Smrg
177d522f475Smrgstatic void
178d522f475SmrgSleep(int msec)
179d522f475Smrg{
180d522f475Smrg    static struct timeval select_timeout;
181d522f475Smrg
182d522f475Smrg    select_timeout.tv_sec = 0;
183d522f475Smrg    select_timeout.tv_usec = msec * 1000;
184d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
185d522f475Smrg}
186d522f475Smrg
187d522f475Smrgstatic void
1883367019cSmrgselectwindow(XtermWidget xw, int flag)
189d522f475Smrg{
1903367019cSmrg    TScreen *screen = TScreenOf(xw);
1913367019cSmrg
192d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
193d522f475Smrg
194d522f475Smrg#if OPT_TEK4014
1953367019cSmrg    if (TEK4014_ACTIVE(xw)) {
196d522f475Smrg	if (!Ttoggled)
197d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
198d522f475Smrg	screen->select |= flag;
199d522f475Smrg	if (!Ttoggled)
200d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
201d522f475Smrg    } else
202d522f475Smrg#endif
203d522f475Smrg    {
204d4fba8b9Smrg#if OPT_INPUT_METHOD
2053367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
2063367019cSmrg	if (input && input->xic)
2073367019cSmrg	    XSetICFocus(input->xic);
2083367019cSmrg#endif
209d522f475Smrg
210d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
211d4fba8b9Smrg	    HideCursor(xw);
212d522f475Smrg	screen->select |= flag;
213d522f475Smrg	if (screen->cursor_state)
214d4fba8b9Smrg	    ShowCursor(xw);
215d522f475Smrg    }
216cd3331d0Smrg    GetScrollLock(screen);
217d522f475Smrg}
218d522f475Smrg
219d522f475Smrgstatic void
2203367019cSmrgunselectwindow(XtermWidget xw, int flag)
221d522f475Smrg{
2223367019cSmrg    TScreen *screen = TScreenOf(xw);
2233367019cSmrg
224d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
225d522f475Smrg
2263367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
227d522f475Smrg	screen->hide_pointer = False;
2288f44fb3bSmrg	xtermDisplayPointer(xw);
229d522f475Smrg    }
230d522f475Smrg
231d4fba8b9Smrg    screen->select &= ~flag;
232d4fba8b9Smrg
233d522f475Smrg    if (!screen->always_highlight) {
234d522f475Smrg#if OPT_TEK4014
2353367019cSmrg	if (TEK4014_ACTIVE(xw)) {
236d522f475Smrg	    if (!Ttoggled)
237d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
238d522f475Smrg	    if (!Ttoggled)
239d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
240d522f475Smrg	} else
241d522f475Smrg#endif
242d522f475Smrg	{
243d4fba8b9Smrg#if OPT_INPUT_METHOD
2443367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2453367019cSmrg	    if (input && input->xic)
2463367019cSmrg		XUnsetICFocus(input->xic);
2473367019cSmrg#endif
248d522f475Smrg
249d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
250d4fba8b9Smrg		HideCursor(xw);
251d522f475Smrg	    if (screen->cursor_state)
252d4fba8b9Smrg		ShowCursor(xw);
253d522f475Smrg	}
254d522f475Smrg    }
255d522f475Smrg}
256d522f475Smrg
257d522f475Smrgstatic void
2589a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
259d522f475Smrg{
260d522f475Smrg    TScreen *screen = TScreenOf(xw);
261d522f475Smrg
262d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
263cd3331d0Smrg    TRACE_FOCUS(xw, ev);
264cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
265cd3331d0Smrg	ev->focus &&
266cd3331d0Smrg	!(screen->select & FOCUS))
2673367019cSmrg	selectwindow(xw, INWINDOW);
268d522f475Smrg}
269d522f475Smrg
270d522f475Smrgstatic void
2719a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
272d522f475Smrg{
273d522f475Smrg    TScreen *screen = TScreenOf(xw);
274d522f475Smrg
275d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
276cd3331d0Smrg    TRACE_FOCUS(xw, ev);
277cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
278cd3331d0Smrg	ev->focus &&
279cd3331d0Smrg	!(screen->select & FOCUS))
2803367019cSmrg	unselectwindow(xw, INWINDOW);
281d522f475Smrg}
282d522f475Smrg
283d522f475Smrg#ifndef XUrgencyHint
284d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
285d522f475Smrg#endif
286d522f475Smrg
287d522f475Smrgstatic void
288c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
289d522f475Smrg{
290c219fbebSmrg    TScreen *screen = TScreenOf(xw);
291c219fbebSmrg
292d522f475Smrg    if (screen->bellIsUrgent) {
293c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
294d522f475Smrg	if (h != 0) {
295c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
296d522f475Smrg		h->flags |= XUrgencyHint;
297d522f475Smrg	    } else {
298d522f475Smrg		h->flags &= ~XUrgencyHint;
299d522f475Smrg	    }
300c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
301d522f475Smrg	}
302d522f475Smrg    }
303d522f475Smrg}
304d522f475Smrg
305d522f475Smrgvoid
306d4fba8b9Smrgdo_xevents(XtermWidget xw)
307d522f475Smrg{
308d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
309d522f475Smrg
3103367019cSmrg    if (xtermAppPending()
311d522f475Smrg	||
312d522f475Smrg#if defined(VMS) || defined(__VMS)
313d522f475Smrg	screen->display->qlen > 0
314d522f475Smrg#else
315d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
316d522f475Smrg#endif
317d522f475Smrg	)
318d4fba8b9Smrg	xevents(xw);
319d522f475Smrg}
320d522f475Smrg
321d522f475Smrgvoid
3228f44fb3bSmrgxtermDisplayPointer(XtermWidget xw)
323d522f475Smrg{
324d522f475Smrg    TScreen *screen = TScreenOf(xw);
325d522f475Smrg
326d522f475Smrg    if (screen->Vshow) {
327d522f475Smrg	if (screen->hide_pointer) {
3288f44fb3bSmrg	    TRACE(("Display text pointer (hidden)\n"));
329d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
330d522f475Smrg	} else {
3318f44fb3bSmrg	    TRACE(("Display text pointer (visible)\n"));
332d522f475Smrg	    recolor_cursor(screen,
333d522f475Smrg			   screen->pointer_cursor,
334d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
335d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
336d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
337d522f475Smrg	}
338d522f475Smrg    }
339d522f475Smrg}
340d522f475Smrg
341d522f475Smrgvoid
342d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
343d522f475Smrg{
344d522f475Smrg    static int tried = -1;
345d522f475Smrg    TScreen *screen = TScreenOf(xw);
346d522f475Smrg
347d522f475Smrg#if OPT_TEK4014
348d522f475Smrg    if (TEK4014_SHOWN(xw))
349d522f475Smrg	enable = True;
350d522f475Smrg#endif
351d522f475Smrg
352d522f475Smrg    /*
353d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
354d522f475Smrg     * the mouse-mode:
355d522f475Smrg     */
356d522f475Smrg    if (!enable) {
357d522f475Smrg	switch (screen->pointer_mode) {
358d522f475Smrg	case pNever:
359d522f475Smrg	    enable = True;
360d522f475Smrg	    break;
361d522f475Smrg	case pNoMouse:
362d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
363d522f475Smrg		enable = True;
364d522f475Smrg	    break;
365d522f475Smrg	case pAlways:
3663367019cSmrg	case pFocused:
367d522f475Smrg	    break;
368d522f475Smrg	}
369d522f475Smrg    }
370d522f475Smrg
371d522f475Smrg    if (enable) {
372d522f475Smrg	if (screen->hide_pointer) {
373d522f475Smrg	    screen->hide_pointer = False;
3748f44fb3bSmrg	    xtermDisplayPointer(xw);
375d522f475Smrg	    switch (screen->send_mouse_pos) {
376d522f475Smrg	    case ANY_EVENT_MOUSE:
377d522f475Smrg		break;
378d522f475Smrg	    default:
379d522f475Smrg		MotionOff(screen, xw);
380d522f475Smrg		break;
381d522f475Smrg	    }
382d522f475Smrg	}
383d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
384d522f475Smrg	if (screen->hidden_cursor == 0) {
385d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
386d522f475Smrg	}
387d522f475Smrg	if (screen->hidden_cursor == 0) {
388d522f475Smrg	    tried = 1;
389d522f475Smrg	} else {
390d522f475Smrg	    tried = 0;
391d522f475Smrg	    screen->hide_pointer = True;
3928f44fb3bSmrg	    xtermDisplayPointer(xw);
393d522f475Smrg	    MotionOn(screen, xw);
394d522f475Smrg	}
395d522f475Smrg    }
396d522f475Smrg}
397d522f475Smrg
3983367019cSmrg/* true if p contains q */
3993367019cSmrg#define ExposeContains(p,q) \
4003367019cSmrg	    ((p)->y <= (q)->y \
4013367019cSmrg	  && (p)->x <= (q)->x \
4023367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
4033367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
4043367019cSmrg
4053367019cSmrgstatic XtInputMask
4069a64e1c5SmrgmergeExposeEvents(XEvent *target)
4073367019cSmrg{
4083367019cSmrg    XEvent next_event;
409037a25ddSmrg    XExposeEvent *p;
4103367019cSmrg
4113367019cSmrg    XtAppNextEvent(app_con, target);
4123367019cSmrg    p = (XExposeEvent *) target;
4133367019cSmrg
4143367019cSmrg    while (XtAppPending(app_con)
4153367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4163367019cSmrg	   && next_event.type == Expose) {
4173367019cSmrg	Boolean merge_this = False;
418d4fba8b9Smrg	XExposeEvent *q = (XExposeEvent *) (&next_event);
4193367019cSmrg
4203367019cSmrg	XtAppNextEvent(app_con, &next_event);
421d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4223367019cSmrg
4233367019cSmrg	/*
4243367019cSmrg	 * If either window is contained within the other, merge the events.
4253367019cSmrg	 * The traces show that there are also cases where a full repaint of
4263367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4273367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4283367019cSmrg	 * to skim several events ahead.
4293367019cSmrg	 */
4303367019cSmrg	if (p->window == q->window) {
4313367019cSmrg	    if (ExposeContains(p, q)) {
4323367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4333367019cSmrg		merge_this = True;
4343367019cSmrg		next_event = *target;
4353367019cSmrg	    } else if (ExposeContains(q, p)) {
4363367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4373367019cSmrg		merge_this = True;
4383367019cSmrg	    }
4393367019cSmrg	}
4403367019cSmrg	if (!merge_this) {
4413367019cSmrg	    XtDispatchEvent(target);
4423367019cSmrg	}
4433367019cSmrg	*target = next_event;
4443367019cSmrg    }
4453367019cSmrg    XtDispatchEvent(target);
4463367019cSmrg    return XtAppPending(app_con);
4473367019cSmrg}
4483367019cSmrg
4493367019cSmrg/*
4503367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4513367019cSmrg * event.  Remove that from the queue so we can look further.
4523367019cSmrg *
4533367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4543367019cSmrg * that.  If the adjacent events are for different windows, process the older
4553367019cSmrg * event and update the event used for comparing windows.  If they are for the
4563367019cSmrg * same window, only the newer event is of interest.
4573367019cSmrg *
4583367019cSmrg * Finally, process the (remaining) configure-notify event.
4593367019cSmrg */
4603367019cSmrgstatic XtInputMask
4619a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4623367019cSmrg{
4633367019cSmrg    XEvent next_event;
464037a25ddSmrg    XConfigureEvent *p;
4653367019cSmrg
4663367019cSmrg    XtAppNextEvent(app_con, target);
4673367019cSmrg    p = (XConfigureEvent *) target;
4683367019cSmrg
4693367019cSmrg    if (XtAppPending(app_con)
4703367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
4713367019cSmrg	&& next_event.type == ConfigureNotify) {
4723367019cSmrg	Boolean merge_this = False;
473d4fba8b9Smrg	XConfigureEvent *q = (XConfigureEvent *) (&next_event);
4743367019cSmrg
4753367019cSmrg	XtAppNextEvent(app_con, &next_event);
476d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4773367019cSmrg
4783367019cSmrg	if (p->window == q->window) {
4793367019cSmrg	    TRACE(("pending Configure...merged\n"));
4803367019cSmrg	    merge_this = True;
4813367019cSmrg	}
4823367019cSmrg	if (!merge_this) {
4833367019cSmrg	    TRACE(("pending Configure...skipped\n"));
4843367019cSmrg	    XtDispatchEvent(target);
4853367019cSmrg	}
4863367019cSmrg	*target = next_event;
4873367019cSmrg    }
4883367019cSmrg    XtDispatchEvent(target);
4893367019cSmrg    return XtAppPending(app_con);
4903367019cSmrg}
4913367019cSmrg
492d4fba8b9Smrg#define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
493d4fba8b9Smrg#define SameButtonEvent(a,b) ( \
494d4fba8b9Smrg	SAME(a,b,type) && \
495d4fba8b9Smrg	SAME(a,b,serial) && \
496d4fba8b9Smrg	SAME(a,b,send_event) && \
497d4fba8b9Smrg	SAME(a,b,display) && \
498d4fba8b9Smrg	SAME(a,b,window) && \
499d4fba8b9Smrg	SAME(a,b,root) && \
500d4fba8b9Smrg	SAME(a,b,subwindow) && \
501d4fba8b9Smrg	SAME(a,b,time) && \
502d4fba8b9Smrg	SAME(a,b,x) && \
503d4fba8b9Smrg	SAME(a,b,y) && \
504d4fba8b9Smrg	SAME(a,b,x_root) && \
505d4fba8b9Smrg	SAME(a,b,y_root) && \
506d4fba8b9Smrg	SAME(a,b,state) && \
507d4fba8b9Smrg	SAME(a,b,button) && \
508d4fba8b9Smrg	SAME(a,b,same_screen))
509d4fba8b9Smrg
510d4fba8b9Smrg/*
511d4fba8b9Smrg * Work around a bug in the X mouse code, which delivers duplicate events.
512d4fba8b9Smrg */
513d4fba8b9Smrgstatic XtInputMask
514d4fba8b9SmrgmergeButtonEvents(XEvent *target)
515d4fba8b9Smrg{
516d4fba8b9Smrg    XEvent next_event;
517d4fba8b9Smrg    XButtonEvent *p;
518d4fba8b9Smrg
519d4fba8b9Smrg    XtAppNextEvent(app_con, target);
520d4fba8b9Smrg    p = (XButtonEvent *) target;
521d4fba8b9Smrg
522d4fba8b9Smrg    if (XtAppPending(app_con)
523d4fba8b9Smrg	&& XtAppPeekEvent(app_con, &next_event)
524d4fba8b9Smrg	&& SameButtonEvent(target, &next_event)) {
525d4fba8b9Smrg	Boolean merge_this = False;
526d4fba8b9Smrg	XButtonEvent *q = (XButtonEvent *) (&next_event);
527d4fba8b9Smrg
528d4fba8b9Smrg	XtAppNextEvent(app_con, &next_event);
529d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
530d4fba8b9Smrg
531d4fba8b9Smrg	if (p->window == q->window) {
532d4fba8b9Smrg	    TRACE(("pending ButtonEvent...merged\n"));
533d4fba8b9Smrg	    merge_this = True;
534d4fba8b9Smrg	}
535d4fba8b9Smrg	if (!merge_this) {
536d4fba8b9Smrg	    TRACE(("pending ButtonEvent...skipped\n"));
537d4fba8b9Smrg	    XtDispatchEvent(target);
538d4fba8b9Smrg	}
539d4fba8b9Smrg	*target = next_event;
540d4fba8b9Smrg    }
541d4fba8b9Smrg    XtDispatchEvent(target);
542d4fba8b9Smrg    return XtAppPending(app_con);
543d4fba8b9Smrg}
544d4fba8b9Smrg
5453367019cSmrg/*
5463367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5473367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5483367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5493367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5503367019cSmrg * point.
5513367019cSmrg *
5523367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5533367019cSmrg */
5543367019cSmrgXtInputMask
5553367019cSmrgxtermAppPending(void)
5563367019cSmrg{
5573367019cSmrg    XtInputMask result = XtAppPending(app_con);
5583367019cSmrg    XEvent this_event;
559037a25ddSmrg    Boolean found = False;
5603367019cSmrg
5613367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
562037a25ddSmrg	found = True;
563d4fba8b9Smrg	TRACE_EVENT("pending", &this_event, (String *) 0, 0);
5643367019cSmrg	if (this_event.type == Expose) {
5653367019cSmrg	    result = mergeExposeEvents(&this_event);
5663367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5673367019cSmrg	    result = mergeConfigureEvents(&this_event);
568d4fba8b9Smrg	} else if (this_event.type == ButtonPress ||
569d4fba8b9Smrg		   this_event.type == ButtonRelease) {
570d4fba8b9Smrg	    result = mergeButtonEvents(&this_event);
5713367019cSmrg	} else {
5723367019cSmrg	    break;
5733367019cSmrg	}
5743367019cSmrg    }
575037a25ddSmrg
576037a25ddSmrg    /*
577037a25ddSmrg     * With NetBSD, closing a shell results in closing the X input event
578037a25ddSmrg     * stream, which interferes with the "-hold" option.  Wait a short time in
579037a25ddSmrg     * this case, to avoid max'ing the CPU.
580037a25ddSmrg     */
581037a25ddSmrg    if (hold_screen && caught_intr && !found) {
582d4fba8b9Smrg	Sleep(EVENT_DELAY);
583037a25ddSmrg    }
5843367019cSmrg    return result;
5853367019cSmrg}
5863367019cSmrg
587d522f475Smrgvoid
588d4fba8b9Smrgxevents(XtermWidget xw)
589d522f475Smrg{
590d522f475Smrg    TScreen *screen = TScreenOf(xw);
591d522f475Smrg    XEvent event;
592d522f475Smrg    XtInputMask input_mask;
593d522f475Smrg
594d522f475Smrg    if (need_cleanup)
5953367019cSmrg	NormalExit();
596d522f475Smrg
597d522f475Smrg    if (screen->scroll_amt)
598d522f475Smrg	FlushScroll(xw);
599d522f475Smrg    /*
600d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
601d522f475Smrg     * will process the timeout and return without blockng on the
602cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
603d522f475Smrg     * with select().
604d522f475Smrg     */
6053367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
606cd3331d0Smrg	if (input_mask & XtIMTimer)
607cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
608d522f475Smrg#if OPT_SESSION_MGT
609cd3331d0Smrg	/*
610cd3331d0Smrg	 * Session management events are alternative input events. Deal with
611cd3331d0Smrg	 * them in the same way.
612cd3331d0Smrg	 */
613cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
614cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
615d522f475Smrg#endif
616cd3331d0Smrg	else
617cd3331d0Smrg	    break;
618cd3331d0Smrg    }
619d522f475Smrg
620d522f475Smrg    /*
621d4fba8b9Smrg     * If there are no XEvents, don't wait around...
622d522f475Smrg     */
623d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
624d522f475Smrg	return;
625d522f475Smrg    do {
626d522f475Smrg	/*
627d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
628d522f475Smrg	 * We simply ignore all events except for those not passed down to
629d522f475Smrg	 * this function, e.g., those handled in in_put().
630d522f475Smrg	 */
631d522f475Smrg	if (screen->waitingForTrackInfo) {
632d4fba8b9Smrg	    Sleep(EVENT_DELAY);
633d522f475Smrg	    return;
634d522f475Smrg	}
635d522f475Smrg	XtAppNextEvent(app_con, &event);
636d522f475Smrg	/*
637d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
638d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
639d522f475Smrg	 * looking at the event ourselves we make sure that we can
640d522f475Smrg	 * do the right thing.
641d522f475Smrg	 */
642d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
643d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
644d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
645d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
646d4fba8b9Smrg	} else if (event.xany.type == MotionNotify
647d4fba8b9Smrg		   && event.xcrossing.window == XtWindow(xw)) {
648d4fba8b9Smrg	    switch (screen->send_mouse_pos) {
649d4fba8b9Smrg	    case ANY_EVENT_MOUSE:
650d522f475Smrg#if OPT_DEC_LOCATOR
651d4fba8b9Smrg	    case DEC_LOCATOR:
652d522f475Smrg#endif /* OPT_DEC_LOCATOR */
653d4fba8b9Smrg		SendMousePosition(xw, &event);
654d4fba8b9Smrg		xtermShowPointer(xw, True);
655d4fba8b9Smrg		continue;
656d4fba8b9Smrg	    case BTN_EVENT_MOUSE:
657d4fba8b9Smrg		SendMousePosition(xw, &event);
658d4fba8b9Smrg		xtermShowPointer(xw, True);
659d4fba8b9Smrg	    }
660d522f475Smrg	}
661d522f475Smrg
662cb4a1343Smrg	/*
663cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
664cb4a1343Smrg	 * mouse pointer back on.
665cb4a1343Smrg	 */
666cb4a1343Smrg	if (screen->hide_pointer) {
6673367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6683367019cSmrg		switch (event.xany.type) {
6693367019cSmrg		case MotionNotify:
6703367019cSmrg		    xtermShowPointer(xw, True);
6713367019cSmrg		    break;
6723367019cSmrg		}
6733367019cSmrg	    } else {
6743367019cSmrg		switch (event.xany.type) {
6753367019cSmrg		case KeyPress:
6763367019cSmrg		case KeyRelease:
6773367019cSmrg		case ButtonPress:
6783367019cSmrg		case ButtonRelease:
6793367019cSmrg		    /* also these... */
6803367019cSmrg		case Expose:
681037a25ddSmrg		case GraphicsExpose:
6823367019cSmrg		case NoExpose:
6833367019cSmrg		case PropertyNotify:
6843367019cSmrg		case ClientMessage:
6853367019cSmrg		    break;
6863367019cSmrg		default:
6873367019cSmrg		    xtermShowPointer(xw, True);
6883367019cSmrg		    break;
6893367019cSmrg		}
690cb4a1343Smrg	    }
691cb4a1343Smrg	}
692cb4a1343Smrg
693d522f475Smrg	if (!event.xany.send_event ||
694d522f475Smrg	    screen->allowSendEvents ||
695d522f475Smrg	    ((event.xany.type != KeyPress) &&
696d522f475Smrg	     (event.xany.type != KeyRelease) &&
697d522f475Smrg	     (event.xany.type != ButtonPress) &&
698d522f475Smrg	     (event.xany.type != ButtonRelease))) {
699d522f475Smrg
700d4fba8b9Smrg	    if (event.xany.type == MappingNotify) {
701d4fba8b9Smrg		XRefreshKeyboardMapping(&(event.xmapping));
702d4fba8b9Smrg		VTInitModifiers(xw);
703d4fba8b9Smrg	    }
704d522f475Smrg	    XtDispatchEvent(&event);
705d522f475Smrg	}
7063367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
707d522f475Smrg}
708d522f475Smrg
709d522f475Smrgstatic Cursor
710d522f475Smrgmake_hidden_cursor(XtermWidget xw)
711d522f475Smrg{
712d522f475Smrg    TScreen *screen = TScreenOf(xw);
713d522f475Smrg    Cursor c;
714d522f475Smrg    Display *dpy = screen->display;
715d522f475Smrg    XFontStruct *fn;
716d522f475Smrg
717d522f475Smrg    static XColor dummy;
718d522f475Smrg
719d522f475Smrg    /*
720d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
721d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
722b7c89284Ssnj     * server insists on drawing _something_.
723d522f475Smrg     */
724d522f475Smrg    TRACE(("Ask for nil2 font\n"));
7258f44fb3bSmrg    if ((fn = xtermLoadQueryFont(xw, "nil2")) == 0) {
726d522f475Smrg	TRACE(("...Ask for fixed font\n"));
7278f44fb3bSmrg	fn = xtermLoadQueryFont(xw, DEFFONT);
728d522f475Smrg    }
729d522f475Smrg
730d4fba8b9Smrg    if (fn != None) {
731d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
732d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
733d522f475Smrg	XFreeFont(dpy, fn);
734d522f475Smrg    } else {
735d4fba8b9Smrg	c = None;
736d522f475Smrg    }
737d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
738d4fba8b9Smrg    return c;
739d522f475Smrg}
740d522f475Smrg
741fa3f02f3Smrg/*
742fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
743fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
744fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
745fa3f02f3Smrg * until the window is initialized.
746fa3f02f3Smrg */
7478f44fb3bSmrg#ifdef HAVE_LIB_XCURSOR
748fa3f02f3Smrgvoid
749037a25ddSmrginit_colored_cursor(Display *dpy)
750fa3f02f3Smrg{
75194644356Smrg    static const char theme[] = "index.theme";
752d1603babSmrg    static const char pattern[] = "xtermXXXXXXXX";
753fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
754fa3f02f3Smrg
755fa3f02f3Smrg    xterm_cursor_theme = 0;
756037a25ddSmrg    /*
757037a25ddSmrg     * The environment variable overrides a (possible) resource Xcursor.theme
758037a25ddSmrg     */
759fa3f02f3Smrg    if (IsEmpty(env)) {
760037a25ddSmrg	env = XGetDefault(dpy, "Xcursor", "theme");
7618f44fb3bSmrg	TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
7628f44fb3bSmrg    } else {
7638f44fb3bSmrg	TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
764037a25ddSmrg    }
7658f44fb3bSmrg
766037a25ddSmrg    /*
767037a25ddSmrg     * If neither found, provide our own default theme.
768037a25ddSmrg     */
769037a25ddSmrg    if (IsEmpty(env)) {
770037a25ddSmrg	const char *tmp_dir;
771037a25ddSmrg	char *filename;
772037a25ddSmrg	size_t needed;
773037a25ddSmrg
7748f44fb3bSmrg	TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
7758f44fb3bSmrg
776fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
777fa3f02f3Smrg	    tmp_dir = P_tmpdir;
778fa3f02f3Smrg	}
779fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
780fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
781fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
782fa3f02f3Smrg
783fa3f02f3Smrg#ifdef HAVE_MKDTEMP
784fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
785fa3f02f3Smrg#else
786d1603babSmrg	    if (MakeTemp(filename) != 0
787fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
788fa3f02f3Smrg		xterm_cursor_theme = filename;
789fa3f02f3Smrg	    }
790fa3f02f3Smrg#endif
791d4fba8b9Smrg	    if (xterm_cursor_theme != filename)
792d4fba8b9Smrg		free(filename);
793fa3f02f3Smrg	    /*
794fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
795fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
796fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
797fa3f02f3Smrg	     */
798fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
799fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
800037a25ddSmrg		FILE *fp;
801037a25ddSmrg
802fa3f02f3Smrg		strcat(leaf, "/");
803fa3f02f3Smrg		strcat(leaf, theme);
8048f44fb3bSmrg
805fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
806fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
807fa3f02f3Smrg		    fclose(fp);
808fa3f02f3Smrg		    *leaf = '\0';
809fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
810fa3f02f3Smrg		    *leaf = '/';
8118f44fb3bSmrg
8128f44fb3bSmrg		    TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
8138f44fb3bSmrg			   xterm_cursor_theme));
8148f44fb3bSmrg		    atexit(cleanup_colored_cursor);
8158f44fb3bSmrg		} else {
8168f44fb3bSmrg		    FreeAndNull(xterm_cursor_theme);
817fa3f02f3Smrg		}
818fa3f02f3Smrg	    }
819fa3f02f3Smrg	}
820fa3f02f3Smrg    }
821fa3f02f3Smrg}
8228f44fb3bSmrg#endif /* HAVE_LIB_XCURSOR */
823fa3f02f3Smrg
824fa3f02f3Smrg/*
825fa3f02f3Smrg * Once done, discard the file and directory holding it.
826fa3f02f3Smrg */
827fa3f02f3Smrgvoid
828fa3f02f3Smrgcleanup_colored_cursor(void)
829fa3f02f3Smrg{
830fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
831fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
832fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
833fa3f02f3Smrg	struct stat sb;
834fa3f02f3Smrg	if (!IsEmpty(my_path)
835fa3f02f3Smrg	    && stat(my_path, &sb) == 0
836fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
837fa3f02f3Smrg	    unlink(xterm_cursor_theme);
838fa3f02f3Smrg	    rmdir(my_path);
839fa3f02f3Smrg	}
8408f44fb3bSmrg	FreeAndNull(xterm_cursor_theme);
841fa3f02f3Smrg    }
842fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
843fa3f02f3Smrg}
844fa3f02f3Smrg
845d522f475SmrgCursor
846d4fba8b9Smrgmake_colored_cursor(unsigned c_index,		/* index into font */
847d522f475Smrg		    unsigned long fg,	/* pixel value */
848d522f475Smrg		    unsigned long bg)	/* pixel value */
849d522f475Smrg{
850d522f475Smrg    TScreen *screen = TScreenOf(term);
851d4fba8b9Smrg    Cursor c = None;
852d522f475Smrg    Display *dpy = screen->display;
853d522f475Smrg
854d4fba8b9Smrg    TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
855d4fba8b9Smrg    if (!IsEmpty(screen->cursor_font_name)) {
856d4fba8b9Smrg	static XTermFonts myFont;
857d4fba8b9Smrg
858d4fba8b9Smrg	/* adapted from XCreateFontCursor(), which hardcodes the font name */
859d4fba8b9Smrg	TRACE(("loading cursor from alternate cursor font\n"));
8608f44fb3bSmrg	myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
8618f44fb3bSmrg	if (myFont.fs != NULL) {
862d4fba8b9Smrg	    if (!xtermMissingChar(c_index, &myFont)
863d4fba8b9Smrg		&& !xtermMissingChar(c_index + 1, &myFont)) {
864d4fba8b9Smrg#define DATA(c) { 0UL, c, c, c, 0, 0 }
865d4fba8b9Smrg		static XColor foreground = DATA(0);
866d4fba8b9Smrg		static XColor background = DATA(65535);
867d4fba8b9Smrg#undef DATA
868d4fba8b9Smrg
869d4fba8b9Smrg		/*
870d4fba8b9Smrg		 * Cursor fonts follow each shape glyph with a mask glyph; so
871d4fba8b9Smrg		 * that character position 0 contains a shape, 1 the mask for
872d4fba8b9Smrg		 * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
873d4fba8b9Smrg		 * contains defined names for each shape.
874d4fba8b9Smrg		 */
875d4fba8b9Smrg		c = XCreateGlyphCursor(dpy,
876d4fba8b9Smrg				       myFont.fs->fid,	/* source_font */
877d4fba8b9Smrg				       myFont.fs->fid,	/* mask_font */
878d4fba8b9Smrg				       c_index + 0,	/* source_char */
879d4fba8b9Smrg				       c_index + 1,	/* mask_char */
880d4fba8b9Smrg				       &foreground,
881d4fba8b9Smrg				       &background);
882d4fba8b9Smrg	    }
883d4fba8b9Smrg	    XFreeFont(dpy, myFont.fs);
884d4fba8b9Smrg	}
885d4fba8b9Smrg	if (c == None) {
886d4fba8b9Smrg	    xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
887d4fba8b9Smrg			 c_index, screen->cursor_font_name);
888d4fba8b9Smrg	}
889d4fba8b9Smrg    }
890d4fba8b9Smrg    if (c == None)
891d4fba8b9Smrg	c = XCreateFontCursor(dpy, c_index);
892d4fba8b9Smrg
893d522f475Smrg    if (c != None) {
894d522f475Smrg	recolor_cursor(screen, c, fg, bg);
895d522f475Smrg    }
896d4fba8b9Smrg    return c;
897d522f475Smrg}
898d522f475Smrg
8998f44fb3bSmrg/* adapted from <X11/cursorfont.h> */
9008f44fb3bSmrgstatic int
9018f44fb3bSmrgLookupCursorShape(const char *name)
9028f44fb3bSmrg{
9038f44fb3bSmrg#define DATA(name) { XC_##name, #name }
9048f44fb3bSmrg    static struct {
9058f44fb3bSmrg	int code;
9068f44fb3bSmrg	const char name[25];
9078f44fb3bSmrg    } table[] = {
9088f44fb3bSmrg	DATA(X_cursor),
9098f44fb3bSmrg	    DATA(arrow),
9108f44fb3bSmrg	    DATA(based_arrow_down),
9118f44fb3bSmrg	    DATA(based_arrow_up),
9128f44fb3bSmrg	    DATA(boat),
9138f44fb3bSmrg	    DATA(bogosity),
9148f44fb3bSmrg	    DATA(bottom_left_corner),
9158f44fb3bSmrg	    DATA(bottom_right_corner),
9168f44fb3bSmrg	    DATA(bottom_side),
9178f44fb3bSmrg	    DATA(bottom_tee),
9188f44fb3bSmrg	    DATA(box_spiral),
9198f44fb3bSmrg	    DATA(center_ptr),
9208f44fb3bSmrg	    DATA(circle),
9218f44fb3bSmrg	    DATA(clock),
9228f44fb3bSmrg	    DATA(coffee_mug),
9238f44fb3bSmrg	    DATA(cross),
9248f44fb3bSmrg	    DATA(cross_reverse),
9258f44fb3bSmrg	    DATA(crosshair),
9268f44fb3bSmrg	    DATA(diamond_cross),
9278f44fb3bSmrg	    DATA(dot),
9288f44fb3bSmrg	    DATA(dotbox),
9298f44fb3bSmrg	    DATA(double_arrow),
9308f44fb3bSmrg	    DATA(draft_large),
9318f44fb3bSmrg	    DATA(draft_small),
9328f44fb3bSmrg	    DATA(draped_box),
9338f44fb3bSmrg	    DATA(exchange),
9348f44fb3bSmrg	    DATA(fleur),
9358f44fb3bSmrg	    DATA(gobbler),
9368f44fb3bSmrg	    DATA(gumby),
9378f44fb3bSmrg	    DATA(hand1),
9388f44fb3bSmrg	    DATA(hand2),
9398f44fb3bSmrg	    DATA(heart),
9408f44fb3bSmrg	    DATA(icon),
9418f44fb3bSmrg	    DATA(iron_cross),
9428f44fb3bSmrg	    DATA(left_ptr),
9438f44fb3bSmrg	    DATA(left_side),
9448f44fb3bSmrg	    DATA(left_tee),
9458f44fb3bSmrg	    DATA(leftbutton),
9468f44fb3bSmrg	    DATA(ll_angle),
9478f44fb3bSmrg	    DATA(lr_angle),
9488f44fb3bSmrg	    DATA(man),
9498f44fb3bSmrg	    DATA(middlebutton),
9508f44fb3bSmrg	    DATA(mouse),
9518f44fb3bSmrg	    DATA(pencil),
9528f44fb3bSmrg	    DATA(pirate),
9538f44fb3bSmrg	    DATA(plus),
9548f44fb3bSmrg	    DATA(question_arrow),
9558f44fb3bSmrg	    DATA(right_ptr),
9568f44fb3bSmrg	    DATA(right_side),
9578f44fb3bSmrg	    DATA(right_tee),
9588f44fb3bSmrg	    DATA(rightbutton),
9598f44fb3bSmrg	    DATA(rtl_logo),
9608f44fb3bSmrg	    DATA(sailboat),
9618f44fb3bSmrg	    DATA(sb_down_arrow),
9628f44fb3bSmrg	    DATA(sb_h_double_arrow),
9638f44fb3bSmrg	    DATA(sb_left_arrow),
9648f44fb3bSmrg	    DATA(sb_right_arrow),
9658f44fb3bSmrg	    DATA(sb_up_arrow),
9668f44fb3bSmrg	    DATA(sb_v_double_arrow),
9678f44fb3bSmrg	    DATA(shuttle),
9688f44fb3bSmrg	    DATA(sizing),
9698f44fb3bSmrg	    DATA(spider),
9708f44fb3bSmrg	    DATA(spraycan),
9718f44fb3bSmrg	    DATA(star),
9728f44fb3bSmrg	    DATA(target),
9738f44fb3bSmrg	    DATA(tcross),
9748f44fb3bSmrg	    DATA(top_left_arrow),
9758f44fb3bSmrg	    DATA(top_left_corner),
9768f44fb3bSmrg	    DATA(top_right_corner),
9778f44fb3bSmrg	    DATA(top_side),
9788f44fb3bSmrg	    DATA(top_tee),
9798f44fb3bSmrg	    DATA(trek),
9808f44fb3bSmrg	    DATA(ul_angle),
9818f44fb3bSmrg	    DATA(umbrella),
9828f44fb3bSmrg	    DATA(ur_angle),
9838f44fb3bSmrg	    DATA(watch),
9848f44fb3bSmrg	    DATA(xterm),
9858f44fb3bSmrg    };
9868f44fb3bSmrg#undef DATA
9878f44fb3bSmrg    Cardinal j;
9888f44fb3bSmrg    int result = -1;
9898f44fb3bSmrg    if (!IsEmpty(name)) {
9908f44fb3bSmrg	for (j = 0; j < XtNumber(table); ++j) {
9918f44fb3bSmrg	    if (!strcmp(name, table[j].name)) {
9928f44fb3bSmrg		result = table[j].code;
9938f44fb3bSmrg		break;
9948f44fb3bSmrg	    }
9958f44fb3bSmrg	}
9968f44fb3bSmrg    }
9978f44fb3bSmrg    return result;
9988f44fb3bSmrg}
9998f44fb3bSmrg
10008f44fb3bSmrgvoid
10018f44fb3bSmrgxtermSetupPointer(XtermWidget xw, const char *theShape)
10028f44fb3bSmrg{
10038f44fb3bSmrg    TScreen *screen = TScreenOf(xw);
10048f44fb3bSmrg    unsigned shape = XC_xterm;
10058f44fb3bSmrg    int other = LookupCursorShape(theShape);
10068f44fb3bSmrg    unsigned which;
10078f44fb3bSmrg
10088f44fb3bSmrg    if (other >= 0 && other < XC_num_glyphs)
10098f44fb3bSmrg	shape = (unsigned) other;
10108f44fb3bSmrg
10118f44fb3bSmrg    TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
10128f44fb3bSmrg	   NonNull(theShape)));
10138f44fb3bSmrg
10148f44fb3bSmrg    which = (unsigned) (shape / 2);
10158f44fb3bSmrg    if (xw->work.pointer_cursors[which] == None) {
10168f44fb3bSmrg	TRACE(("creating text pointer cursor from shape %d\n", shape));
10178f44fb3bSmrg	xw->work.pointer_cursors[which] =
10188f44fb3bSmrg	    make_colored_cursor(shape,
10198f44fb3bSmrg				T_COLOR(screen, MOUSE_FG),
10208f44fb3bSmrg				T_COLOR(screen, MOUSE_BG));
10218f44fb3bSmrg    } else {
10228f44fb3bSmrg	TRACE(("updating text pointer cursor for shape %d\n", shape));
10238f44fb3bSmrg	recolor_cursor(screen,
10248f44fb3bSmrg		       screen->pointer_cursor,
10258f44fb3bSmrg		       T_COLOR(screen, MOUSE_FG),
10268f44fb3bSmrg		       T_COLOR(screen, MOUSE_BG));
10278f44fb3bSmrg    }
10288f44fb3bSmrg    if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
10298f44fb3bSmrg	screen->pointer_cursor = xw->work.pointer_cursors[which];
10308f44fb3bSmrg	TRACE(("defining text pointer cursor with shape %d\n", shape));
10318f44fb3bSmrg	XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
10328f44fb3bSmrg	if (XtIsRealized((Widget) xw)) {
10338f44fb3bSmrg	    /* briefly override pointerMode after changing the pointer */
10348f44fb3bSmrg	    if (screen->pointer_mode != pNever)
10358f44fb3bSmrg		screen->hide_pointer = True;
10368f44fb3bSmrg	    xtermShowPointer(xw, True);
10378f44fb3bSmrg	}
10388f44fb3bSmrg    }
10398f44fb3bSmrg}
10408f44fb3bSmrg
1041d522f475Smrg/* ARGSUSED */
1042d522f475Smrgvoid
1043d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
10449a64e1c5Smrg		 XEvent *event,
1045fa3f02f3Smrg		 String *params GCC_UNUSED,
1046d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
1047d522f475Smrg{
1048cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
1049cd3331d0Smrg    Input(term, &event->xkey, False);
1050d522f475Smrg}
1051d522f475Smrg
1052d522f475Smrg/* ARGSUSED */
1053d522f475Smrgvoid
1054d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
10559a64e1c5Smrg			 XEvent *event,
1056fa3f02f3Smrg			 String *params GCC_UNUSED,
1057d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
1058d522f475Smrg{
1059cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
1060cd3331d0Smrg    Input(term, &event->xkey, True);
1061d522f475Smrg}
1062d522f475Smrg
1063d522f475Smrg/* ARGSUSED */
1064d522f475Smrgvoid
1065d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
10669a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1067fa3f02f3Smrg		  String *params,
1068d522f475Smrg		  Cardinal *nparams)
1069d522f475Smrg{
1070d522f475Smrg
1071d522f475Smrg    if (*nparams != 1)
1072d522f475Smrg	return;
1073d522f475Smrg
1074d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
10750d92cbfdSchristos	const char *abcdef = "ABCDEF";
10760d92cbfdSchristos	const char *xxxxxx;
1077cd3331d0Smrg	Char c;
1078cd3331d0Smrg	UString p;
10790d92cbfdSchristos	unsigned value = 0;
10800d92cbfdSchristos
1081cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
10820d92cbfdSchristos	     '\0'; p++) {
10830d92cbfdSchristos	    value *= 16;
1084d522f475Smrg	    if (c >= '0' && c <= '9')
10850d92cbfdSchristos		value += (unsigned) (c - '0');
1086fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
10870d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
1088d522f475Smrg	    else
1089d522f475Smrg		break;
1090d522f475Smrg	}
10910d92cbfdSchristos	if (c == '\0') {
10920d92cbfdSchristos	    Char hexval[2];
10930d92cbfdSchristos	    hexval[0] = (Char) value;
10940d92cbfdSchristos	    hexval[1] = 0;
1095b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
10960d92cbfdSchristos	}
1097d522f475Smrg    } else {
1098cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
1099d522f475Smrg    }
1100d522f475Smrg}
1101d522f475Smrg
1102d522f475Smrg#if OPT_EXEC_XTERM
1103d522f475Smrg
1104d522f475Smrg#ifndef PROCFS_ROOT
1105d522f475Smrg#define PROCFS_ROOT "/proc"
1106d522f475Smrg#endif
1107d522f475Smrg
1108037a25ddSmrg/*
1109037a25ddSmrg * Determine the current working directory of the child so that we can
1110037a25ddSmrg * spawn a new terminal in the same directory.
1111037a25ddSmrg *
1112037a25ddSmrg * If we cannot get the CWD of the child, just use our own.
1113037a25ddSmrg */
1114037a25ddSmrgchar *
1115037a25ddSmrgProcGetCWD(pid_t pid)
1116037a25ddSmrg{
1117037a25ddSmrg    char *child_cwd = NULL;
1118037a25ddSmrg
1119037a25ddSmrg    if (pid) {
1120037a25ddSmrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
1121037a25ddSmrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
1122037a25ddSmrg	child_cwd = Readlink(child_cwd_link);
1123037a25ddSmrg    }
1124037a25ddSmrg    return child_cwd;
1125037a25ddSmrg}
1126037a25ddSmrg
1127d522f475Smrg/* ARGSUSED */
1128d522f475Smrgvoid
1129d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
11309a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1131fa3f02f3Smrg		    String *params,
1132d522f475Smrg		    Cardinal *nparams)
1133d522f475Smrg{
1134cd3331d0Smrg    TScreen *screen = TScreenOf(term);
1135d522f475Smrg    char *child_cwd = NULL;
1136d522f475Smrg    char *child_exe;
1137d522f475Smrg    pid_t pid;
1138d522f475Smrg
1139d522f475Smrg    /*
1140d522f475Smrg     * Try to find the actual program which is running in the child process.
1141d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
1142d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
1143d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
1144d522f475Smrg     */
1145d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
1146d522f475Smrg    if (!child_exe) {
1147cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
1148cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
1149d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
1150d522f475Smrg	} else {
11513367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
1152d522f475Smrg	}
1153d522f475Smrg	if (child_exe == 0)
1154d522f475Smrg	    return;
1155d522f475Smrg    }
1156d522f475Smrg
1157037a25ddSmrg    child_cwd = ProcGetCWD(screen->pid);
1158d522f475Smrg
1159d522f475Smrg    /* The reaper will take care of cleaning up the child */
1160d522f475Smrg    pid = fork();
1161d522f475Smrg    if (pid == -1) {
11623367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
1163d522f475Smrg    } else if (!pid) {
1164d522f475Smrg	/* We are the child */
1165d522f475Smrg	if (child_cwd) {
1166cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
1167d522f475Smrg	}
1168d522f475Smrg
1169d522f475Smrg	if (setuid(screen->uid) == -1
1170d522f475Smrg	    || setgid(screen->gid) == -1) {
11713367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
1172d522f475Smrg	} else {
11730d92cbfdSchristos	    unsigned myargc = *nparams + 1;
1174d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
1175d522f475Smrg
117694644356Smrg	    if (myargv != 0) {
117794644356Smrg		unsigned n = 0;
1178d522f475Smrg
117994644356Smrg		myargv[n++] = child_exe;
1180d522f475Smrg
118194644356Smrg		while (n < myargc) {
118294644356Smrg		    myargv[n++] = (char *) *params++;
118394644356Smrg		}
118494644356Smrg
118594644356Smrg		myargv[n] = 0;
118694644356Smrg		execv(child_exe, myargv);
118794644356Smrg	    }
1188d522f475Smrg
1189d522f475Smrg	    /* If we get here, we've failed */
11903367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
1191d522f475Smrg	}
1192d522f475Smrg	_exit(0);
1193d522f475Smrg    }
11943367019cSmrg
11953367019cSmrg    /* We are the parent; clean up */
1196d4fba8b9Smrg    free(child_cwd);
11973367019cSmrg    free(child_exe);
1198d522f475Smrg}
1199d522f475Smrg#endif /* OPT_EXEC_XTERM */
1200d522f475Smrg
1201d522f475Smrg/*
1202d522f475Smrg * Rather than sending characters to the host, put them directly into our
1203d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
1204d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
1205d522f475Smrg *
1206d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
1207d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
1208d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
1209d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
1210d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
1211d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
1212d522f475Smrg */
1213d522f475Smrg/* ARGSUSED */
1214d522f475Smrgvoid
1215d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
12169a64e1c5Smrg		XEvent *event GCC_UNUSED,
1217fa3f02f3Smrg		String *params,
1218d522f475Smrg		Cardinal *param_count)
1219d522f475Smrg{
1220d522f475Smrg    if (*param_count == 1) {
1221cd3331d0Smrg	const char *value = params[0];
1222d1603babSmrg	size_t need = strlen(value);
1223d1603babSmrg	size_t used = (size_t) (VTbuffer->next - VTbuffer->buffer);
1224d1603babSmrg	size_t have = (size_t) (VTbuffer->last - VTbuffer->buffer);
1225d522f475Smrg
1226d1603babSmrg	if ((have - used) + need < (size_t) BUF_SIZE) {
1227d522f475Smrg
1228d1603babSmrg	    fillPtyData(term, VTbuffer, value, strlen(value));
1229d522f475Smrg
1230d522f475Smrg	    TRACE(("Interpret %s\n", value));
1231d522f475Smrg	    VTbuffer->update++;
1232d522f475Smrg	}
1233d522f475Smrg    }
1234d522f475Smrg}
1235d522f475Smrg
1236d522f475Smrg/*ARGSUSED*/
1237d522f475Smrgvoid
1238d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
1239d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12409a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1241fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1242d522f475Smrg{
1243d522f475Smrg    /* NOP since we handled it above */
1244d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
1245cd3331d0Smrg    TRACE_FOCUS(w, event);
1246d522f475Smrg}
1247d522f475Smrg
1248d522f475Smrg/*ARGSUSED*/
1249d522f475Smrgvoid
1250d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
1251d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12529a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1253fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1254d522f475Smrg{
1255d522f475Smrg    /* NOP since we handled it above */
1256d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1257cd3331d0Smrg    TRACE_FOCUS(w, event);
1258d522f475Smrg}
1259d522f475Smrg
1260d522f475Smrg/*ARGSUSED*/
1261d522f475Smrgvoid
1262d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1263d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12649a64e1c5Smrg		  XEvent *ev,
1265fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1266d522f475Smrg{
1267d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1268d522f475Smrg    XtermWidget xw = term;
1269d522f475Smrg    TScreen *screen = TScreenOf(xw);
1270d522f475Smrg
12713367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1272d522f475Smrg	   visibleEventType(event->type),
12733367019cSmrg	   visibleNotifyMode(event->mode),
12743367019cSmrg	   visibleNotifyDetail(event->detail)));
1275cd3331d0Smrg    TRACE_FOCUS(xw, event);
1276d522f475Smrg
1277d522f475Smrg    if (screen->quiet_grab
1278d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1279c219fbebSmrg	/* EMPTY */ ;
1280d522f475Smrg    } else if (event->type == FocusIn) {
128194644356Smrg	if (event->detail != NotifyPointer) {
128294644356Smrg	    setXUrgency(xw, False);
128394644356Smrg	}
1284d522f475Smrg
1285d522f475Smrg	/*
1286d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1287d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1288d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1289d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1290d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1291d522f475Smrg	 * delivered to the obscured window.
1292d522f475Smrg	 */
1293d522f475Smrg	if (event->detail == NotifyNonlinear
1294d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
12953367019cSmrg	    unselectwindow(xw, INWINDOW);
1296d522f475Smrg	}
12973367019cSmrg	selectwindow(xw,
1298d522f475Smrg		     ((event->detail == NotifyPointer)
1299d522f475Smrg		      ? INWINDOW
1300d522f475Smrg		      : FOCUS));
1301d522f475Smrg	SendFocusButton(xw, event);
1302d522f475Smrg    } else {
1303d522f475Smrg#if OPT_FOCUS_EVENT
1304d522f475Smrg	if (event->type == FocusOut) {
1305d522f475Smrg	    SendFocusButton(xw, event);
1306d522f475Smrg	}
1307d522f475Smrg#endif
1308d522f475Smrg	/*
1309d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1310d522f475Smrg	 * ignore.
1311d522f475Smrg	 */
1312d522f475Smrg	if (event->mode != NotifyGrab) {
13133367019cSmrg	    unselectwindow(xw,
1314d522f475Smrg			   ((event->detail == NotifyPointer)
1315d522f475Smrg			    ? INWINDOW
1316d522f475Smrg			    : FOCUS));
1317d522f475Smrg	}
1318d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1319cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1320d522f475Smrg	    ReverseVideo(xw);
1321d522f475Smrg	    screen->grabbedKbd = False;
1322d522f475Smrg	    update_securekbd();
1323d522f475Smrg	}
1324d522f475Smrg    }
1325d522f475Smrg}
1326d522f475Smrg
1327d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1328d522f475Smrg
1329b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1330b7c89284Ssnjstatic Atom
1331b7c89284SsnjAtomBell(XtermWidget xw, int which)
1332b7c89284Ssnj{
1333b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1334b7c89284Ssnj    static struct {
1335b7c89284Ssnj	int value;
1336b7c89284Ssnj	const char *name;
1337b7c89284Ssnj    } table[] = {
1338b7c89284Ssnj	DATA(Info),
1339b7c89284Ssnj	    DATA(MarginBell),
1340b7c89284Ssnj	    DATA(MinorError),
1341b7c89284Ssnj	    DATA(TerminalBell)
1342b7c89284Ssnj    };
1343d4fba8b9Smrg#undef DATA
1344b7c89284Ssnj    Cardinal n;
1345b7c89284Ssnj    Atom result = None;
1346b7c89284Ssnj
1347b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1348b7c89284Ssnj	if (table[n].value == which) {
1349cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1350b7c89284Ssnj	    break;
1351b7c89284Ssnj	}
1352b7c89284Ssnj    }
1353b7c89284Ssnj    return result;
1354b7c89284Ssnj}
1355b7c89284Ssnj#endif
1356b7c89284Ssnj
1357d522f475Smrgvoid
1358b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1359d522f475Smrg{
1360b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1361b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1362b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1363cd3331d0Smrg#endif
1364cd3331d0Smrg
1365cd3331d0Smrg    switch (which) {
1366cd3331d0Smrg    case XkbBI_Info:
1367cd3331d0Smrg    case XkbBI_MinorError:
1368cd3331d0Smrg    case XkbBI_MajorError:
1369cd3331d0Smrg    case XkbBI_TerminalBell:
1370cd3331d0Smrg	switch (screen->warningVolume) {
1371cd3331d0Smrg	case bvOff:
1372cd3331d0Smrg	    percent = -100;
1373cd3331d0Smrg	    break;
1374cd3331d0Smrg	case bvLow:
1375cd3331d0Smrg	    break;
1376cd3331d0Smrg	case bvHigh:
1377cd3331d0Smrg	    percent = 100;
1378cd3331d0Smrg	    break;
1379cd3331d0Smrg	}
1380cd3331d0Smrg	break;
1381cd3331d0Smrg    case XkbBI_MarginBell:
1382cd3331d0Smrg	switch (screen->marginVolume) {
1383cd3331d0Smrg	case bvOff:
1384cd3331d0Smrg	    percent = -100;
1385cd3331d0Smrg	    break;
1386cd3331d0Smrg	case bvLow:
1387cd3331d0Smrg	    break;
1388cd3331d0Smrg	case bvHigh:
1389cd3331d0Smrg	    percent = 100;
1390cd3331d0Smrg	    break;
1391cd3331d0Smrg	}
1392cd3331d0Smrg	break;
1393cd3331d0Smrg    default:
1394cd3331d0Smrg	break;
1395cd3331d0Smrg    }
1396cd3331d0Smrg
1397cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1398b7c89284Ssnj    if (tony != None) {
1399c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1400b7c89284Ssnj    } else
1401b7c89284Ssnj#endif
1402b7c89284Ssnj	XBell(screen->display, percent);
1403b7c89284Ssnj}
1404b7c89284Ssnj
1405b7c89284Ssnjvoid
1406cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1407b7c89284Ssnj{
1408b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1409d522f475Smrg    struct timeval curtime;
1410d522f475Smrg
1411b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1412b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1413d522f475Smrg	return;
1414d522f475Smrg    }
1415d522f475Smrg
1416c219fbebSmrg    setXUrgency(xw, True);
1417d522f475Smrg
1418d522f475Smrg    /* has enough time gone by that we are allowed to ring
1419d522f475Smrg       the bell again? */
1420d522f475Smrg    if (screen->bellSuppressTime) {
1421037a25ddSmrg	long now_msecs;
1422037a25ddSmrg
1423d522f475Smrg	if (screen->bellInProgress) {
1424d4fba8b9Smrg	    do_xevents(xw);
1425d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1426d522f475Smrg		return;
1427d522f475Smrg	    }
1428d522f475Smrg	}
1429d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1430d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1431d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1432d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1433d522f475Smrg	    return;
1434d522f475Smrg	}
1435d522f475Smrg	lastBellTime = now_msecs;
1436d522f475Smrg    }
1437d522f475Smrg
1438d522f475Smrg    if (screen->visualbell) {
1439d522f475Smrg	VisualBell();
1440d522f475Smrg    } else {
1441b7c89284Ssnj	xtermBell(xw, which, percent);
1442d522f475Smrg    }
1443d522f475Smrg
1444d522f475Smrg    if (screen->poponbell)
1445c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1446d522f475Smrg
1447d522f475Smrg    if (screen->bellSuppressTime) {
1448d522f475Smrg	/* now we change a property and wait for the notify event to come
1449d522f475Smrg	   back.  If the server is suspending operations while the bell
1450d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1451d522f475Smrg	   know when the previous bell has finished */
1452d522f475Smrg	Widget w = CURRENT_EMU();
1453d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1454d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1455d522f475Smrg	screen->bellInProgress = True;
1456d522f475Smrg    }
1457d522f475Smrg}
1458d522f475Smrg
1459d522f475Smrgstatic void
1460fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1461d522f475Smrg{
14623367019cSmrg    int y = 0;
14633367019cSmrg    int x = 0;
14643367019cSmrg
14653367019cSmrg    if (screen->flash_line) {
14663367019cSmrg	y = CursorY(screen, screen->cur_row);
14673367019cSmrg	height = (unsigned) FontHeight(screen);
14683367019cSmrg    }
14693367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1470d522f475Smrg    XFlush(screen->display);
1471d522f475Smrg    Sleep(VB_DELAY);
14723367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1473d522f475Smrg}
1474d522f475Smrg
1475d522f475Smrgvoid
1476d522f475SmrgVisualBell(void)
1477d522f475Smrg{
1478d4fba8b9Smrg    XtermWidget xw = term;
1479d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1480d522f475Smrg
1481d522f475Smrg    if (VB_DELAY > 0) {
1482d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1483d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1484d522f475Smrg	XGCValues gcval;
1485d522f475Smrg	GC visualGC;
1486d522f475Smrg
1487d522f475Smrg	gcval.function = GXxor;
1488d522f475Smrg	gcval.foreground = xorPixel;
1489d4fba8b9Smrg	visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
1490d522f475Smrg#if OPT_TEK4014
1491d4fba8b9Smrg	if (TEK4014_ACTIVE(xw)) {
1492cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1493d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1494d522f475Smrg			TFullWidth(tekscr),
1495d522f475Smrg			TFullHeight(tekscr));
1496d522f475Smrg	} else
1497d522f475Smrg#endif
1498d522f475Smrg	{
1499d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1500d522f475Smrg			FullWidth(screen),
1501d522f475Smrg			FullHeight(screen));
1502d522f475Smrg	}
1503d4fba8b9Smrg	XtReleaseGC((Widget) xw, visualGC);
1504d522f475Smrg    }
1505d522f475Smrg}
1506d522f475Smrg
1507d522f475Smrg/* ARGSUSED */
1508d522f475Smrgvoid
1509d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1510d522f475Smrg			 XtPointer data GCC_UNUSED,
15119a64e1c5Smrg			 XEvent *ev,
1512fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1513d522f475Smrg{
1514d522f475Smrg    TScreen *screen = TScreenOf(term);
1515d522f475Smrg
1516d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1517d522f475Smrg	screen->bellInProgress = False;
1518d522f475Smrg    }
1519d522f475Smrg}
1520d522f475Smrg
15213367019cSmrgvoid
1522d4fba8b9SmrgxtermWarning(const char *fmt, ...)
15233367019cSmrg{
15243367019cSmrg    int save_err = errno;
15253367019cSmrg    va_list ap;
15263367019cSmrg
1527dfb07bc7Smrg    fflush(stdout);
1528d4fba8b9Smrg
1529d4fba8b9Smrg#if OPT_TRACE
1530d4fba8b9Smrg    va_start(ap, fmt);
1531d4fba8b9Smrg    Trace("xtermWarning: ");
1532d4fba8b9Smrg    TraceVA(fmt, ap);
1533d4fba8b9Smrg    va_end(ap);
1534d4fba8b9Smrg#endif
1535d4fba8b9Smrg
15363367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15373367019cSmrg    va_start(ap, fmt);
15383367019cSmrg    vfprintf(stderr, fmt, ap);
15393367019cSmrg    (void) fflush(stderr);
15403367019cSmrg
15413367019cSmrg    va_end(ap);
15423367019cSmrg    errno = save_err;
15433367019cSmrg}
15443367019cSmrg
15453367019cSmrgvoid
1546d4fba8b9SmrgxtermPerror(const char *fmt, ...)
15473367019cSmrg{
15483367019cSmrg    int save_err = errno;
1549d4fba8b9Smrg    const char *msg = strerror(errno);
15503367019cSmrg    va_list ap;
15513367019cSmrg
1552dfb07bc7Smrg    fflush(stdout);
1553d4fba8b9Smrg
1554d4fba8b9Smrg#if OPT_TRACE
1555d4fba8b9Smrg    va_start(ap, fmt);
1556d4fba8b9Smrg    Trace("xtermPerror: ");
1557d4fba8b9Smrg    TraceVA(fmt, ap);
1558d4fba8b9Smrg    va_end(ap);
1559d4fba8b9Smrg#endif
1560d4fba8b9Smrg
15613367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15623367019cSmrg    va_start(ap, fmt);
15633367019cSmrg    vfprintf(stderr, fmt, ap);
15643367019cSmrg    fprintf(stderr, ": %s\n", msg);
15653367019cSmrg    (void) fflush(stderr);
15663367019cSmrg
15673367019cSmrg    va_end(ap);
15683367019cSmrg    errno = save_err;
15693367019cSmrg}
15703367019cSmrg
1571d522f475SmrgWindow
1572c219fbebSmrgWMFrameWindow(XtermWidget xw)
1573d522f475Smrg{
1574d522f475Smrg    Window win_root, win_current, *children;
1575d522f475Smrg    Window win_parent = 0;
1576d522f475Smrg    unsigned int nchildren;
1577d522f475Smrg
1578c219fbebSmrg    win_current = XtWindow(xw);
1579d522f475Smrg
1580d522f475Smrg    /* find the parent which is child of root */
1581d522f475Smrg    do {
1582d522f475Smrg	if (win_parent)
1583d522f475Smrg	    win_current = win_parent;
1584c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1585d522f475Smrg		   win_current,
1586d522f475Smrg		   &win_root,
1587d522f475Smrg		   &win_parent,
1588d522f475Smrg		   &children,
1589d522f475Smrg		   &nchildren);
1590d522f475Smrg	XFree(children);
1591d522f475Smrg    } while (win_root != win_parent);
1592d522f475Smrg
1593d522f475Smrg    return win_current;
1594d522f475Smrg}
1595d522f475Smrg
1596d522f475Smrg#if OPT_DABBREV
1597d522f475Smrg/*
1598d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1599d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1600d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1601d522f475Smrg * expansions and ignores one of them if they are identical.
1602d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1603d522f475Smrg */
1604d522f475Smrg
1605d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1606d522f475Smrg
1607d522f475Smrgstatic int
1608fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1609d522f475Smrg{
1610b7c89284Ssnj    int result = -1;
1611b7c89284Ssnj    int firstLine = -(screen->savedlines);
1612d522f475Smrg
1613b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1614b7c89284Ssnj    while (cell->row >= firstLine) {
1615b7c89284Ssnj	if (--(cell->col) >= 0) {
1616b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1617b7c89284Ssnj	    break;
1618b7c89284Ssnj	}
1619b7c89284Ssnj	if (--(cell->row) < firstLine)
1620b7c89284Ssnj	    break;		/* ...there is no previous line */
1621b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1622b7c89284Ssnj	cell->col = MaxCols(screen);
1623b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1624b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1625d522f475Smrg	    break;
1626b7c89284Ssnj	}
1627d522f475Smrg    }
1628b7c89284Ssnj    return result;
1629d522f475Smrg}
1630d522f475Smrg
1631d522f475Smrgstatic char *
16329a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1633d522f475Smrg{
16349a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1635d522f475Smrg    char *abword;
1636d522f475Smrg    int c;
16379a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1638b7c89284Ssnj    char *result = 0;
1639d522f475Smrg
1640b7c89284Ssnj    abword = ab_end;
1641d522f475Smrg    *abword = '\0';		/* end of string marker */
1642d522f475Smrg
1643b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1644b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
16459a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1646b7c89284Ssnj	    *(--abword) = (char) c;
1647d522f475Smrg    }
1648d522f475Smrg
1649b7c89284Ssnj    if (c >= 0) {
1650b7c89284Ssnj	result = abword;
1651b7c89284Ssnj    } else if (abword != ab_end) {
1652b7c89284Ssnj	result = abword;
1653b7c89284Ssnj    }
1654b7c89284Ssnj
1655b7c89284Ssnj    if (result != 0) {
1656b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1657b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1658b7c89284Ssnj	    ;			/* skip preceding spaces */
1659b7c89284Ssnj	}
1660b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1661b7c89284Ssnj    }
1662b7c89284Ssnj    return result;
1663d522f475Smrg}
1664d522f475Smrg
1665d522f475Smrgstatic int
16669a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1667d522f475Smrg{
16689a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1669d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1670d522f475Smrg
1671b7c89284Ssnj    static CELL cell;
1672d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1673d522f475Smrg    static unsigned int expansions;
1674d522f475Smrg
1675d522f475Smrg    char *expansion;
1676d522f475Smrg    size_t hint_len;
1677b7c89284Ssnj    int result = 0;
1678b7c89284Ssnj    LineData *ld;
1679d522f475Smrg
1680d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1681d522f475Smrg	expansions = 0;
1682b7c89284Ssnj	cell.col = screen->cur_col;
1683b7c89284Ssnj	cell.row = screen->cur_row;
1684b7c89284Ssnj
1685d4fba8b9Smrg	free(dabbrev_hint);
1686b7c89284Ssnj
16879a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1688b7c89284Ssnj
1689d4fba8b9Smrg	    free(lastexpansion);
1690b7c89284Ssnj
1691b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1692b7c89284Ssnj
1693b7c89284Ssnj		/* make own copy */
1694b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1695b7c89284Ssnj		    screen->dabbrev_working = True;
1696b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1697b7c89284Ssnj		}
1698cd3331d0Smrg	    } else {
1699cd3331d0Smrg		return result;
1700b7c89284Ssnj	    }
1701cd3331d0Smrg	} else {
1702cd3331d0Smrg	    return result;
1703d522f475Smrg	}
1704b7c89284Ssnj	if (!screen->dabbrev_working) {
1705d4fba8b9Smrg	    free(lastexpansion);
1706d4fba8b9Smrg	    lastexpansion = 0;
1707b7c89284Ssnj	    return result;
1708b7c89284Ssnj	}
1709d522f475Smrg    }
1710d522f475Smrg
1711cd3331d0Smrg    if (dabbrev_hint == 0)
1712cd3331d0Smrg	return result;
1713cd3331d0Smrg
1714d522f475Smrg    hint_len = strlen(dabbrev_hint);
1715d522f475Smrg    for (;;) {
17169a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1717d522f475Smrg	    if (expansions >= 2) {
1718d522f475Smrg		expansions = 0;
1719b7c89284Ssnj		cell.col = screen->cur_col;
1720b7c89284Ssnj		cell.row = screen->cur_row;
1721d522f475Smrg		continue;
1722d522f475Smrg	    }
1723d522f475Smrg	    break;
1724d522f475Smrg	}
1725d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1726d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1727d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1728d522f475Smrg	    break;
1729d522f475Smrg    }
1730d522f475Smrg
1731b7c89284Ssnj    if (expansion != 0) {
1732037a25ddSmrg	Char *copybuffer;
1733037a25ddSmrg	size_t del_cnt = strlen(lastexpansion) - hint_len;
1734037a25ddSmrg	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1735b7c89284Ssnj
1736b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1737b7c89284Ssnj	    /* delete previous expansion */
1738b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1739b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1740b7c89284Ssnj		    expansion + hint_len,
1741b7c89284Ssnj		    strlen(expansion) - hint_len);
1742d1603babSmrg	    v_write(pty, copybuffer, buf_cnt);
1743b7c89284Ssnj	    /* v_write() just reset our flag */
1744b7c89284Ssnj	    screen->dabbrev_working = True;
1745b7c89284Ssnj	    free(copybuffer);
1746b7c89284Ssnj
1747b7c89284Ssnj	    free(lastexpansion);
1748b7c89284Ssnj
1749b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1750b7c89284Ssnj		result = 1;
1751b7c89284Ssnj		expansions++;
1752b7c89284Ssnj	    }
1753b7c89284Ssnj	}
1754b7c89284Ssnj    }
1755b7c89284Ssnj
1756b7c89284Ssnj    return result;
1757d522f475Smrg}
1758d522f475Smrg
1759d522f475Smrg/*ARGSUSED*/
1760d522f475Smrgvoid
1761b7c89284SsnjHandleDabbrevExpand(Widget w,
17629a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1763fa3f02f3Smrg		    String *params GCC_UNUSED,
1764d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1765d522f475Smrg{
1766b7c89284Ssnj    XtermWidget xw;
1767b7c89284Ssnj
1768cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1769b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
17709a64e1c5Smrg	if (!dabbrev_expand(xw))
1771cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1772d522f475Smrg    }
1773d522f475Smrg}
1774d522f475Smrg#endif /* OPT_DABBREV */
1775d522f475Smrg
1776d4fba8b9Smrgvoid
1777d4fba8b9SmrgxtermDeiconify(XtermWidget xw)
1778d4fba8b9Smrg{
1779d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1780d4fba8b9Smrg    Display *dpy = screen->display;
1781d4fba8b9Smrg    Window target = VShellWindow(xw);
1782d4fba8b9Smrg    XEvent e;
1783d4fba8b9Smrg    Atom atom_state = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1784d4fba8b9Smrg
1785d4fba8b9Smrg    if (xtermIsIconified(xw)) {
1786d4fba8b9Smrg	TRACE(("...de-iconify window %#lx\n", target));
1787d4fba8b9Smrg	XMapWindow(dpy, target);
1788d4fba8b9Smrg
1789d4fba8b9Smrg	memset(&e, 0, sizeof(e));
1790d4fba8b9Smrg	e.xclient.type = ClientMessage;
1791d4fba8b9Smrg	e.xclient.message_type = atom_state;
1792d4fba8b9Smrg	e.xclient.display = dpy;
1793d4fba8b9Smrg	e.xclient.window = target;
1794d4fba8b9Smrg	e.xclient.format = 32;
1795d4fba8b9Smrg	e.xclient.data.l[0] = 1;
1796d4fba8b9Smrg	e.xclient.data.l[1] = CurrentTime;
1797d4fba8b9Smrg
1798d4fba8b9Smrg	XSendEvent(dpy, DefaultRootWindow(dpy), False,
1799d4fba8b9Smrg		   SubstructureRedirectMask | SubstructureNotifyMask, &e);
1800d4fba8b9Smrg	xevents(xw);
1801d4fba8b9Smrg    }
1802d4fba8b9Smrg}
1803d4fba8b9Smrg
1804d4fba8b9Smrgvoid
1805d4fba8b9SmrgxtermIconify(XtermWidget xw)
1806d4fba8b9Smrg{
1807d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1808d4fba8b9Smrg    Window target = VShellWindow(xw);
1809d4fba8b9Smrg
1810d4fba8b9Smrg    if (!xtermIsIconified(xw)) {
1811d4fba8b9Smrg	TRACE(("...iconify window %#lx\n", target));
1812d4fba8b9Smrg	XIconifyWindow(screen->display,
1813d4fba8b9Smrg		       target,
1814d4fba8b9Smrg		       DefaultScreen(screen->display));
1815d4fba8b9Smrg	xevents(xw);
1816d4fba8b9Smrg    }
1817d4fba8b9Smrg}
1818d4fba8b9Smrg
1819d4fba8b9SmrgBoolean
1820d4fba8b9SmrgxtermIsIconified(XtermWidget xw)
1821d4fba8b9Smrg{
1822d4fba8b9Smrg    XWindowAttributes win_attrs;
1823d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1824d4fba8b9Smrg    Window target = VShellWindow(xw);
1825d4fba8b9Smrg    Display *dpy = screen->display;
1826d4fba8b9Smrg    Boolean result = False;
1827d4fba8b9Smrg
1828d4fba8b9Smrg    if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
1829d4fba8b9Smrg	Atom actual_return_type;
1830d4fba8b9Smrg	int actual_format_return = 0;
1831d4fba8b9Smrg	unsigned long nitems_return = 0;
1832d4fba8b9Smrg	unsigned long bytes_after_return = 0;
1833d4fba8b9Smrg	unsigned char *prop_return = 0;
1834d4fba8b9Smrg	long long_length = 1024;
1835d4fba8b9Smrg	Atom requested_type = XA_ATOM;
1836d4fba8b9Smrg	Atom is_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
1837d4fba8b9Smrg	Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
1838d4fba8b9Smrg
1839d4fba8b9Smrg	/* this works with non-EWMH */
1840d4fba8b9Smrg	result = (win_attrs.map_state != IsViewable) ? True : False;
1841d4fba8b9Smrg
1842d4fba8b9Smrg	/* this is a convention used by some EWMH applications */
1843d4fba8b9Smrg	if (xtermGetWinProp(dpy,
1844d4fba8b9Smrg			    target,
1845d4fba8b9Smrg			    wm_state,
1846d4fba8b9Smrg			    0L,
1847d4fba8b9Smrg			    long_length,
1848d4fba8b9Smrg			    requested_type,
1849d4fba8b9Smrg			    &actual_return_type,
1850d4fba8b9Smrg			    &actual_format_return,
1851d4fba8b9Smrg			    &nitems_return,
1852d4fba8b9Smrg			    &bytes_after_return,
185350027b5bSmrg			    &prop_return)) {
185450027b5bSmrg	    if (prop_return != 0
185550027b5bSmrg		&& actual_return_type == requested_type
185650027b5bSmrg		&& actual_format_return == 32) {
185750027b5bSmrg		unsigned long n;
185850027b5bSmrg		for (n = 0; n < nitems_return; ++n) {
185950027b5bSmrg		    unsigned long check = (((unsigned long *)
186050027b5bSmrg					    (void *) prop_return)[n]);
186150027b5bSmrg		    if (check == is_hidden) {
186250027b5bSmrg			result = True;
186350027b5bSmrg			break;
186450027b5bSmrg		    }
1865d4fba8b9Smrg		}
186650027b5bSmrg		XFree(prop_return);
1867d4fba8b9Smrg	    }
1868d4fba8b9Smrg	}
1869d4fba8b9Smrg    }
1870d4fba8b9Smrg    TRACE(("...window %#lx is%s iconified\n",
1871d4fba8b9Smrg	   target,
1872d4fba8b9Smrg	   result ? "" : " not"));
1873d4fba8b9Smrg    return result;
1874d4fba8b9Smrg}
1875d4fba8b9Smrg
1876d522f475Smrg#if OPT_MAXIMIZE
1877d522f475Smrg/*ARGSUSED*/
1878d522f475Smrgvoid
1879b7c89284SsnjHandleDeIconify(Widget w,
18809a64e1c5Smrg		XEvent *event GCC_UNUSED,
1881fa3f02f3Smrg		String *params GCC_UNUSED,
1882d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1883d522f475Smrg{
1884b7c89284Ssnj    XtermWidget xw;
1885b7c89284Ssnj
1886b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1887d4fba8b9Smrg	xtermDeiconify(xw);
1888d522f475Smrg    }
1889d522f475Smrg}
1890d522f475Smrg
1891d522f475Smrg/*ARGSUSED*/
1892d522f475Smrgvoid
1893b7c89284SsnjHandleIconify(Widget w,
18949a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1895fa3f02f3Smrg	      String *params GCC_UNUSED,
1896d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1897d522f475Smrg{
1898b7c89284Ssnj    XtermWidget xw;
1899b7c89284Ssnj
1900b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1901d4fba8b9Smrg	xtermIconify(xw);
1902d522f475Smrg    }
1903d522f475Smrg}
1904d522f475Smrg
1905d522f475Smrgint
1906c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1907d522f475Smrg{
1908c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1909d522f475Smrg    XSizeHints hints;
1910d522f475Smrg    long supp = 0;
1911d522f475Smrg    Window root_win;
1912d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1913d522f475Smrg    int root_y = -1;
1914d522f475Smrg    unsigned root_border;
1915d522f475Smrg    unsigned root_depth;
19163367019cSmrg    int code;
1917d522f475Smrg
1918d522f475Smrg    if (XGetGeometry(screen->display,
1919c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1920d522f475Smrg		     &root_win,
1921d522f475Smrg		     &root_x,
1922d522f475Smrg		     &root_y,
1923d522f475Smrg		     width,
1924d522f475Smrg		     height,
1925d522f475Smrg		     &root_border,
1926d522f475Smrg		     &root_depth)) {
1927d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1928d522f475Smrg	       root_x,
1929d522f475Smrg	       root_y,
1930d522f475Smrg	       *width,
1931d522f475Smrg	       *height,
1932d522f475Smrg	       root_border));
1933d522f475Smrg
1934d522f475Smrg	*width -= (root_border * 2);
1935d522f475Smrg	*height -= (root_border * 2);
1936d522f475Smrg
1937d522f475Smrg	hints.flags = PMaxSize;
1938d522f475Smrg	if (XGetWMNormalHints(screen->display,
1939c219fbebSmrg			      VShellWindow(xw),
1940d522f475Smrg			      &hints,
1941d522f475Smrg			      &supp)
1942d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1943d522f475Smrg
1944d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1945d522f475Smrg		   hints.max_width,
1946d522f475Smrg		   hints.max_height));
1947d522f475Smrg
1948d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1949b7c89284Ssnj		*width = (unsigned) hints.max_width;
1950d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1951b7c89284Ssnj		*height = (unsigned) hints.max_height;
1952d522f475Smrg	}
19533367019cSmrg	code = 1;
19543367019cSmrg    } else {
19553367019cSmrg	*width = 0;
19563367019cSmrg	*height = 0;
19573367019cSmrg	code = 0;
1958d522f475Smrg    }
19593367019cSmrg    return code;
1960d522f475Smrg}
1961d522f475Smrg
1962d522f475Smrgvoid
1963c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1964d522f475Smrg{
1965c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1966d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1967d4fba8b9Smrg    unsigned root_width = 0, root_height = 0;
19683367019cSmrg    Boolean success = False;
1969d522f475Smrg
19703367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
19713367019cSmrg	   maximize,
19723367019cSmrg	   (maximize
19733367019cSmrg	    ? "maximize"
19743367019cSmrg	    : "restore")));
1975d522f475Smrg
19763367019cSmrg    /*
19773367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
19783367019cSmrg     * as well as the estimated root-window size.
19793367019cSmrg     */
19803367019cSmrg    if (maximize
19813367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
19823367019cSmrg	&& xtermGetWinAttrs(screen->display,
19833367019cSmrg			    WMFrameWindow(xw),
19843367019cSmrg			    &wm_attrs)
19853367019cSmrg	&& xtermGetWinAttrs(screen->display,
19863367019cSmrg			    VShellWindow(xw),
19873367019cSmrg			    &vshell_attrs)) {
19883367019cSmrg
19893367019cSmrg	if (screen->restore_data != True
19903367019cSmrg	    || screen->restore_width != root_width
19913367019cSmrg	    || screen->restore_height != root_height) {
19923367019cSmrg	    screen->restore_data = True;
1993d4fba8b9Smrg	    screen->restore_x = wm_attrs.x;
1994d4fba8b9Smrg	    screen->restore_y = wm_attrs.y;
19953367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
19963367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
19973367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1998d522f475Smrg		   screen->restore_x,
1999d522f475Smrg		   screen->restore_y,
2000d522f475Smrg		   screen->restore_width,
2001d522f475Smrg		   screen->restore_height));
20023367019cSmrg	}
2003d522f475Smrg
20043367019cSmrg	/* subtract wm decoration dimensions */
2005d4fba8b9Smrg	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
2006d4fba8b9Smrg	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
20073367019cSmrg	success = True;
20083367019cSmrg    } else if (screen->restore_data) {
20093367019cSmrg	success = True;
20103367019cSmrg	maximize = 0;
20113367019cSmrg    }
20123367019cSmrg
20133367019cSmrg    if (success) {
20143367019cSmrg	switch (maximize) {
20153367019cSmrg	case 3:
20163367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
20173367019cSmrg	    break;
20183367019cSmrg	case 2:
20193367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
20203367019cSmrg	    break;
20213367019cSmrg	case 1:
20223367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
2023d4fba8b9Smrg	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
2024d4fba8b9Smrg		   0,
2025d4fba8b9Smrg		   0,
2026d4fba8b9Smrg		   root_width,
2027d4fba8b9Smrg		   root_height));
20283367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
2029d4fba8b9Smrg			      0,	/* x */
2030d4fba8b9Smrg			      0,	/* y */
20313367019cSmrg			      root_width,
20323367019cSmrg			      root_height);
20333367019cSmrg	    break;
20343367019cSmrg
20353367019cSmrg	default:
20363367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
20373367019cSmrg	    if (screen->restore_data) {
20383367019cSmrg		screen->restore_data = False;
20393367019cSmrg
2040d4fba8b9Smrg		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
20413367019cSmrg		       screen->restore_x,
20423367019cSmrg		       screen->restore_y,
20433367019cSmrg		       screen->restore_width,
20443367019cSmrg		       screen->restore_height));
20453367019cSmrg
20463367019cSmrg		XMoveResizeWindow(screen->display,
20473367019cSmrg				  VShellWindow(xw),
20483367019cSmrg				  screen->restore_x,
20493367019cSmrg				  screen->restore_y,
20503367019cSmrg				  screen->restore_width,
20513367019cSmrg				  screen->restore_height);
20523367019cSmrg	    }
20533367019cSmrg	    break;
2054d522f475Smrg	}
2055d522f475Smrg    }
2056d522f475Smrg}
2057d522f475Smrg
2058d522f475Smrg/*ARGSUSED*/
2059d522f475Smrgvoid
2060b7c89284SsnjHandleMaximize(Widget w,
20619a64e1c5Smrg	       XEvent *event GCC_UNUSED,
2062fa3f02f3Smrg	       String *params GCC_UNUSED,
2063d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
2064d522f475Smrg{
2065b7c89284Ssnj    XtermWidget xw;
2066b7c89284Ssnj
2067b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2068b7c89284Ssnj	RequestMaximize(xw, 1);
2069d522f475Smrg    }
2070d522f475Smrg}
2071d522f475Smrg
2072d522f475Smrg/*ARGSUSED*/
2073d522f475Smrgvoid
2074b7c89284SsnjHandleRestoreSize(Widget w,
20759a64e1c5Smrg		  XEvent *event GCC_UNUSED,
2076fa3f02f3Smrg		  String *params GCC_UNUSED,
2077d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
2078d522f475Smrg{
2079b7c89284Ssnj    XtermWidget xw;
2080b7c89284Ssnj
2081b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2082b7c89284Ssnj	RequestMaximize(xw, 0);
2083d522f475Smrg    }
2084d522f475Smrg}
2085d522f475Smrg#endif /* OPT_MAXIMIZE */
2086d522f475Smrg
2087d522f475Smrgvoid
2088d522f475SmrgRedraw(void)
2089d522f475Smrg{
2090d4fba8b9Smrg    XtermWidget xw = term;
2091d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2092d522f475Smrg    XExposeEvent event;
2093d522f475Smrg
2094d522f475Smrg    TRACE(("Redraw\n"));
2095d522f475Smrg
2096d522f475Smrg    event.type = Expose;
2097d522f475Smrg    event.display = screen->display;
2098d522f475Smrg    event.x = 0;
2099d522f475Smrg    event.y = 0;
2100d522f475Smrg    event.count = 0;
2101d522f475Smrg
2102d522f475Smrg    if (VWindow(screen)) {
2103d522f475Smrg	event.window = VWindow(screen);
2104d4fba8b9Smrg	event.width = xw->core.width;
2105d4fba8b9Smrg	event.height = xw->core.height;
2106d4fba8b9Smrg	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
2107d4fba8b9Smrg						     (XEvent *) &event,
2108d4fba8b9Smrg						     NULL);
2109d522f475Smrg	if (ScrollbarWidth(screen)) {
2110d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
21119a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
2112d522f475Smrg	}
2113d522f475Smrg    }
2114d522f475Smrg#if OPT_TEK4014
2115d4fba8b9Smrg    if (TEK4014_SHOWN(xw)) {
2116cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
2117d522f475Smrg	event.window = TWindow(tekscr);
2118d522f475Smrg	event.width = tekWidget->core.width;
2119d522f475Smrg	event.height = tekWidget->core.height;
21209a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
2121d522f475Smrg    }
2122d522f475Smrg#endif
2123d522f475Smrg}
2124d522f475Smrg
2125d522f475Smrg#ifdef VMS
2126d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
2127d522f475Smrg#else
2128d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
2129d522f475Smrg#endif
2130d522f475Smrg
2131d522f475Smrgvoid
2132d522f475Smrgtimestamp_filename(char *dst, const char *src)
2133d522f475Smrg{
2134d522f475Smrg    time_t tstamp;
2135d522f475Smrg    struct tm *tstruct;
2136d522f475Smrg
2137d522f475Smrg    tstamp = time((time_t *) 0);
2138d522f475Smrg    tstruct = localtime(&tstamp);
2139d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
2140d522f475Smrg	    src,
21413367019cSmrg	    (int) tstruct->tm_year + 1900,
2142d522f475Smrg	    tstruct->tm_mon + 1,
2143d522f475Smrg	    tstruct->tm_mday,
2144d522f475Smrg	    tstruct->tm_hour,
2145d522f475Smrg	    tstruct->tm_min,
2146d522f475Smrg	    tstruct->tm_sec);
2147d522f475Smrg}
2148d522f475Smrg
2149d1603babSmrg#if OPT_SCREEN_DUMPS
2150d4fba8b9SmrgFILE *
2151d4fba8b9Smrgcreate_printfile(XtermWidget xw, const char *suffix)
2152d4fba8b9Smrg{
2153d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2154d4fba8b9Smrg    char fname[1024];
2155d4fba8b9Smrg    int fd;
2156d4fba8b9Smrg    FILE *fp;
2157d4fba8b9Smrg
2158d4fba8b9Smrg#ifdef VMS
2159d4fba8b9Smrg    sprintf(fname, "sys$scratch:xterm%s", suffix);
2160d4fba8b9Smrg#elif defined(HAVE_STRFTIME)
2161d4fba8b9Smrg    {
2162d4fba8b9Smrg	char format[1024];
2163d4fba8b9Smrg	time_t now;
2164d4fba8b9Smrg	struct tm *ltm;
2165d4fba8b9Smrg
2166d4fba8b9Smrg	now = time((time_t *) 0);
2167d4fba8b9Smrg	ltm = localtime(&now);
2168d4fba8b9Smrg
2169d4fba8b9Smrg	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2170d4fba8b9Smrg	if (strftime(fname, sizeof fname, format, ltm) == 0) {
2171d4fba8b9Smrg	    sprintf(fname, "xterm%s", suffix);
2172d4fba8b9Smrg	}
2173d4fba8b9Smrg    }
2174d4fba8b9Smrg#else
2175d4fba8b9Smrg    sprintf(fname, "xterm%s", suffix);
2176d4fba8b9Smrg#endif
2177d4fba8b9Smrg    fd = open_userfile(screen->uid, screen->gid, fname, False);
2178d4fba8b9Smrg    fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2179d4fba8b9Smrg    return fp;
2180d4fba8b9Smrg}
2181d1603babSmrg#endif /* OPT_SCREEN_DUMPS */
2182d4fba8b9Smrg
2183d1603babSmrg#if OPT_SCREEN_DUMPS || defined(ALLOWLOGGING)
2184d522f475Smrgint
2185d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2186d522f475Smrg{
2187d522f475Smrg    int fd;
2188d522f475Smrg    struct stat sb;
2189d522f475Smrg
2190d522f475Smrg#ifdef VMS
2191d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
2192d522f475Smrg	int the_error = errno;
21933367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
21943367019cSmrg		     path,
21953367019cSmrg		     the_error,
21963367019cSmrg		     SysErrorMsg(the_error));
2197d522f475Smrg	return -1;
2198d522f475Smrg    }
2199d522f475Smrg    chown(path, uid, gid);
2200d522f475Smrg#else
2201d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
2202d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
2203d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2204d522f475Smrg	int the_error = errno;
22053367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
22063367019cSmrg		     path,
22073367019cSmrg		     the_error,
22083367019cSmrg		     SysErrorMsg(the_error));
2209d522f475Smrg	return -1;
2210d522f475Smrg    }
2211d522f475Smrg#endif
2212d522f475Smrg
2213d522f475Smrg    /*
2214d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
2215d522f475Smrg     * we do any damage, and that it is not world-writable.
2216d522f475Smrg     */
2217d522f475Smrg    if (fstat(fd, &sb) < 0
2218d522f475Smrg	|| sb.st_uid != uid
2219d522f475Smrg	|| (sb.st_mode & 022) != 0) {
22203367019cSmrg	xtermWarning("you do not own %s\n", path);
2221d522f475Smrg	close(fd);
2222d522f475Smrg	return -1;
2223d522f475Smrg    }
2224d522f475Smrg    return fd;
2225d522f475Smrg}
2226d522f475Smrg
2227d522f475Smrg#ifndef VMS
2228d522f475Smrg/*
2229d522f475Smrg * Create a file only if we could with the permissions of the real user id.
2230d522f475Smrg * We could emulate this with careful use of access() and following
2231d522f475Smrg * symbolic links, but that is messy and has race conditions.
2232d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2233d522f475Smrg * being available.
2234d522f475Smrg *
2235d522f475Smrg * Note: When called for user logging, we have ensured that the real and
2236d522f475Smrg * effective user ids are the same, so this remains as a convenience function
2237d522f475Smrg * for the debug logs.
2238d522f475Smrg *
2239d522f475Smrg * Returns
2240d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
2241d522f475Smrg *	-1 on error, e.g., cannot fork
2242d522f475Smrg *	 0 otherwise.
2243d522f475Smrg */
2244d522f475Smrgint
2245712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2246d522f475Smrg{
2247d522f475Smrg    int fd;
2248d522f475Smrg    pid_t pid;
2249d522f475Smrg    int retval = 0;
2250d522f475Smrg    int childstat = 0;
2251d522f475Smrg#ifndef HAVE_WAITPID
2252d522f475Smrg    int waited;
22533367019cSmrg    void (*chldfunc) (int);
2254d522f475Smrg
2255d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
2256d522f475Smrg#endif /* HAVE_WAITPID */
2257d522f475Smrg
2258d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2259d522f475Smrg	   (int) uid, (int) geteuid(),
2260d522f475Smrg	   (int) gid, (int) getegid(),
2261d522f475Smrg	   append,
2262d522f475Smrg	   pathname,
2263d522f475Smrg	   mode));
2264d522f475Smrg
2265d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
2266d522f475Smrg	fd = open(pathname,
2267d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2268d522f475Smrg		  mode);
2269d522f475Smrg	if (fd >= 0)
2270d522f475Smrg	    close(fd);
2271d522f475Smrg	return (fd >= 0);
2272d522f475Smrg    }
2273d522f475Smrg
2274d522f475Smrg    pid = fork();
2275d522f475Smrg    switch (pid) {
2276d522f475Smrg    case 0:			/* child */
2277d522f475Smrg	if (setgid(gid) == -1
2278d522f475Smrg	    || setuid(uid) == -1) {
2279d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
2280d522f475Smrg	    retval = 1;
2281d522f475Smrg	} else {
2282d522f475Smrg	    fd = open(pathname,
2283d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2284d522f475Smrg		      mode);
2285d522f475Smrg	    if (fd >= 0) {
2286d522f475Smrg		close(fd);
2287d522f475Smrg		retval = 0;
2288d522f475Smrg	    } else {
2289d522f475Smrg		retval = 1;
2290d522f475Smrg	    }
2291d522f475Smrg	}
2292d522f475Smrg	_exit(retval);
2293d522f475Smrg	/* NOTREACHED */
2294d522f475Smrg    case -1:			/* error */
2295d522f475Smrg	return retval;
2296d522f475Smrg    default:			/* parent */
2297d522f475Smrg#ifdef HAVE_WAITPID
2298d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
2299d522f475Smrg#ifdef EINTR
2300d522f475Smrg	    if (errno == EINTR)
2301d522f475Smrg		continue;
2302d522f475Smrg#endif /* EINTR */
2303d522f475Smrg#ifdef ERESTARTSYS
2304d522f475Smrg	    if (errno == ERESTARTSYS)
2305d522f475Smrg		continue;
2306d522f475Smrg#endif /* ERESTARTSYS */
2307d522f475Smrg	    break;
2308d522f475Smrg	}
2309d522f475Smrg#else /* HAVE_WAITPID */
2310d522f475Smrg	waited = wait(&childstat);
2311d522f475Smrg	signal(SIGCHLD, chldfunc);
2312d522f475Smrg	/*
2313d522f475Smrg	   Since we had the signal handler uninstalled for a while,
2314d522f475Smrg	   we might have missed the termination of our screen child.
2315d522f475Smrg	   If we can check for this possibility without hanging, do so.
2316d522f475Smrg	 */
2317d522f475Smrg	do
2318cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
23193367019cSmrg		NormalExit();
2320d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
2321d522f475Smrg#endif /* HAVE_WAITPID */
2322d522f475Smrg#ifndef WIFEXITED
2323d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
2324d522f475Smrg#endif
2325d522f475Smrg	if (WIFEXITED(childstat))
2326d522f475Smrg	    retval = 1;
2327d522f475Smrg	return retval;
2328d522f475Smrg    }
2329d522f475Smrg}
2330d522f475Smrg#endif /* !VMS */
2331d1603babSmrg#endif /* OPT_SCREEN_DUMPS || defined(ALLOWLOGGING) */
2332d522f475Smrg
2333d522f475Smrgint
2334fa3f02f3SmrgxtermResetIds(TScreen *screen)
2335d522f475Smrg{
2336d522f475Smrg    int result = 0;
2337d522f475Smrg    if (setgid(screen->gid) == -1) {
23383367019cSmrg	xtermWarning("unable to reset group-id\n");
2339d522f475Smrg	result = -1;
2340d522f475Smrg    }
2341d522f475Smrg    if (setuid(screen->uid) == -1) {
23423367019cSmrg	xtermWarning("unable to reset user-id\n");
2343d522f475Smrg	result = -1;
2344d522f475Smrg    }
2345d522f475Smrg    return result;
2346d522f475Smrg}
2347d522f475Smrg
2348d522f475Smrg#ifdef ALLOWLOGGING
2349d522f475Smrg
2350d522f475Smrg/*
2351d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
2352d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
2353d522f475Smrg */
2354d522f475Smrg
2355d522f475Smrg#ifdef ALLOWLOGFILEEXEC
23563367019cSmrgstatic void
2357d4fba8b9Smrghandle_SIGPIPE(int sig GCC_UNUSED)
2358d522f475Smrg{
2359cd3331d0Smrg    XtermWidget xw = term;
2360cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2361d522f475Smrg
23623367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2363d522f475Smrg#ifdef SYSV
2364d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2365d522f475Smrg#endif /* SYSV */
2366d522f475Smrg    if (screen->logging)
2367cd3331d0Smrg	CloseLog(xw);
2368d522f475Smrg}
2369d4fba8b9Smrg
2370d4fba8b9Smrg/*
2371d4fba8b9Smrg * Open a command to pipe log data to it.
2372d4fba8b9Smrg * Warning, enabling this "feature" allows arbitrary programs
2373d4fba8b9Smrg * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2374d4fba8b9Smrg * done through escape sequences....  You have been warned.
2375d4fba8b9Smrg */
2376d4fba8b9Smrgstatic void
2377d4fba8b9SmrgStartLogExec(TScreen *screen)
2378d4fba8b9Smrg{
2379d4fba8b9Smrg    int pid;
2380d4fba8b9Smrg    int p[2];
2381d4fba8b9Smrg    static char *shell;
2382d4fba8b9Smrg    struct passwd pw;
2383d4fba8b9Smrg
2384d4fba8b9Smrg    if ((shell = x_getenv("SHELL")) == NULL) {
2385d4fba8b9Smrg
2386d4fba8b9Smrg	if (x_getpwuid(screen->uid, &pw)) {
2387d4fba8b9Smrg	    char *name = x_getlogin(screen->uid, &pw);
2388d4fba8b9Smrg	    if (*(pw.pw_shell)) {
2389d4fba8b9Smrg		shell = pw.pw_shell;
2390d4fba8b9Smrg	    }
2391d4fba8b9Smrg	    free(name);
2392d4fba8b9Smrg	}
2393d4fba8b9Smrg    }
2394d4fba8b9Smrg
2395d4fba8b9Smrg    if (shell == 0) {
2396d4fba8b9Smrg	static char dummy[] = "/bin/sh";
2397d4fba8b9Smrg	shell = dummy;
2398d4fba8b9Smrg    }
2399d4fba8b9Smrg
2400d4fba8b9Smrg    if (access(shell, X_OK) != 0) {
2401d4fba8b9Smrg	xtermPerror("Can't execute `%s'\n", shell);
2402d4fba8b9Smrg	return;
2403d4fba8b9Smrg    }
2404d4fba8b9Smrg
2405d4fba8b9Smrg    if (pipe(p) < 0) {
2406d4fba8b9Smrg	xtermPerror("Can't make a pipe connection\n");
2407d4fba8b9Smrg	return;
2408d4fba8b9Smrg    } else if ((pid = fork()) < 0) {
2409d4fba8b9Smrg	xtermPerror("Can't fork...\n");
2410d4fba8b9Smrg	return;
2411d4fba8b9Smrg    }
2412d4fba8b9Smrg    if (pid == 0) {		/* child */
2413d4fba8b9Smrg	/*
2414d4fba8b9Smrg	 * Close our output (we won't be talking back to the
2415d4fba8b9Smrg	 * parent), and redirect our child's output to the
2416d4fba8b9Smrg	 * original stderr.
2417d4fba8b9Smrg	 */
2418d4fba8b9Smrg	close(p[1]);
2419d4fba8b9Smrg	dup2(p[0], 0);
2420d4fba8b9Smrg	close(p[0]);
2421d4fba8b9Smrg	dup2(fileno(stderr), 1);
2422d4fba8b9Smrg	dup2(fileno(stderr), 2);
2423d4fba8b9Smrg
2424d4fba8b9Smrg	close(fileno(stderr));
2425d4fba8b9Smrg	close(ConnectionNumber(screen->display));
2426d4fba8b9Smrg	close(screen->respond);
2427d4fba8b9Smrg
2428d4fba8b9Smrg	signal(SIGHUP, SIG_DFL);
2429d4fba8b9Smrg	signal(SIGCHLD, SIG_DFL);
2430d4fba8b9Smrg
2431d4fba8b9Smrg	/* (this is redundant) */
2432d4fba8b9Smrg	if (xtermResetIds(screen) < 0)
2433d4fba8b9Smrg	    exit(ERROR_SETUID);
2434d4fba8b9Smrg
2435d4fba8b9Smrg	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2436d4fba8b9Smrg	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2437d4fba8b9Smrg	exit(ERROR_LOGEXEC);
2438d4fba8b9Smrg    }
2439d4fba8b9Smrg    close(p[0]);
2440d4fba8b9Smrg    screen->logfd = p[1];
2441d4fba8b9Smrg    signal(SIGPIPE, handle_SIGPIPE);
2442d4fba8b9Smrg}
2443d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2444d522f475Smrg
2445d4fba8b9Smrg/*
2446d4fba8b9Smrg * Generate a path for a logfile if no default path is given.
2447d4fba8b9Smrg */
2448d4fba8b9Smrgstatic char *
2449d4fba8b9SmrgGenerateLogPath(void)
2450d4fba8b9Smrg{
2451d4fba8b9Smrg    static char *log_default = NULL;
2452d4fba8b9Smrg
2453d4fba8b9Smrg    /* once opened we just reuse the same log name */
2454d4fba8b9Smrg    if (log_default)
2455d4fba8b9Smrg	return (log_default);
2456d4fba8b9Smrg
2457d4fba8b9Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2458d4fba8b9Smrg    {
2459d4fba8b9Smrg#define LEN_HOSTNAME 255
2460d4fba8b9Smrg	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
2461d4fba8b9Smrg	 * the total length of a domain name (i.e., label octets and label
2462d4fba8b9Smrg	 * length octets) is restricted to 255 octets or less.''
2463d4fba8b9Smrg	 */
2464d4fba8b9Smrg#define LEN_GETPID 9
2465d4fba8b9Smrg	/*
2466d4fba8b9Smrg	 * This is arbitrary...
2467d4fba8b9Smrg	 */
2468d4fba8b9Smrg	const char form[] = "Xterm.log.%s%s.%lu";
2469d4fba8b9Smrg	char where[LEN_HOSTNAME + 1];
2470d4fba8b9Smrg	char when[LEN_TIMESTAMP];
2471d4fba8b9Smrg	time_t now = time((time_t *) 0);
2472d4fba8b9Smrg	struct tm *ltm = (struct tm *) localtime(&now);
2473d4fba8b9Smrg
2474d4fba8b9Smrg	if ((gethostname(where, sizeof(where)) == 0) &&
2475d4fba8b9Smrg	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2476d4fba8b9Smrg	    ((log_default = (char *) malloc((sizeof(form)
2477d4fba8b9Smrg					     + strlen(where)
2478d4fba8b9Smrg					     + strlen(when)
2479d4fba8b9Smrg					     + LEN_GETPID))) != NULL)) {
2480d4fba8b9Smrg	    (void) sprintf(log_default,
2481d4fba8b9Smrg			   form,
2482d4fba8b9Smrg			   where, when,
2483d4fba8b9Smrg			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
2484d4fba8b9Smrg	}
2485d4fba8b9Smrg    }
2486d4fba8b9Smrg#else
2487d4fba8b9Smrg    static const char log_def_name[] = "XtermLog.XXXXXX";
2488d4fba8b9Smrg    if ((log_default = x_strdup(log_def_name)) != NULL) {
2489d1603babSmrg	MakeTemp(log_default);
2490d4fba8b9Smrg    }
2491d4fba8b9Smrg#endif
2492d4fba8b9Smrg
2493d4fba8b9Smrg    return (log_default);
2494d4fba8b9Smrg}
2495d4fba8b9Smrg
2496d522f475Smrgvoid
2497cd3331d0SmrgStartLog(XtermWidget xw)
2498d522f475Smrg{
2499cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2500d522f475Smrg
2501d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2502d522f475Smrg	return;
2503d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
2504d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
2505d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2506d522f475Smrg			 0640);
2507d522f475Smrg    if (screen->logfd < 0)
2508d522f475Smrg	return;			/* open failed */
2509d522f475Smrg#else /*VMS */
25103367019cSmrg
2511d4fba8b9Smrg    /* if we weren't supplied with a logfile path, generate one */
2512d4fba8b9Smrg    if (IsEmpty(screen->logfile))
2513d4fba8b9Smrg	screen->logfile = GenerateLogPath();
25143367019cSmrg
2515d4fba8b9Smrg    /* give up if we were unable to allocate the filename */
2516d4fba8b9Smrg    if (!screen->logfile)
2517d4fba8b9Smrg	return;
2518d522f475Smrg
2519d4fba8b9Smrg    if (*screen->logfile == '|') {	/* exec command */
2520d4fba8b9Smrg#ifdef ALLOWLOGFILEEXEC
2521d4fba8b9Smrg	StartLogExec(screen);
2522d522f475Smrg#else
2523cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2524cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2525d522f475Smrg	return;
2526d522f475Smrg#endif
2527d4fba8b9Smrg    } else if (strcmp(screen->logfile, "-") == 0) {
2528d4fba8b9Smrg	screen->logfd = STDOUT_FILENO;
2529d522f475Smrg    } else {
2530d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2531d522f475Smrg					   screen->gid,
2532d522f475Smrg					   screen->logfile,
2533d4fba8b9Smrg					   True)) < 0)
2534d522f475Smrg	    return;
2535d522f475Smrg    }
2536d522f475Smrg#endif /*VMS */
2537d522f475Smrg    screen->logstart = VTbuffer->next;
2538d522f475Smrg    screen->logging = True;
2539d522f475Smrg    update_logging();
2540d522f475Smrg}
2541d522f475Smrg
2542d522f475Smrgvoid
2543cd3331d0SmrgCloseLog(XtermWidget xw)
2544d522f475Smrg{
2545cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2546cd3331d0Smrg
2547d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2548d522f475Smrg	return;
2549cd3331d0Smrg    FlushLog(xw);
2550d522f475Smrg    close(screen->logfd);
2551d522f475Smrg    screen->logging = False;
2552d522f475Smrg    update_logging();
2553d522f475Smrg}
2554d522f475Smrg
2555d522f475Smrgvoid
2556cd3331d0SmrgFlushLog(XtermWidget xw)
2557d522f475Smrg{
2558cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2559cd3331d0Smrg
2560d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2561d522f475Smrg	Char *cp;
2562d1603babSmrg	size_t i;
2563d522f475Smrg
2564d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2565d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2566d522f475Smrg	if (!tt_new_output)
2567d522f475Smrg	    return;
2568d522f475Smrg	tt_new_output = False;
2569d522f475Smrg#endif /* VMS */
2570d522f475Smrg	cp = VTbuffer->next;
2571d522f475Smrg	if (screen->logstart != 0
2572d1603babSmrg	    && (i = (size_t) (cp - screen->logstart)) > 0) {
2573d1603babSmrg	    IGNORE_RC(write(screen->logfd, screen->logstart, i));
2574d522f475Smrg	}
2575d522f475Smrg	screen->logstart = VTbuffer->next;
2576d522f475Smrg    }
2577d522f475Smrg}
2578d522f475Smrg
2579d522f475Smrg#endif /* ALLOWLOGGING */
2580d522f475Smrg
2581d522f475Smrg/***====================================================================***/
2582d522f475Smrg
2583d4fba8b9Smrgstatic unsigned
2584d4fba8b9SmrgmaskToShift(unsigned long mask)
2585d4fba8b9Smrg{
2586d4fba8b9Smrg    unsigned result = 0;
2587d4fba8b9Smrg    if (mask != 0) {
2588d4fba8b9Smrg	while ((mask & 1) == 0) {
2589d4fba8b9Smrg	    mask >>= 1;
2590d4fba8b9Smrg	    ++result;
2591d4fba8b9Smrg	}
2592d4fba8b9Smrg    }
2593d4fba8b9Smrg    return result;
2594d4fba8b9Smrg}
2595d4fba8b9Smrg
2596d4fba8b9Smrgstatic unsigned
2597d4fba8b9SmrgmaskToWidth(unsigned long mask)
2598d4fba8b9Smrg{
2599d4fba8b9Smrg    unsigned result = 0;
2600d4fba8b9Smrg    while (mask != 0) {
2601d4fba8b9Smrg	if ((mask & 1) != 0)
2602d4fba8b9Smrg	    ++result;
2603d4fba8b9Smrg	mask >>= 1;
2604d4fba8b9Smrg    }
2605d4fba8b9Smrg    return result;
2606d4fba8b9Smrg}
2607d4fba8b9Smrg
2608c48a5815SmrgXVisualInfo *
2609fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2610fa3f02f3Smrg{
2611fa3f02f3Smrg#define MYFMT "getVisualInfo \
2612fa3f02f3Smrgdepth %d, \
2613fa3f02f3Smrgtype %d (%s), \
2614fa3f02f3Smrgsize %d \
2615fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2616fa3f02f3Smrg#define MYARG \
2617fa3f02f3Smrg       vi->depth,\
2618fa3f02f3Smrg       vi->class,\
2619fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2620fa3f02f3Smrg       vi->colormap_size,\
2621fa3f02f3Smrg       vi->red_mask,\
2622fa3f02f3Smrg       vi->green_mask,\
2623fa3f02f3Smrg       vi->blue_mask
2624d522f475Smrg
2625fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2626fa3f02f3Smrg    Display *dpy = screen->display;
2627fa3f02f3Smrg    XVisualInfo myTemplate;
2628fa3f02f3Smrg
2629fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2630fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2631fa3f02f3Smrg								XDefaultScreen(dpy)));
2632fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2633fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2634fa3f02f3Smrg
2635fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2636fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2637d4fba8b9Smrg	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2638d4fba8b9Smrg	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2639d4fba8b9Smrg	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2640d4fba8b9Smrg	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2641d4fba8b9Smrg	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2642d4fba8b9Smrg	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2643d4fba8b9Smrg
2644d4fba8b9Smrg	    xw->has_rgb = ((vi->red_mask != 0) &&
2645d4fba8b9Smrg			   (vi->green_mask != 0) &&
2646d4fba8b9Smrg			   (vi->blue_mask != 0) &&
2647d4fba8b9Smrg			   ((vi->red_mask & vi->green_mask) == 0) &&
2648d4fba8b9Smrg			   ((vi->green_mask & vi->blue_mask) == 0) &&
2649c48a5815Smrg			   ((vi->blue_mask & vi->red_mask) == 0) &&
2650d1603babSmrg			   xw->rgb_widths[0] <= (unsigned) vi->bits_per_rgb &&
2651d1603babSmrg			   xw->rgb_widths[1] <= (unsigned) vi->bits_per_rgb &&
2652d1603babSmrg			   xw->rgb_widths[2] <= (unsigned) vi->bits_per_rgb &&
2653c48a5815Smrg			   (vi->class == TrueColor
2654c48a5815Smrg			    || vi->class == DirectColor));
2655d4fba8b9Smrg
2656fa3f02f3Smrg	    if (resource.reportColors) {
2657fa3f02f3Smrg		printf(MYFMT, MYARG);
2658fa3f02f3Smrg	    }
2659fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2660d4fba8b9Smrg	    TRACE(("...shifts %u/%u/%u\n",
2661d4fba8b9Smrg		   xw->rgb_shifts[0],
2662d4fba8b9Smrg		   xw->rgb_shifts[1],
2663d4fba8b9Smrg		   xw->rgb_shifts[2]));
2664c48a5815Smrg	    TRACE(("...widths %u/%u/%u\n",
2665c48a5815Smrg		   xw->rgb_widths[0],
2666c48a5815Smrg		   xw->rgb_widths[1],
2667c48a5815Smrg		   xw->rgb_widths[2]));
2668fa3f02f3Smrg	}
2669fa3f02f3Smrg    }
2670c48a5815Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
2671fa3f02f3Smrg#undef MYFMT
2672fa3f02f3Smrg#undef MYARG
2673fa3f02f3Smrg}
26743367019cSmrg
26759a64e1c5Smrg#if OPT_ISO_COLORS
2676d4fba8b9Smrgstatic Bool
2677d4fba8b9SmrgReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
26789a64e1c5Smrg{
2679d4fba8b9Smrg    Bool result = False;
2680d4fba8b9Smrg
26819a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
26829a64e1c5Smrg	XColor color;
26839a64e1c5Smrg	char buffer[80];
26849a64e1c5Smrg
26859a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
26869a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2687c48a5815Smrg	(void) QueryOneColor(xw, &color);
2688d4fba8b9Smrg	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2689d4fba8b9Smrg		opcode,
2690d4fba8b9Smrg		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
26919a64e1c5Smrg		color.red,
26929a64e1c5Smrg		color.green,
26939a64e1c5Smrg		color.blue);
26949a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
26959a64e1c5Smrg	unparseputs(xw, buffer);
26969a64e1c5Smrg	unparseputc1(xw, final);
2697d4fba8b9Smrg	result = True;
26989a64e1c5Smrg    }
2699d4fba8b9Smrg    return result;
27009a64e1c5Smrg}
27019a64e1c5Smrg
2702fa3f02f3Smrgstatic void
2703fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2704fa3f02f3Smrg{
2705fa3f02f3Smrg    if (getVisualInfo(xw)) {
2706fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2707fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2708fa3f02f3Smrg    } else {
2709fa3f02f3Smrg	*typep = 0;
2710fa3f02f3Smrg	*sizep = 0;
2711fa3f02f3Smrg    }
27123367019cSmrg}
27133367019cSmrg
27143367019cSmrg#define MAX_COLORTABLE 4096
27153367019cSmrg
27163367019cSmrg/*
27173367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
27183367019cSmrg */
27193367019cSmrgstatic Boolean
27203367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
27213367019cSmrg{
27223367019cSmrg    Colormap cmap = xw->core.colormap;
27233367019cSmrg    TScreen *screen = TScreenOf(xw);
2724fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
27253367019cSmrg
2726fa3f02f3Smrg    if (!result
27273367019cSmrg	&& length != 0
27283367019cSmrg	&& length < MAX_COLORTABLE) {
27293367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2730037a25ddSmrg
27313367019cSmrg	if (screen->cmap_data != 0) {
2732037a25ddSmrg	    unsigned i;
2733d4fba8b9Smrg	    unsigned shift;
2734d4fba8b9Smrg
2735d4fba8b9Smrg	    if (getVisualInfo(xw))
2736d4fba8b9Smrg		shift = xw->rgb_shifts[2];
2737d4fba8b9Smrg	    else
2738d4fba8b9Smrg		shift = 0;
2739037a25ddSmrg
27403367019cSmrg	    screen->cmap_size = length;
27413367019cSmrg
27423367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
2743d4fba8b9Smrg		screen->cmap_data[i].pixel = (unsigned long) i << shift;
27443367019cSmrg	    }
27453367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
27463367019cSmrg					     cmap,
27473367019cSmrg					     screen->cmap_data,
27483367019cSmrg					     (int) screen->cmap_size) != 0);
27493367019cSmrg	}
27503367019cSmrg    }
2751d522f475Smrg    return result;
2752d522f475Smrg}
2753d522f475Smrg
2754c48a5815Smrg/***====================================================================***/
2755c48a5815Smrg
2756c48a5815Smrg/*
2757c48a5815Smrg * Call this function with def->{red,green,blue} initialized, to obtain a pixel
2758c48a5815Smrg * value.
2759c48a5815Smrg */
2760c48a5815SmrgBoolean
2761c48a5815SmrgAllocOneColor(XtermWidget xw, XColor *def)
2762c48a5815Smrg{
2763c48a5815Smrg    TScreen *screen = TScreenOf(xw);
2764c48a5815Smrg    Boolean result = True;
2765c48a5815Smrg
2766c48a5815Smrg#define MaskIt(name,nn) \
2767c48a5815Smrg	((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
2768c48a5815Smrg	             << xw->rgb_shifts[nn]) \
2769c48a5815Smrg	 & xw->visInfo->name ##_mask)
2770c48a5815Smrg
2771d1603babSmrg#define VisualIsRGB(xw) (getVisualInfo(xw) != NULL && xw->has_rgb && xw->visInfo->bits_per_rgb <= 8)
2772d1603babSmrg
2773d1603babSmrg    if (VisualIsRGB(xw)) {
2774c48a5815Smrg	def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
2775c48a5815Smrg    } else {
2776c48a5815Smrg	Display *dpy = screen->display;
2777c48a5815Smrg	if (!XAllocColor(dpy, xw->core.colormap, def)) {
2778c48a5815Smrg	    /*
2779c48a5815Smrg	     * Decide between foreground and background by a grayscale
2780c48a5815Smrg	     * approximation.
2781c48a5815Smrg	     */
2782c48a5815Smrg	    int bright = def->red * 3 + def->green * 10 + def->blue;
2783c48a5815Smrg	    int levels = 14 * 0x8000;
2784c48a5815Smrg	    def->pixel = ((bright >= levels)
2785c48a5815Smrg			  ? xw->dft_background
2786c48a5815Smrg			  : xw->dft_foreground);
2787d1603babSmrg	    TRACE(("XAllocColor failed, for %04x/%04x/%04x: choose %08lx (%d vs %d)\n",
2788d1603babSmrg		   def->red, def->green, def->blue,
2789d1603babSmrg		   def->pixel, bright, levels));
2790c48a5815Smrg	    result = False;
2791c48a5815Smrg	}
2792c48a5815Smrg    }
2793c48a5815Smrg    return result;
2794c48a5815Smrg}
2795c48a5815Smrg
2796c48a5815Smrg/***====================================================================***/
2797c48a5815Smrg
2798c48a5815Smrg/*
2799c48a5815Smrg * Call this function with def->pixel set to the color that we want to convert
2800c48a5815Smrg * to separate red/green/blue.
2801c48a5815Smrg */
2802c48a5815SmrgBoolean
2803c48a5815SmrgQueryOneColor(XtermWidget xw, XColor *def)
2804c48a5815Smrg{
2805c48a5815Smrg    Boolean result = True;
2806c48a5815Smrg
2807c48a5815Smrg#define UnMaskIt(name,nn) \
2808c48a5815Smrg	((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
2809c48a5815Smrg#define UnMaskIt2(name,nn) \
2810c48a5815Smrg	(unsigned short)((((UnMaskIt(name,nn) << 8) \
2811c48a5815Smrg			   |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
2812c48a5815Smrg
2813d1603babSmrg    if (VisualIsRGB(xw)) {
2814c48a5815Smrg	/* *INDENT-EQLS* */
2815c48a5815Smrg	def->red   = UnMaskIt2(red, 0);
2816c48a5815Smrg	def->green = UnMaskIt2(green, 1);
2817c48a5815Smrg	def->blue  = UnMaskIt2(blue, 2);
2818d1603babSmrg    } else {
2819d1603babSmrg	Display *dpy = TScreenOf(xw)->display;
2820d1603babSmrg	if (!XQueryColor(dpy, xw->core.colormap, def)) {
2821d1603babSmrg	    TRACE(("XQueryColor failed, given %08lx\n", def->pixel));
2822d1603babSmrg	    result     = False;
2823d1603babSmrg	}
2824c48a5815Smrg    }
2825c48a5815Smrg    return result;
2826c48a5815Smrg}
2827c48a5815Smrg
2828c48a5815Smrg/***====================================================================***/
2829c48a5815Smrg
2830d522f475Smrg/*
2831d522f475Smrg * Find closest color for "def" in "cmap".
2832d522f475Smrg * Set "def" to the resulting color.
2833d522f475Smrg *
2834d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2835d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2836d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2837d522f475Smrg *
2838d522f475Smrg * Return False if not able to find or allocate a color.
2839d522f475Smrg */
2840d522f475Smrgstatic Boolean
2841c48a5815SmrgallocateClosestRGB(XtermWidget xw, XColor *def)
2842d522f475Smrg{
28433367019cSmrg    TScreen *screen = TScreenOf(xw);
2844d522f475Smrg    Boolean result = False;
28453367019cSmrg    unsigned cmap_type;
2846d522f475Smrg    unsigned cmap_size;
2847d522f475Smrg
2848fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2849d522f475Smrg
28503367019cSmrg    if ((cmap_type & 1) != 0) {
28513367019cSmrg
28523367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2853037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2854d522f475Smrg
2855d522f475Smrg	    if (tried != 0) {
2856037a25ddSmrg		unsigned attempts;
2857d522f475Smrg
2858d522f475Smrg		/*
2859d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2860d522f475Smrg		 * approximation to the requested color.
2861d522f475Smrg		 */
2862d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2863d522f475Smrg		    Boolean first = True;
2864037a25ddSmrg		    double bestRGB = 0.0;
2865037a25ddSmrg		    unsigned bestInx = 0;
2866037a25ddSmrg		    unsigned i;
2867d522f475Smrg
2868d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2869d522f475Smrg			if (!tried[bestInx]) {
2870037a25ddSmrg			    double diff, thisRGB = 0.0;
2871037a25ddSmrg
2872d522f475Smrg			    /*
2873d522f475Smrg			     * Look for the best match based on luminance.
2874d522f475Smrg			     * Measure this by the least-squares difference of
2875d522f475Smrg			     * the weighted R/G/B components from the color map
2876d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2877d522f475Smrg			     * component of the YIQ color space model for
2878d522f475Smrg			     * weights that correspond to the luminance.
2879d522f475Smrg			     */
2880d522f475Smrg#define AddColorWeight(weight, color) \
28813367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2882037a25ddSmrg			    thisRGB += diff * diff
2883d522f475Smrg
2884d522f475Smrg			    AddColorWeight(0.30, red);
2885d522f475Smrg			    AddColorWeight(0.61, green);
2886d522f475Smrg			    AddColorWeight(0.11, blue);
2887d522f475Smrg
2888d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2889d522f475Smrg				first = False;
2890d522f475Smrg				bestInx = i;
2891d522f475Smrg				bestRGB = thisRGB;
2892d522f475Smrg			    }
2893d522f475Smrg			}
2894d522f475Smrg		    }
2895c48a5815Smrg		    if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
28963367019cSmrg			*def = screen->cmap_data[bestInx];
28973367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
28983367019cSmrg			       def->green, def->blue));
2899d522f475Smrg			result = True;
2900d522f475Smrg			break;
2901d522f475Smrg		    }
2902d522f475Smrg		    /*
2903d522f475Smrg		     * It failed - either the color map entry was readonly, or
2904d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2905d522f475Smrg		     * so we will ignore it
2906d522f475Smrg		     */
2907d522f475Smrg		    tried[bestInx] = True;
2908d522f475Smrg		}
2909d522f475Smrg		free(tried);
2910d522f475Smrg	    }
2911d522f475Smrg	}
2912d522f475Smrg    }
2913d522f475Smrg    return result;
2914d522f475Smrg}
2915d522f475Smrg
29163367019cSmrg#ifndef ULONG_MAX
29173367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
29183367019cSmrg#endif
29193367019cSmrg
2920d522f475Smrg/*
2921d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2922d522f475Smrg * to 256.
2923d522f475Smrg *
2924d522f475Smrg * Returns
2925d522f475Smrg *	-1 on error
2926d522f475Smrg *	0 on no change
2927d522f475Smrg *	1 if a new color was allocated.
2928d522f475Smrg */
2929d522f475Smrgstatic int
2930d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2931d522f475Smrg		  ColorRes * res,
2932cd3331d0Smrg		  const char *spec)
2933d522f475Smrg{
2934d522f475Smrg    int result;
2935d522f475Smrg    XColor def;
2936d522f475Smrg
29373367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2938c48a5815Smrg	if (res->mode == True &&
2939c48a5815Smrg	    EQL_COLOR_RES(res, def.pixel)) {
2940d522f475Smrg	    result = 0;
2941d522f475Smrg	} else {
2942d522f475Smrg	    result = 1;
2943d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
29443367019cSmrg	    res->red = def.red;
29453367019cSmrg	    res->green = def.green;
29463367019cSmrg	    res->blue = def.blue;
29473367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
29483367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
29493367019cSmrg		   def.red,
29503367019cSmrg		   def.green,
29513367019cSmrg		   def.blue,
29523367019cSmrg		   def.pixel));
2953d522f475Smrg	    if (!res->mode)
2954d522f475Smrg		result = 0;
2955d522f475Smrg	    res->mode = True;
2956d522f475Smrg	}
2957d522f475Smrg    } else {
2958d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2959d522f475Smrg	result = -1;
2960d522f475Smrg    }
2961d522f475Smrg    return (result);
2962d522f475Smrg}
2963d522f475Smrg
2964d522f475SmrgPixel
2965cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2966d522f475Smrg{
2967d522f475Smrg    Pixel result = 0;
2968d522f475Smrg
2969d522f475Smrg    if (res->mode) {
2970d522f475Smrg	result = res->value;
2971d522f475Smrg    } else {
2972d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2973cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2974d522f475Smrg
2975cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2976cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2977d522f475Smrg
2978cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2979cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2980d522f475Smrg		res->mode = -True;
29813367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
29823367019cSmrg			     NonNull(res->resource));
2983d522f475Smrg	    }
2984d522f475Smrg	    result = res->value;
2985d522f475Smrg	} else {
2986d522f475Smrg	    result = 0;
2987d522f475Smrg	}
2988d522f475Smrg    }
2989d522f475Smrg    return result;
2990d522f475Smrg}
2991d522f475Smrg
2992cd3331d0Smrgstatic int
2993cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2994cd3331d0Smrg{
2995cd3331d0Smrg    int code;
2996cd3331d0Smrg
2997cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2998cd3331d0Smrg	code = -1;
2999cd3331d0Smrg    } else {
3000cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
3001cd3331d0Smrg
3002cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
3003cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
3004cd3331d0Smrg    }
3005cd3331d0Smrg    return code;
3006cd3331d0Smrg}
3007cd3331d0Smrg
3008cd3331d0Smrg/*
3009cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
3010cd3331d0Smrg * values from the given buffer.
3011cd3331d0Smrg *
3012cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
3013cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
3014cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
3015cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
3016cd3331d0Smrg * 'first' set to the beginning of those indices.
3017cd3331d0Smrg *
3018cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
3019cd3331d0Smrg */
3020d522f475Smrgstatic Bool
3021d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
3022d4fba8b9Smrg		       int opcode,
3023d522f475Smrg		       char *buf,
3024cd3331d0Smrg		       int first,
3025d522f475Smrg		       int final)
3026d522f475Smrg{
3027d522f475Smrg    int repaint = False;
3028d522f475Smrg    int code;
3029cd3331d0Smrg    int last = (MAXCOLORS - first);
3030d4fba8b9Smrg    int queried = 0;
3031d522f475Smrg
3032d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
3033d522f475Smrg
3034d522f475Smrg    while (buf && *buf) {
3035037a25ddSmrg	int color;
3036037a25ddSmrg	char *name = strchr(buf, ';');
3037037a25ddSmrg
3038d522f475Smrg	if (name == NULL)
3039d522f475Smrg	    break;
3040d522f475Smrg	*name = '\0';
3041d522f475Smrg	name++;
3042d522f475Smrg	color = atoi(buf);
3043cd3331d0Smrg	if (color < 0 || color >= last)
3044cd3331d0Smrg	    break;		/* quit on any error */
3045d522f475Smrg	buf = strchr(name, ';');
3046d522f475Smrg	if (buf) {
3047d522f475Smrg	    *buf = '\0';
3048d522f475Smrg	    buf++;
3049d522f475Smrg	}
3050cd3331d0Smrg	if (!strcmp(name, "?")) {
3051d4fba8b9Smrg	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3052d4fba8b9Smrg		++queried;
3053cd3331d0Smrg	} else {
3054cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
3055d522f475Smrg	    if (code < 0) {
3056d522f475Smrg		/* stop on any error */
3057d522f475Smrg		break;
3058d522f475Smrg	    } else if (code > 0) {
3059d522f475Smrg		repaint = True;
3060d522f475Smrg	    }
3061d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
3062d522f475Smrg	     * change style (dynamic colors).
3063d522f475Smrg	     */
3064d522f475Smrg	}
3065d522f475Smrg    }
3066d4fba8b9Smrg    if (queried)
3067d4fba8b9Smrg	unparse_end(xw);
3068d522f475Smrg
3069d522f475Smrg    return (repaint);
3070d522f475Smrg}
3071cd3331d0Smrg
3072cd3331d0Smrgstatic Bool
3073cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
3074cd3331d0Smrg{
3075cd3331d0Smrg    Bool repaint = False;
3076cd3331d0Smrg    int last = MAXCOLORS - start;
3077cd3331d0Smrg
3078cd3331d0Smrg    if (color >= 0 && color < last) {
3079cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3080cd3331d0Smrg
3081cd3331d0Smrg	if (res->mode) {
3082cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
3083cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3084cd3331d0Smrg		repaint = True;
3085cd3331d0Smrg	    }
3086cd3331d0Smrg	}
3087cd3331d0Smrg    }
3088cd3331d0Smrg    return repaint;
3089cd3331d0Smrg}
3090cd3331d0Smrg
3091cd3331d0Smrgint
3092cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3093cd3331d0Smrg{
3094cd3331d0Smrg    int repaint = 0;
3095cd3331d0Smrg    int color;
3096cd3331d0Smrg
3097cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3098cd3331d0Smrg    if (*buf != '\0') {
3099cd3331d0Smrg	/* reset specific colors */
3100cd3331d0Smrg	while (!IsEmpty(buf)) {
3101cd3331d0Smrg	    char *next;
3102cd3331d0Smrg
3103037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
3104037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
3105cd3331d0Smrg		break;		/* no number at all */
3106cd3331d0Smrg	    if (next != 0) {
3107cd3331d0Smrg		if (strchr(";", *next) == 0)
3108cd3331d0Smrg		    break;	/* unexpected delimiter */
3109cd3331d0Smrg		++next;
3110cd3331d0Smrg	    }
3111cd3331d0Smrg
3112cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3113cd3331d0Smrg		++repaint;
3114cd3331d0Smrg	    }
3115cd3331d0Smrg	    buf = next;
3116cd3331d0Smrg	}
3117cd3331d0Smrg    } else {
3118cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3119cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
3120cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3121cd3331d0Smrg		++repaint;
3122cd3331d0Smrg	    }
3123cd3331d0Smrg	}
3124cd3331d0Smrg    }
3125cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3126cd3331d0Smrg    return repaint;
3127cd3331d0Smrg}
3128d522f475Smrg#else
3129c48a5815Smrg#define allocateClosestRGB(xw, def) 0
3130d522f475Smrg#endif /* OPT_ISO_COLORS */
3131d522f475Smrg
3132fa3f02f3SmrgBoolean
31339a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
3134fa3f02f3Smrg{
3135c48a5815Smrg    (void) xw;
3136c48a5815Smrg    (void) def;
3137c48a5815Smrg    return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
3138fa3f02f3Smrg}
3139fa3f02f3Smrg
31403367019cSmrgstatic Boolean
31419a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
31423367019cSmrg{
31433367019cSmrg    Boolean result = False;
31443367019cSmrg    TScreen *screen = TScreenOf(xw);
31453367019cSmrg    Colormap cmap = xw->core.colormap;
31468f44fb3bSmrg    size_t have = strlen(spec);
31473367019cSmrg
31488f44fb3bSmrg    if (have == 0 || have > MAX_U_STRING) {
31498f44fb3bSmrg	if (resource.reportColors) {
3150c48a5815Smrg	    printf("color  (ignored, length %lu)\n", (unsigned long) have);
31518f44fb3bSmrg	}
31528f44fb3bSmrg    } else if (XParseColor(screen->display, cmap, spec, def)) {
3153fa3f02f3Smrg	XColor save_def = *def;
3154fa3f02f3Smrg	if (resource.reportColors) {
3155fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3156fa3f02f3Smrg		   def->red, def->green, def->blue,
3157fa3f02f3Smrg		   spec);
3158fa3f02f3Smrg	}
3159fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
3160fa3f02f3Smrg	    if (resource.reportColors) {
3161fa3f02f3Smrg		if (def->red != save_def.red ||
3162fa3f02f3Smrg		    def->green != save_def.green ||
3163fa3f02f3Smrg		    def->blue != save_def.blue) {
3164fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3165fa3f02f3Smrg			   def->red, def->green, def->blue,
3166fa3f02f3Smrg			   spec);
3167fa3f02f3Smrg		}
3168fa3f02f3Smrg	    }
3169fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3170fa3f02f3Smrg		   def->red, def->green, def->blue));
3171fa3f02f3Smrg	    result = True;
3172fa3f02f3Smrg	}
31733367019cSmrg    }
31743367019cSmrg    return result;
31753367019cSmrg}
31763367019cSmrg
31773367019cSmrg/*
31783367019cSmrg * This provides an approximation (the closest color from xterm's palette)
31793367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
31803367019cSmrg * because of the context in which it is used.
31813367019cSmrg */
31823367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
31833367019cSmrgint
31843367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
31853367019cSmrg{
31863367019cSmrg    int result = -1;
3187c48a5815Smrg#if OPT_ISO_COLORS
31883367019cSmrg    int n;
31893367019cSmrg    int best_index = -1;
31903367019cSmrg    unsigned long best_value = 0;
31913367019cSmrg    unsigned long this_value;
31923367019cSmrg    long diff_red, diff_green, diff_blue;
31933367019cSmrg
31943367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
31953367019cSmrg
31963367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
31973367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
31983367019cSmrg
31993367019cSmrg	/* ensure that we have a value for each of the colors */
32003367019cSmrg	if (!res->mode) {
32013367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
32023367019cSmrg	}
32033367019cSmrg
32043367019cSmrg	/* find the closest match */
32053367019cSmrg	if (res->mode == True) {
32063367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
32073367019cSmrg		    res->value, res->red, res->green, res->blue));
32083367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
32093367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
32103367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
32113367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
32123367019cSmrg					  + (diff_green * diff_green)
32133367019cSmrg					  + (diff_blue * diff_blue));
32143367019cSmrg	    if (best_index < 0 || this_value < best_value) {
32153367019cSmrg		best_index = n;
32163367019cSmrg		best_value = this_value;
32173367019cSmrg	    }
32183367019cSmrg	}
32193367019cSmrg    }
32203367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
32213367019cSmrg    result = best_index;
3222c48a5815Smrg
32233367019cSmrg#else
32243367019cSmrg    (void) xw;
32253367019cSmrg    (void) find_red;
32263367019cSmrg    (void) find_green;
32273367019cSmrg    (void) find_blue;
32283367019cSmrg#endif
32293367019cSmrg    return result;
32303367019cSmrg}
32313367019cSmrg
3232d4fba8b9Smrg#if OPT_DIRECT_COLOR
3233d4fba8b9Smrgint
3234d4fba8b9SmrggetDirectColor(XtermWidget xw, int red, int green, int blue)
3235d4fba8b9Smrg{
3236c48a5815Smrg    Pixel result = 0;
3237c48a5815Smrg
3238c48a5815Smrg#define getRGB(name,shift) \
3239c48a5815Smrg    do { \
3240c48a5815Smrg	Pixel value = (Pixel) name & 0xff; \
3241c48a5815Smrg	if (xw->rgb_widths[shift] < 8) { \
3242c48a5815Smrg	    value >>= (int) (8 - xw->rgb_widths[shift]); \
3243c48a5815Smrg	} \
3244c48a5815Smrg	value <<= xw->rgb_shifts[shift]; \
3245c48a5815Smrg	value &= xw->visInfo->name ##_mask; \
3246c48a5815Smrg	result |= value; \
3247c48a5815Smrg    } while (0)
3248c48a5815Smrg
3249c48a5815Smrg    getRGB(red, 0);
3250c48a5815Smrg    getRGB(green, 1);
3251c48a5815Smrg    getRGB(blue, 2);
3252c48a5815Smrg
3253c48a5815Smrg#undef getRGB
3254c48a5815Smrg
3255d4fba8b9Smrg    return (int) result;
3256d4fba8b9Smrg}
3257d4fba8b9Smrg
3258d4fba8b9Smrgstatic void
3259d4fba8b9SmrgformatDirectColor(char *target, XtermWidget xw, unsigned value)
3260d4fba8b9Smrg{
3261c48a5815Smrg    Pixel result[3];
3262c48a5815Smrg
3263c48a5815Smrg#define getRGB(name, shift) \
3264c48a5815Smrg    do { \
3265c48a5815Smrg	result[shift] = value & xw->visInfo->name ## _mask; \
3266c48a5815Smrg	result[shift] >>= xw->rgb_shifts[shift]; \
3267c48a5815Smrg	if (xw->rgb_widths[shift] < 8) \
3268c48a5815Smrg	    result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
3269c48a5815Smrg    } while(0)
3270c48a5815Smrg
3271c48a5815Smrg    getRGB(red, 0);
3272c48a5815Smrg    getRGB(green, 1);
3273c48a5815Smrg    getRGB(blue, 2);
3274c48a5815Smrg
3275c48a5815Smrg#undef getRGB
3276c48a5815Smrg
3277c48a5815Smrg    sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
3278d4fba8b9Smrg}
3279d4fba8b9Smrg#endif /* OPT_DIRECT_COLOR */
3280d4fba8b9Smrg
3281d4fba8b9Smrg#define fg2SGR(n) \
3282d4fba8b9Smrg		(n) >= 8 ? 9 : 3, \
3283d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3284d4fba8b9Smrg#define bg2SGR(n) \
3285d4fba8b9Smrg		(n) >= 8 ? 10 : 4, \
3286d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3287d4fba8b9Smrg
3288d4fba8b9Smrg#define EndOf(s) (s) + strlen(s)
3289d4fba8b9Smrg
3290d4fba8b9Smrgchar *
3291d4fba8b9SmrgxtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3292d4fba8b9Smrg{
3293d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
3294d4fba8b9Smrg    char *msg = target;
3295d4fba8b9Smrg
3296d4fba8b9Smrg    strcpy(target, "0");
3297d4fba8b9Smrg    if (attr & BOLD)
3298d4fba8b9Smrg	strcat(msg, ";1");
3299d4fba8b9Smrg    if (attr & UNDERLINE)
3300d4fba8b9Smrg	strcat(msg, ";4");
3301d4fba8b9Smrg    if (attr & BLINK)
3302d4fba8b9Smrg	strcat(msg, ";5");
3303d4fba8b9Smrg    if (attr & INVERSE)
3304d4fba8b9Smrg	strcat(msg, ";7");
3305d4fba8b9Smrg    if (attr & INVISIBLE)
3306d4fba8b9Smrg	strcat(msg, ";8");
3307d4fba8b9Smrg#if OPT_WIDE_ATTRS
3308d4fba8b9Smrg    if (attr & ATR_FAINT)
3309d4fba8b9Smrg	strcat(msg, ";2");
3310d4fba8b9Smrg    if (attr & ATR_ITALIC)
3311d4fba8b9Smrg	strcat(msg, ";3");
3312d4fba8b9Smrg    if (attr & ATR_STRIKEOUT)
3313d4fba8b9Smrg	strcat(msg, ";9");
3314d4fba8b9Smrg    if (attr & ATR_DBL_UNDER)
3315d4fba8b9Smrg	strcat(msg, ";21");
3316d4fba8b9Smrg#endif
3317d4fba8b9Smrg#if OPT_256_COLORS || OPT_88_COLORS
3318d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3319d4fba8b9Smrg	if (attr & FG_COLOR) {
3320d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3321d4fba8b9Smrg		strcat(msg, ";38:2::");
3322d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3323d4fba8b9Smrg	    }) if (fg >= 16) {
3324d4fba8b9Smrg		sprintf(EndOf(msg), ";38:5:%d", fg);
3325d4fba8b9Smrg	    } else {
3326d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3327d4fba8b9Smrg	    }
3328d4fba8b9Smrg	}
3329d4fba8b9Smrg	if (attr & BG_COLOR) {
3330d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3331d4fba8b9Smrg		strcat(msg, ";48:2::");
3332d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3333d4fba8b9Smrg	    }) if (bg >= 16) {
3334d4fba8b9Smrg		sprintf(EndOf(msg), ";48:5:%d", bg);
3335d4fba8b9Smrg	    } else {
3336d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3337d4fba8b9Smrg	    }
3338d4fba8b9Smrg	}
3339d4fba8b9Smrg    });
3340d4fba8b9Smrg#elif OPT_ISO_COLORS
3341d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3342d4fba8b9Smrg	if (attr & FG_COLOR) {
3343d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3344d4fba8b9Smrg	}
3345d4fba8b9Smrg	if (attr & BG_COLOR) {
3346d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3347d4fba8b9Smrg	}
3348d4fba8b9Smrg    });
3349d4fba8b9Smrg#else
3350d4fba8b9Smrg    (void) screen;
3351d4fba8b9Smrg    (void) fg;
3352d4fba8b9Smrg    (void) bg;
3353d4fba8b9Smrg#endif
3354d4fba8b9Smrg    return target;
3355d4fba8b9Smrg}
3356d4fba8b9Smrg
3357d522f475Smrg#if OPT_PASTE64
3358d522f475Smrgstatic void
3359fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3360d522f475Smrg{
3361d522f475Smrg#define PDATA(a,b) { a, #b }
3362d522f475Smrg    static struct {
3363d522f475Smrg	char given;
3364cd3331d0Smrg	String result;
3365d522f475Smrg    } table[] = {
3366d522f475Smrg	PDATA('s', SELECT),
3367d522f475Smrg	    PDATA('p', PRIMARY),
3368d4fba8b9Smrg	    PDATA('q', SECONDARY),
3369d522f475Smrg	    PDATA('c', CLIPBOARD),
3370d522f475Smrg	    PDATA('0', CUT_BUFFER0),
3371d522f475Smrg	    PDATA('1', CUT_BUFFER1),
3372d522f475Smrg	    PDATA('2', CUT_BUFFER2),
3373d522f475Smrg	    PDATA('3', CUT_BUFFER3),
3374d522f475Smrg	    PDATA('4', CUT_BUFFER4),
3375d522f475Smrg	    PDATA('5', CUT_BUFFER5),
3376d522f475Smrg	    PDATA('6', CUT_BUFFER6),
3377d522f475Smrg	    PDATA('7', CUT_BUFFER7),
3378d522f475Smrg    };
3379d1603babSmrg    char target_used[XtNumber(table)];
3380d1603babSmrg    char select_code[XtNumber(table) + 1];
3381d1603babSmrg    String select_args[XtNumber(table) + 1];
3382d522f475Smrg
3383cd3331d0Smrg    const char *base = buf;
3384d1603babSmrg    Cardinal j;
3385d1603babSmrg    Cardinal num_targets = 0;
3386d522f475Smrg
3387d522f475Smrg    TRACE(("Manipulate selection data\n"));
3388d522f475Smrg
3389d1603babSmrg    memset(target_used, 0, sizeof(target_used));
3390d522f475Smrg    while (*buf != ';' && *buf != '\0') {
3391d522f475Smrg	++buf;
3392d522f475Smrg    }
3393d522f475Smrg
3394d522f475Smrg    if (*buf == ';') {
3395037a25ddSmrg
3396d522f475Smrg	*buf++ = '\0';
3397d522f475Smrg	if (*base == '\0')
3398d522f475Smrg	    base = "s0";
3399d522f475Smrg
3400d1603babSmrg	while (*base != '\0') {
3401d1603babSmrg	    for (j = 0; j < XtNumber(table); ++j) {
3402d1603babSmrg		if (*base == table[j].given) {
3403d1603babSmrg		    if (!target_used[j]) {
3404d1603babSmrg			target_used[j] = 1;
3405d1603babSmrg			select_code[num_targets] = *base;
3406d1603babSmrg			select_args[num_targets++] = table[j].result;
3407d1603babSmrg			TRACE(("atom[%d] %s\n", num_targets, table[j].result));
34083367019cSmrg		    }
3409d1603babSmrg		    break;
34103367019cSmrg		}
3411d1603babSmrg	    }
3412d1603babSmrg	    ++base;
3413d1603babSmrg	}
3414d1603babSmrg	select_code[num_targets] = '\0';
3415d1603babSmrg
3416d1603babSmrg	if (!strcmp(buf, "?")) {
3417d1603babSmrg	    if (AllowWindowOps(xw, ewGetSelection)) {
3418d1603babSmrg		TRACE(("Getting selection\n"));
3419d1603babSmrg		unparseputc1(xw, ANSI_OSC);
3420d1603babSmrg		unparseputs(xw, "52");
3421d1603babSmrg		unparseputc(xw, ';');
3422d1603babSmrg
3423d1603babSmrg		unparseputs(xw, select_code);
3424d1603babSmrg		unparseputc(xw, ';');
3425d1603babSmrg
3426d1603babSmrg		/* Tell xtermGetSelection data is base64 encoded */
3427d1603babSmrg		screen->base64_paste = num_targets;
3428d1603babSmrg		screen->base64_final = final;
3429d1603babSmrg
3430d1603babSmrg		screen->selection_time =
3431d1603babSmrg		    XtLastTimestampProcessed(TScreenOf(xw)->display);
3432d1603babSmrg
3433d1603babSmrg		/* terminator will be written in this call */
3434d1603babSmrg		xtermGetSelection((Widget) xw,
3435d1603babSmrg				  screen->selection_time,
3436d1603babSmrg				  select_args, num_targets,
3437d1603babSmrg				  NULL);
3438d1603babSmrg	    }
3439d1603babSmrg	} else {
3440d1603babSmrg	    if (AllowWindowOps(xw, ewSetSelection)) {
3441d1603babSmrg		char *old = buf;
3442d1603babSmrg
3443d1603babSmrg		TRACE(("Setting selection(%s) with %s\n", select_code, buf));
3444d1603babSmrg		screen->selection_time =
3445d1603babSmrg		    XtLastTimestampProcessed(TScreenOf(xw)->display);
3446d1603babSmrg
3447d1603babSmrg		for (j = 0; j < num_targets; ++j) {
3448d1603babSmrg		    buf = old;
3449d1603babSmrg		    ClearSelectionBuffer(screen, select_args[j]);
3450d1603babSmrg		    while (*buf != '\0') {
3451d1603babSmrg			AppendToSelectionBuffer(screen,
3452d1603babSmrg						CharOf(*buf++),
3453d1603babSmrg						select_args[j]);
34543367019cSmrg		    }
34553367019cSmrg		}
3456d1603babSmrg		CompleteSelection(xw, select_args, num_targets);
3457cd3331d0Smrg	    }
3458d522f475Smrg	}
3459d522f475Smrg    }
3460d522f475Smrg}
3461d522f475Smrg#endif /* OPT_PASTE64 */
3462d522f475Smrg
3463d522f475Smrg/***====================================================================***/
3464d522f475Smrg
3465d4fba8b9Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3466d4fba8b9Smrg			 || (xw->screen.utf8_title) \
3467d4fba8b9Smrg			 || (xw->screen.c1_printable))
3468cd3331d0Smrg
3469d522f475Smrgstatic Bool
3470fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3471d522f475Smrg{
3472cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3473d522f475Smrg    Bool result = False;
3474d522f475Smrg    Char *cp = *bufp;
3475d522f475Smrg    Char *next = cp;
3476d522f475Smrg
3477d522f475Smrg    (void) screen;
3478d522f475Smrg    (void) last;
3479d522f475Smrg
3480d522f475Smrg#if OPT_WIDE_CHARS
3481cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3482d522f475Smrg	PtyData data;
3483d522f475Smrg
34849a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3485d522f475Smrg	    if (data.utf_data != UCS_REPL
3486d522f475Smrg		&& (data.utf_data >= 128 ||
3487d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3488d522f475Smrg		next += (data.utf_size - 1);
3489d522f475Smrg		result = True;
3490d522f475Smrg	    } else {
3491d522f475Smrg		result = False;
3492d522f475Smrg	    }
3493d522f475Smrg	} else {
3494d522f475Smrg	    result = False;
3495d522f475Smrg	}
3496d522f475Smrg    } else
3497d522f475Smrg#endif
3498d522f475Smrg#if OPT_C1_PRINT
3499d522f475Smrg	if (screen->c1_printable
3500d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3501d522f475Smrg	result = True;
3502d522f475Smrg    } else
3503d522f475Smrg#endif
3504d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3505d522f475Smrg	result = True;
3506d522f475Smrg    }
3507d522f475Smrg    *bufp = next;
3508d522f475Smrg    return result;
3509d522f475Smrg}
3510d522f475Smrg
3511d522f475Smrg/***====================================================================***/
3512d522f475Smrg
3513d522f475Smrg/*
3514d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3515cd3331d0Smrg * array indices.  Compare with TermColors.
3516d522f475Smrg */
3517d522f475Smrgtypedef enum {
3518d522f475Smrg    OSC_TEXT_FG = 10
3519d522f475Smrg    ,OSC_TEXT_BG
3520d522f475Smrg    ,OSC_TEXT_CURSOR
3521d522f475Smrg    ,OSC_MOUSE_FG
3522d522f475Smrg    ,OSC_MOUSE_BG
3523d522f475Smrg#if OPT_TEK4014
3524d522f475Smrg    ,OSC_TEK_FG = 15
3525d522f475Smrg    ,OSC_TEK_BG
3526d522f475Smrg#endif
3527d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3528d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3529d522f475Smrg#endif
3530d522f475Smrg#if OPT_TEK4014
3531d522f475Smrg    ,OSC_TEK_CURSOR = 18
3532d522f475Smrg#endif
3533d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3534d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3535d522f475Smrg#endif
3536d522f475Smrg    ,OSC_NCOLORS
3537d522f475Smrg} OscTextColors;
3538d522f475Smrg
3539cd3331d0Smrg/*
3540cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3541cd3331d0Smrg */
3542cd3331d0Smrg#define OSC_RESET 100
3543cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3544cd3331d0Smrg
3545d1603babSmrg/*
3546d1603babSmrg * Other (non-color) OSC controls
3547d1603babSmrg */
3548d1603babSmrgtypedef enum {
3549d1603babSmrg    OSC_IconBoth = 0
3550d1603babSmrg    ,OSC_IconOnly = 1
3551d1603babSmrg    ,OSC_TitleOnly = 2
3552d1603babSmrg    ,OSC_X_Property = 3
3553d1603babSmrg    ,OSC_SetAnsiColor = 4
3554d1603babSmrg    ,OSC_GetAnsiColors = 5
3555d1603babSmrg    ,OSC_ColorMode = 6
3556d1603babSmrg    ,OSC_SetupPointer = 22
3557d1603babSmrg    ,OSC_Unused_30 = 30		/* Konsole (unused) */
3558d1603babSmrg    ,OSC_Unused_31 = 31		/* Konsole (unused) */
3559d1603babSmrg    ,OSC_NewLogFile = 46
3560d1603babSmrg    ,OSC_FontOps = 50
3561d1603babSmrg    ,OSC_Unused_51		/* Emacs (unused) */
3562d1603babSmrg    ,OSC_SelectionData = 52
3563d1603babSmrg    ,OSC_AllowedOps = 60
3564d1603babSmrg    ,OSC_DisallowedOps = 61
3565d1603babSmrg} OscMiscOps;
3566d1603babSmrg
3567d522f475Smrgstatic Bool
3568d522f475SmrgGetOldColors(XtermWidget xw)
3569d522f475Smrg{
35709a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3571037a25ddSmrg	int i;
3572037a25ddSmrg
35739a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
35749a64e1c5Smrg	if (xw->work.oldColors == NULL) {
35753367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3576d522f475Smrg	    return (False);
3577d522f475Smrg	}
35789a64e1c5Smrg	xw->work.oldColors->which = 0;
3579d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
35809a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
35819a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3582d522f475Smrg	}
35839a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3584d522f475Smrg    }
3585d522f475Smrg    return (True);
3586d522f475Smrg}
3587d522f475Smrg
3588d522f475Smrgstatic int
3589d4fba8b9SmrgoppositeColor(XtermWidget xw, int n)
3590d522f475Smrg{
3591d4fba8b9Smrg    Boolean reversed = (xw->misc.re_verse);
3592d4fba8b9Smrg
3593d522f475Smrg    switch (n) {
3594d522f475Smrg    case TEXT_FG:
3595d4fba8b9Smrg	n = reversed ? TEXT_FG : TEXT_BG;
3596d522f475Smrg	break;
3597d522f475Smrg    case TEXT_BG:
3598d4fba8b9Smrg	n = reversed ? TEXT_BG : TEXT_FG;
3599d522f475Smrg	break;
3600d522f475Smrg    case MOUSE_FG:
3601d522f475Smrg	n = MOUSE_BG;
3602d522f475Smrg	break;
3603d522f475Smrg    case MOUSE_BG:
3604d522f475Smrg	n = MOUSE_FG;
3605d522f475Smrg	break;
3606d522f475Smrg#if OPT_TEK4014
3607d522f475Smrg    case TEK_FG:
3608d4fba8b9Smrg	n = reversed ? TEK_FG : TEK_BG;
3609d522f475Smrg	break;
3610d522f475Smrg    case TEK_BG:
3611d4fba8b9Smrg	n = reversed ? TEK_BG : TEK_FG;
3612d522f475Smrg	break;
3613d522f475Smrg#endif
3614d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3615d522f475Smrg    case HIGHLIGHT_FG:
3616d522f475Smrg	n = HIGHLIGHT_BG;
3617d522f475Smrg	break;
3618d522f475Smrg    case HIGHLIGHT_BG:
3619d522f475Smrg	n = HIGHLIGHT_FG;
3620d522f475Smrg	break;
3621d522f475Smrg#endif
3622d522f475Smrg    default:
3623d522f475Smrg	break;
3624d522f475Smrg    }
3625d522f475Smrg    return n;
3626d522f475Smrg}
3627d522f475Smrg
3628d4fba8b9Smrgstatic Bool
3629d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3630d522f475Smrg{
3631d4fba8b9Smrg    Bool result = False;
3632d4fba8b9Smrg
3633cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3634cd3331d0Smrg	XColor color;
3635cd3331d0Smrg	char buffer[80];
3636d522f475Smrg
3637cd3331d0Smrg	/*
3638cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3639cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3640cd3331d0Smrg	 * reporting the opposite color which would be used.
3641cd3331d0Smrg	 */
3642d4fba8b9Smrg	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3643cd3331d0Smrg
3644cd3331d0Smrg	GetOldColors(xw);
36459a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3646c48a5815Smrg	(void) QueryOneColor(xw, &color);
3647cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3648cd3331d0Smrg		color.red,
3649cd3331d0Smrg		color.green,
3650cd3331d0Smrg		color.blue);
3651712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
36529a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3653cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3654cd3331d0Smrg	unparseputs(xw, buffer);
3655cd3331d0Smrg	unparseputc1(xw, final);
3656d4fba8b9Smrg	result = True;
3657cd3331d0Smrg    }
3658d4fba8b9Smrg    return result;
3659d522f475Smrg}
3660d522f475Smrg
3661d522f475Smrgstatic Bool
3662d4fba8b9SmrgUpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3663d522f475Smrg{
3664d522f475Smrg    int i;
3665d522f475Smrg
3666d522f475Smrg    /* if we were going to free old colors, this would be the place to
3667d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3668d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3669d522f475Smrg     * we could save some overhead this way.   The only case in which this
3670d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3671d522f475Smrg     * which case they can restart xterm
3672d522f475Smrg     */
3673d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3674d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
36759a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
36769a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
36779a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3678d522f475Smrg	    }
3679d522f475Smrg	    if (pNew->names[i]) {
36809a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3681d522f475Smrg	    }
36829a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3683d522f475Smrg	}
3684d522f475Smrg    }
3685d522f475Smrg    return (True);
3686d522f475Smrg}
3687d522f475Smrg
3688d522f475Smrg/*
3689d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3690d522f475Smrg * xterm is compiled.
3691d522f475Smrg */
3692d522f475Smrgstatic int
3693d522f475SmrgOscToColorIndex(OscTextColors mode)
3694d522f475Smrg{
3695d522f475Smrg    int result = 0;
3696d522f475Smrg
3697d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3698d522f475Smrg    switch (mode) {
3699d522f475Smrg	CASE(TEXT_FG);
3700d522f475Smrg	CASE(TEXT_BG);
3701d522f475Smrg	CASE(TEXT_CURSOR);
3702d522f475Smrg	CASE(MOUSE_FG);
3703d522f475Smrg	CASE(MOUSE_BG);
3704d522f475Smrg#if OPT_TEK4014
3705d522f475Smrg	CASE(TEK_FG);
3706d522f475Smrg	CASE(TEK_BG);
3707d522f475Smrg#endif
3708d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3709d522f475Smrg	CASE(HIGHLIGHT_BG);
3710d522f475Smrg	CASE(HIGHLIGHT_FG);
3711d522f475Smrg#endif
3712d522f475Smrg#if OPT_TEK4014
3713d522f475Smrg	CASE(TEK_CURSOR);
3714d522f475Smrg#endif
3715d522f475Smrg    case OSC_NCOLORS:
3716d522f475Smrg	break;
3717d522f475Smrg    }
3718d1603babSmrg#undef CASE
3719d522f475Smrg    return result;
3720d522f475Smrg}
3721d522f475Smrg
3722d522f475Smrgstatic Bool
3723d522f475SmrgChangeColorsRequest(XtermWidget xw,
3724d522f475Smrg		    int start,
3725d522f475Smrg		    char *names,
3726d522f475Smrg		    int final)
3727d522f475Smrg{
3728d522f475Smrg    Bool result = False;
3729d522f475Smrg    ScrnColors newColors;
3730d522f475Smrg
3731d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3732d522f475Smrg
3733d522f475Smrg    if (GetOldColors(xw)) {
3734037a25ddSmrg	int i;
3735d4fba8b9Smrg	int queried = 0;
3736037a25ddSmrg
3737d522f475Smrg	newColors.which = 0;
3738d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3739d522f475Smrg	    newColors.names[i] = NULL;
3740d522f475Smrg	}
3741d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3742037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3743d522f475Smrg	    if (xw->misc.re_verse)
3744d4fba8b9Smrg		ndx = oppositeColor(xw, ndx);
3745d522f475Smrg
3746cd3331d0Smrg	    if (IsEmpty(names)) {
3747d522f475Smrg		newColors.names[ndx] = NULL;
3748d522f475Smrg	    } else {
3749037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3750037a25ddSmrg
3751d522f475Smrg		names = strchr(names, ';');
3752d522f475Smrg		if (names != NULL) {
3753d522f475Smrg		    *names++ = '\0';
3754d522f475Smrg		}
3755fa3f02f3Smrg		if (thisName != 0) {
3756fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3757d4fba8b9Smrg			if (ReportColorRequest(xw, ndx, final))
3758d4fba8b9Smrg			    ++queried;
37599a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
37609a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3761fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3762fa3f02f3Smrg		    }
3763d522f475Smrg		}
3764d522f475Smrg	    }
3765d522f475Smrg	}
3766d522f475Smrg
3767d522f475Smrg	if (newColors.which != 0) {
3768d522f475Smrg	    ChangeColors(xw, &newColors);
3769d522f475Smrg	    UpdateOldColors(xw, &newColors);
3770d4fba8b9Smrg	} else if (queried) {
3771d4fba8b9Smrg	    unparse_end(xw);
3772d522f475Smrg	}
3773d522f475Smrg	result = True;
3774d522f475Smrg    }
3775d522f475Smrg    return result;
3776d522f475Smrg}
3777d522f475Smrg
3778cd3331d0Smrgstatic Bool
3779cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3780cd3331d0Smrg		   int code)
3781cd3331d0Smrg{
3782cd3331d0Smrg    Bool result = False;
3783cd3331d0Smrg
3784dfb07bc7Smrg    (void) xw;
3785dfb07bc7Smrg    (void) code;
3786dfb07bc7Smrg
3787cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3788cd3331d0Smrg    if (GetOldColors(xw)) {
3789037a25ddSmrg	ScrnColors newColors;
3790037a25ddSmrg	const char *thisName;
3791037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3792037a25ddSmrg
3793cd3331d0Smrg	if (xw->misc.re_verse)
3794d4fba8b9Smrg	    ndx = oppositeColor(xw, ndx);
3795cd3331d0Smrg
3796cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3797cd3331d0Smrg
3798cd3331d0Smrg	newColors.which = 0;
3799cd3331d0Smrg	newColors.names[ndx] = NULL;
3800cd3331d0Smrg
3801cd3331d0Smrg	if (thisName != 0
38029a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
38039a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3804cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3805cd3331d0Smrg
3806cd3331d0Smrg	    if (newColors.which != 0) {
3807cd3331d0Smrg		ChangeColors(xw, &newColors);
3808cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3809cd3331d0Smrg	    }
3810cd3331d0Smrg	}
3811cd3331d0Smrg	result = True;
3812cd3331d0Smrg    }
3813cd3331d0Smrg    return result;
3814cd3331d0Smrg}
3815cd3331d0Smrg
3816cd3331d0Smrg#if OPT_SHIFT_FONTS
3817cd3331d0Smrg/*
3818cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3819cd3331d0Smrg *
3820cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3821cd3331d0Smrg * the corresponding menu font entry.
3822cd3331d0Smrg */
3823cd3331d0Smrgstatic int
3824fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3825cd3331d0Smrg{
3826cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3827cd3331d0Smrg    int num = screen->menu_font_number;
3828cd3331d0Smrg    int rel = 0;
3829cd3331d0Smrg
3830cd3331d0Smrg    if (*++source == '+') {
3831cd3331d0Smrg	rel = 1;
3832cd3331d0Smrg	source++;
3833cd3331d0Smrg    } else if (*source == '-') {
3834cd3331d0Smrg	rel = -1;
3835cd3331d0Smrg	source++;
3836cd3331d0Smrg    }
3837cd3331d0Smrg
3838cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3839cd3331d0Smrg	int val = atoi(source);
3840cd3331d0Smrg	if (rel > 0)
3841cd3331d0Smrg	    rel = val;
3842cd3331d0Smrg	else if (rel < 0)
3843cd3331d0Smrg	    rel = -val;
3844cd3331d0Smrg	else
3845cd3331d0Smrg	    num = val;
3846cd3331d0Smrg    }
3847cd3331d0Smrg
3848cd3331d0Smrg    if (rel != 0) {
3849cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3850cd3331d0Smrg				     screen->menu_font_number, rel);
3851cd3331d0Smrg
3852cd3331d0Smrg    }
3853cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3854cd3331d0Smrg    *target = source;
3855cd3331d0Smrg    return num;
3856cd3331d0Smrg}
3857cd3331d0Smrg
3858cd3331d0Smrgstatic void
3859cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3860cd3331d0Smrg{
3861cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3862cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3863cd3331d0Smrg	Bool success = True;
3864cd3331d0Smrg	int num;
3865cb4a1343Smrg	String base = buf + 1;
3866cd3331d0Smrg	const char *name = 0;
3867cd3331d0Smrg
3868cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3869cd3331d0Smrg	if (num < 0
3870cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3871cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3872cd3331d0Smrg	    success = False;
3873cd3331d0Smrg	} else {
3874cd3331d0Smrg#if OPT_RENDERFONT
3875cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3876cd3331d0Smrg		name = getFaceName(xw, False);
3877cd3331d0Smrg	    } else
3878cd3331d0Smrg#endif
3879cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3880cd3331d0Smrg		success = False;
3881cd3331d0Smrg	    }
3882cd3331d0Smrg	}
3883cd3331d0Smrg
3884cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3885cd3331d0Smrg	unparseputs(xw, "50");
3886cd3331d0Smrg
3887cd3331d0Smrg	if (success) {
3888cd3331d0Smrg	    unparseputc(xw, ';');
3889cd3331d0Smrg	    if (buf >= base) {
3890cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3891cd3331d0Smrg		if (*buf != '\0') {
3892037a25ddSmrg		    char temp[10];
3893037a25ddSmrg
3894cd3331d0Smrg		    unparseputc(xw, '#');
3895cd3331d0Smrg		    sprintf(temp, "%d", num);
3896cd3331d0Smrg		    unparseputs(xw, temp);
3897cd3331d0Smrg		    if (*name != '\0')
3898cd3331d0Smrg			unparseputc(xw, ' ');
3899cd3331d0Smrg		}
3900cd3331d0Smrg	    }
3901cd3331d0Smrg	    unparseputs(xw, name);
3902cd3331d0Smrg	}
3903cd3331d0Smrg
3904cd3331d0Smrg	unparseputc1(xw, final);
3905cd3331d0Smrg	unparse_end(xw);
3906cd3331d0Smrg    }
3907cd3331d0Smrg}
3908cd3331d0Smrg
3909cd3331d0Smrgstatic void
3910cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3911cd3331d0Smrg{
3912cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3913cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3914cd3331d0Smrg	Bool success = True;
3915cd3331d0Smrg	int num;
3916cd3331d0Smrg	VTFontNames fonts;
3917cd3331d0Smrg	char *name;
3918cd3331d0Smrg
3919cd3331d0Smrg	/*
3920cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3921cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3922cd3331d0Smrg	 *
3923cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3924cd3331d0Smrg	 * to load the font entry.
3925cd3331d0Smrg	 */
3926cd3331d0Smrg	if (*buf == '#') {
3927cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3928cd3331d0Smrg
3929cd3331d0Smrg	    if (num < 0
3930cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3931cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3932cd3331d0Smrg		success = False;
3933cd3331d0Smrg	    } else {
3934cd3331d0Smrg		/*
3935cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3936cd3331d0Smrg		 * for a font specification within the control.
3937cd3331d0Smrg		 */
3938cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3939cd3331d0Smrg		    ++buf;
3940cd3331d0Smrg		}
3941cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3942cd3331d0Smrg		    ++buf;
3943cd3331d0Smrg		}
3944cd3331d0Smrg#if OPT_RENDERFONT
3945cd3331d0Smrg		if (UsingRenderFont(xw)) {
3946c219fbebSmrg		    /* EMPTY */
3947c219fbebSmrg		    /* there is only one font entry to load */
3948c219fbebSmrg		    ;
3949cd3331d0Smrg		} else
3950cd3331d0Smrg#endif
3951cd3331d0Smrg		{
3952cd3331d0Smrg		    /*
3953cd3331d0Smrg		     * Normally there is no font specified in the control.
3954cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3955cd3331d0Smrg		     */
3956cd3331d0Smrg		    if (*buf == '\0') {
3957cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3958cd3331d0Smrg			    success = False;
3959cd3331d0Smrg			}
3960cd3331d0Smrg		    }
3961cd3331d0Smrg		}
3962cd3331d0Smrg	    }
3963cd3331d0Smrg	} else {
3964cd3331d0Smrg	    num = screen->menu_font_number;
3965cd3331d0Smrg	}
3966cd3331d0Smrg	name = x_strtrim(buf);
396794644356Smrg	if (screen->EscapeFontName()) {
396894644356Smrg	    FREE_STRING(screen->EscapeFontName());
396994644356Smrg	    screen->EscapeFontName() = 0;
397094644356Smrg	}
3971cd3331d0Smrg	if (success && !IsEmpty(name)) {
3972cd3331d0Smrg#if OPT_RENDERFONT
3973cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3974cd3331d0Smrg		setFaceName(xw, name);
3975cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3976cd3331d0Smrg	    } else
3977cd3331d0Smrg#endif
3978cd3331d0Smrg	    {
3979cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3980cd3331d0Smrg		fonts.f_n = name;
3981d1603babSmrg		if (SetVTFont(xw, num, True, &fonts)
3982d1603babSmrg		    && num == screen->menu_font_number
3983d1603babSmrg		    && num != fontMenu_fontescape) {
398494644356Smrg		    screen->EscapeFontName() = x_strdup(name);
398594644356Smrg		}
3986cd3331d0Smrg	    }
3987cd3331d0Smrg	} else {
3988cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3989cd3331d0Smrg	}
399094644356Smrg	update_font_escape();
3991cd3331d0Smrg	free(name);
3992cd3331d0Smrg    }
3993cd3331d0Smrg}
3994cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3995cd3331d0Smrg
3996d522f475Smrg/***====================================================================***/
3997d522f475Smrg
3998d1603babSmrgstatic void
3999d1603babSmrgreport_allowed_ops(XtermWidget xw, int final)
4000d1603babSmrg{
4001d1603babSmrg    TScreen *screen = TScreenOf(xw);
4002d1603babSmrg    char delimiter = ';';
4003d1603babSmrg
4004d1603babSmrg    unparseputc1(xw, ANSI_OSC);
4005d1603babSmrg    unparseputn(xw, OSC_AllowedOps);
4006d1603babSmrg
4007d1603babSmrg#define CASE(name) \
4008d1603babSmrg    if (screen->name) { \
4009d1603babSmrg	unparseputc(xw, delimiter); \
4010d1603babSmrg	unparseputs(xw, #name); \
4011d1603babSmrg	delimiter = ','; \
4012d1603babSmrg    }
4013d1603babSmrg    CASE(allowColorOps);
4014d1603babSmrg    CASE(allowFontOps);
4015d1603babSmrg    CASE(allowMouseOps);
4016d1603babSmrg    CASE(allowPasteControls);
4017d1603babSmrg    CASE(allowTcapOps);
4018d1603babSmrg    CASE(allowTitleOps);
4019d1603babSmrg    CASE(allowWindowOps);
4020d1603babSmrg#undef CASE
4021d1603babSmrg
4022d1603babSmrg    unparseputc1(xw, final);
4023d1603babSmrg}
4024d1603babSmrg
4025d1603babSmrgstatic void
4026d1603babSmrgreport_disallowed_ops(XtermWidget xw, char *value, int final)
4027d1603babSmrg{
4028d1603babSmrg    unparseputc1(xw, ANSI_OSC);
4029d1603babSmrg    unparseputn(xw, OSC_DisallowedOps);
4030d1603babSmrg    unparse_disallowed_ops(xw, value);
4031d1603babSmrg    unparseputc1(xw, final);
4032d1603babSmrg}
4033d1603babSmrg
4034d1603babSmrg/***====================================================================***/
4035d1603babSmrg
4036d522f475Smrgvoid
4037fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
4038d522f475Smrg{
4039cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4040d522f475Smrg    int mode;
4041d522f475Smrg    Char *cp;
4042d522f475Smrg    int state = 0;
4043d522f475Smrg    char *buf = 0;
404450027b5bSmrg    char temp[20];
4045cd3331d0Smrg#if OPT_ISO_COLORS
4046cd3331d0Smrg    int ansi_colors = 0;
4047cd3331d0Smrg#endif
4048cd3331d0Smrg    Bool need_data = True;
4049fa3f02f3Smrg    Bool optional_data = False;
4050d522f475Smrg
4051d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
4052d522f475Smrg
4053712a7ff4Smrg    (void) screen;
4054712a7ff4Smrg
4055d522f475Smrg    /*
4056d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
4057d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
4058d522f475Smrg     * with the same final character as the application sends to make this
4059d522f475Smrg     * work better with shell scripts, which may have trouble reading an
4060d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
4061d522f475Smrg     */
4062d522f475Smrg    mode = 0;
4063d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
4064d522f475Smrg	switch (state) {
4065d522f475Smrg	case 0:
4066d522f475Smrg	    if (isdigit(*cp)) {
4067d522f475Smrg		mode = 10 * mode + (*cp - '0');
4068d522f475Smrg		if (mode > 65535) {
4069d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
4070d522f475Smrg		    return;
4071d522f475Smrg		}
4072d522f475Smrg		break;
4073d4fba8b9Smrg	    } else {
4074d4fba8b9Smrg		switch (*cp) {
4075d4fba8b9Smrg		case 'I':
4076d4fba8b9Smrg		    xtermLoadIcon(xw, (char *) ++cp);
4077d4fba8b9Smrg		    return;
4078d4fba8b9Smrg		case 'l':
4079d4fba8b9Smrg		    ChangeTitle(xw, (char *) ++cp);
4080d4fba8b9Smrg		    return;
4081d4fba8b9Smrg		case 'L':
4082d4fba8b9Smrg		    ChangeIconName(xw, (char *) ++cp);
4083d4fba8b9Smrg		    return;
4084d4fba8b9Smrg		}
4085d522f475Smrg	    }
4086d522f475Smrg	    /* FALLTHRU */
4087d522f475Smrg	case 1:
4088d522f475Smrg	    if (*cp != ';') {
4089d1603babSmrg		TRACE(("do_osc did not find semicolon offset %lu\n",
4090d1603babSmrg		       (unsigned long) (cp - oscbuf)));
4091d522f475Smrg		return;
4092d522f475Smrg	    }
4093d522f475Smrg	    state = 2;
4094d522f475Smrg	    break;
4095d522f475Smrg	case 2:
4096d522f475Smrg	    buf = (char *) cp;
4097d522f475Smrg	    state = 3;
4098d522f475Smrg	    /* FALLTHRU */
4099d522f475Smrg	default:
4100cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4101d522f475Smrg		switch (mode) {
4102d522f475Smrg		case 0:
4103d522f475Smrg		case 1:
4104d522f475Smrg		case 2:
4105d522f475Smrg		    break;
4106d522f475Smrg		default:
4107d1603babSmrg		    TRACE(("do_osc found nonprinting char %02X offset %lu\n",
4108d522f475Smrg			   CharOf(*cp),
4109d1603babSmrg			   (unsigned long) (cp - oscbuf)));
4110d522f475Smrg		    return;
4111d522f475Smrg		}
4112d522f475Smrg	    }
4113d522f475Smrg	}
4114d522f475Smrg    }
4115cd3331d0Smrg
41163367019cSmrg    /*
41173367019cSmrg     * Check if the palette changed and there are no more immediate changes
41183367019cSmrg     * that could be deferred to the next repaint.
41193367019cSmrg     */
4120dfb07bc7Smrg    if (xw->work.palette_changed) {
41213367019cSmrg	switch (mode) {
4122d1603babSmrg	case OSC_AllowedOps:
4123d1603babSmrg	case OSC_DisallowedOps:
4124d1603babSmrg	case OSC_FontOps:
4125d1603babSmrg	case OSC_NewLogFile:
4126d1603babSmrg	case OSC_SelectionData:
4127d1603babSmrg	case OSC_Unused_30:
4128d1603babSmrg	case OSC_Unused_31:
4129d1603babSmrg	case OSC_Unused_51:
4130d1603babSmrg	case OSC_X_Property:
41313367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
4132dfb07bc7Smrg	    xw->work.palette_changed = False;
41333367019cSmrg	    xtermRepaint(xw);
41343367019cSmrg	    break;
4135d4fba8b9Smrg	default:
4136d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
4137d4fba8b9Smrg	    break;
41383367019cSmrg	}
41393367019cSmrg    }
41403367019cSmrg
4141cd3331d0Smrg    /*
4142cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
4143cd3331d0Smrg     * a special case.
4144cd3331d0Smrg     */
4145cd3331d0Smrg    switch (mode) {
4146d1603babSmrg    case OSC_FontOps:
4147cd3331d0Smrg#if OPT_ISO_COLORS
4148d1603babSmrg    case OSC_Reset(OSC_SetAnsiColor):
4149d1603babSmrg    case OSC_Reset(OSC_GetAnsiColors):
4150fa3f02f3Smrg	need_data = False;
4151fa3f02f3Smrg	optional_data = True;
4152fa3f02f3Smrg	break;
4153cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4154cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4155cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4156cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4157cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4158cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4159cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4160cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4161cd3331d0Smrg#endif
4162cd3331d0Smrg#if OPT_TEK4014
4163cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4164cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4165cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4166cd3331d0Smrg#endif
4167d1603babSmrg    case OSC_AllowedOps:
4168cd3331d0Smrg	need_data = False;
4169cd3331d0Smrg	break;
4170cd3331d0Smrg#endif
4171cd3331d0Smrg    default:
4172cd3331d0Smrg	break;
4173cd3331d0Smrg    }
4174cd3331d0Smrg
4175cd3331d0Smrg    /*
4176cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
4177cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
4178cd3331d0Smrg     */
4179cd3331d0Smrg    if (IsEmpty(buf)) {
4180cd3331d0Smrg	if (need_data) {
418150027b5bSmrg	    switch (mode) {
418250027b5bSmrg	    case 0:
418350027b5bSmrg	    case 1:
418450027b5bSmrg	    case 2:
418550027b5bSmrg		buf = strcpy(temp, "xterm");
418650027b5bSmrg		break;
418750027b5bSmrg	    default:
418850027b5bSmrg		TRACE(("do_osc found no data\n"));
418950027b5bSmrg		return;
419050027b5bSmrg	    }
419150027b5bSmrg	} else {
419250027b5bSmrg	    temp[0] = '\0';
419350027b5bSmrg	    buf = temp;
4194cd3331d0Smrg	}
4195fa3f02f3Smrg    } else if (!need_data && !optional_data) {
4196fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
4197d522f475Smrg	return;
41980d92cbfdSchristos    }
4199d522f475Smrg
4200d522f475Smrg    switch (mode) {
4201d1603babSmrg    case OSC_IconBoth:		/* new icon name and title */
4202b7c89284Ssnj	ChangeIconName(xw, buf);
4203b7c89284Ssnj	ChangeTitle(xw, buf);
4204d522f475Smrg	break;
4205d522f475Smrg
4206d1603babSmrg    case OSC_IconOnly:		/* new icon name only */
4207b7c89284Ssnj	ChangeIconName(xw, buf);
4208d522f475Smrg	break;
4209d522f475Smrg
4210d1603babSmrg    case OSC_TitleOnly:	/* new title only */
4211b7c89284Ssnj	ChangeTitle(xw, buf);
4212d522f475Smrg	break;
4213d522f475Smrg
421422d8e007Schristos#ifdef notdef
4215d1603babSmrg    case OSC_X_Property:	/* change X property */
4216cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
42170d92cbfdSchristos	    ChangeXprop(buf);
4218d522f475Smrg	break;
421922d8e007Schristos#endif
4220d522f475Smrg#if OPT_ISO_COLORS
4221d1603babSmrg    case OSC_GetAnsiColors:
4222cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4223cd3331d0Smrg	/* FALLTHRU */
4224d1603babSmrg    case OSC_SetAnsiColor:
4225d4fba8b9Smrg	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4226dfb07bc7Smrg	    xw->work.palette_changed = True;
4227cd3331d0Smrg	break;
4228d1603babSmrg    case OSC_ColorMode:
422994644356Smrg	/* FALLTHRU */
4230d1603babSmrg    case OSC_Reset(OSC_ColorMode):
423194644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
423294644356Smrg	while (*buf != '\0') {
423394644356Smrg	    long which = 0;
423494644356Smrg	    long value = 0;
423594644356Smrg	    char *next;
423694644356Smrg	    if (*buf == ';') {
423794644356Smrg		++buf;
423894644356Smrg	    } else {
423994644356Smrg		which = strtol(buf, &next, 10);
4240037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
424194644356Smrg		    break;
424294644356Smrg		buf = next;
424394644356Smrg		if (*buf == ';')
424494644356Smrg		    ++buf;
424594644356Smrg	    }
424694644356Smrg	    if (*buf == ';') {
424794644356Smrg		++buf;
424894644356Smrg	    } else {
424994644356Smrg		value = strtol(buf, &next, 10);
4250dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
425194644356Smrg		    break;
425294644356Smrg		buf = next;
425394644356Smrg		if (*buf == ';')
425494644356Smrg		    ++buf;
425594644356Smrg	    }
425694644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
425794644356Smrg	    switch (which) {
425894644356Smrg	    case 0:
425994644356Smrg		screen->colorBDMode = (value != 0);
426094644356Smrg		break;
426194644356Smrg	    case 1:
426294644356Smrg		screen->colorULMode = (value != 0);
426394644356Smrg		break;
426494644356Smrg	    case 2:
426594644356Smrg		screen->colorBLMode = (value != 0);
426694644356Smrg		break;
426794644356Smrg	    case 3:
426894644356Smrg		screen->colorRVMode = (value != 0);
426994644356Smrg		break;
427094644356Smrg#if OPT_WIDE_ATTRS
427194644356Smrg	    case 4:
427294644356Smrg		screen->colorITMode = (value != 0);
427394644356Smrg		break;
427494644356Smrg#endif
427594644356Smrg	    default:
427694644356Smrg		TRACE(("...unknown colorXXMode\n"));
427794644356Smrg		break;
427894644356Smrg	    }
427994644356Smrg	}
428094644356Smrg	break;
4281d1603babSmrg    case OSC_Reset(OSC_GetAnsiColors):
4282cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4283cd3331d0Smrg	/* FALLTHRU */
4284d1603babSmrg    case OSC_Reset(OSC_SetAnsiColor):
4285cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4286dfb07bc7Smrg	    xw->work.palette_changed = True;
4287d522f475Smrg	break;
4288d522f475Smrg#endif
4289d522f475Smrg    case OSC_TEXT_FG:
4290d522f475Smrg    case OSC_TEXT_BG:
4291d522f475Smrg    case OSC_TEXT_CURSOR:
4292d522f475Smrg    case OSC_MOUSE_FG:
4293d522f475Smrg    case OSC_MOUSE_BG:
4294d522f475Smrg#if OPT_HIGHLIGHT_COLOR
4295d522f475Smrg    case OSC_HIGHLIGHT_BG:
4296cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
4297d522f475Smrg#endif
4298d522f475Smrg#if OPT_TEK4014
4299d522f475Smrg    case OSC_TEK_FG:
4300d522f475Smrg    case OSC_TEK_BG:
4301d522f475Smrg    case OSC_TEK_CURSOR:
4302d522f475Smrg#endif
4303cd3331d0Smrg	if (xw->misc.dynamicColors) {
4304d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
4305cd3331d0Smrg	}
4306cd3331d0Smrg	break;
4307cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4308cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4309cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4310cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4311cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4312cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4313cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4314cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4315cd3331d0Smrg#endif
4316cd3331d0Smrg#if OPT_TEK4014
4317cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4318cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4319cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4320cd3331d0Smrg#endif
4321cd3331d0Smrg	if (xw->misc.dynamicColors) {
4322cd3331d0Smrg	    ResetColorsRequest(xw, mode);
4323cd3331d0Smrg	}
4324d522f475Smrg	break;
4325d522f475Smrg
4326d1603babSmrg    case OSC_SetupPointer:
43278f44fb3bSmrg	xtermSetupPointer(xw, buf);
43288f44fb3bSmrg	break;
43298f44fb3bSmrg
4330d522f475Smrg#ifdef ALLOWLOGGING
4331d1603babSmrg    case OSC_NewLogFile:
4332d522f475Smrg#ifdef ALLOWLOGFILECHANGES
4333d522f475Smrg	/*
4334d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
4335d522f475Smrg	 * arbitrary files accessible to the person running xterm.
4336d522f475Smrg	 */
4337037a25ddSmrg	if (strcmp(buf, "?")) {
4338037a25ddSmrg	    char *bp;
4339dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
4340d4fba8b9Smrg		free(screen->logfile);
4341037a25ddSmrg		screen->logfile = bp;
4342037a25ddSmrg		break;
4343037a25ddSmrg	    }
4344d522f475Smrg	}
4345d522f475Smrg#endif
4346cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4347cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4348d522f475Smrg	break;
4349d522f475Smrg#endif /* ALLOWLOGGING */
4350d522f475Smrg
4351d1603babSmrg    case OSC_FontOps:
4352d522f475Smrg#if OPT_SHIFT_FONTS
4353cd3331d0Smrg	if (*buf == '?') {
4354cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
4355cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
4356cd3331d0Smrg	    ChangeFontRequest(xw, buf);
4357d522f475Smrg	}
4358d522f475Smrg#endif /* OPT_SHIFT_FONTS */
4359d522f475Smrg	break;
4360d522f475Smrg
4361d522f475Smrg#if OPT_PASTE64
4362d1603babSmrg    case OSC_SelectionData:
4363cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
4364d522f475Smrg	break;
4365d522f475Smrg#endif
4366d1603babSmrg
4367d1603babSmrg    case OSC_AllowedOps:	/* XTQALLOWED */
4368d1603babSmrg	report_allowed_ops(xw, final);
4369d1603babSmrg	break;
4370d1603babSmrg
4371d1603babSmrg    case OSC_DisallowedOps:	/* XTQDISALLOWED */
4372d1603babSmrg	report_disallowed_ops(xw, buf, final);
4373d1603babSmrg	break;
4374d1603babSmrg
4375d1603babSmrg    case OSC_Unused_30:
4376d1603babSmrg    case OSC_Unused_31:
4377d1603babSmrg    case OSC_Unused_51:
4378cd3331d0Smrg    default:
4379cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
4380cd3331d0Smrg	break;
4381d522f475Smrg    }
4382d522f475Smrg    unparse_end(xw);
4383d522f475Smrg}
4384d522f475Smrg
4385d522f475Smrg/*
4386d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
4387d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
4388d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4389d522f475Smrg * "real" terminals accept commas in the string definitions).
4390d522f475Smrg */
4391d522f475Smrgstatic int
4392cd3331d0Smrgudk_value(const char **cp)
4393d522f475Smrg{
4394cd3331d0Smrg    int result = -1;
4395d522f475Smrg
4396d522f475Smrg    for (;;) {
4397037a25ddSmrg	int c;
4398037a25ddSmrg
4399d522f475Smrg	if ((c = **cp) != '\0')
4400d522f475Smrg	    *cp = *cp + 1;
4401d522f475Smrg	if (c == ';' || c == '\0')
4402cd3331d0Smrg	    break;
4403cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
4404cd3331d0Smrg	    break;
4405d522f475Smrg    }
4406cd3331d0Smrg
4407cd3331d0Smrg    return result;
4408d522f475Smrg}
4409d522f475Smrg
4410d522f475Smrgvoid
44119a64e1c5Smrgreset_decudk(XtermWidget xw)
4412d522f475Smrg{
4413d522f475Smrg    int n;
4414d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
4415d4fba8b9Smrg	FreeAndNull(xw->work.user_keys[n].str);
4416d4fba8b9Smrg	xw->work.user_keys[n].len = 0;
4417d522f475Smrg    }
4418d522f475Smrg}
4419d522f475Smrg
4420d522f475Smrg/*
4421d522f475Smrg * Parse the data for DECUDK (user-defined keys).
4422d522f475Smrg */
4423d522f475Smrgstatic void
44249a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
4425d522f475Smrg{
4426d522f475Smrg    while (*cp) {
4427cd3331d0Smrg	const char *base = cp;
4428d4fba8b9Smrg	char *str = malloc(strlen(cp) + 3);
4429d522f475Smrg	unsigned key = 0;
4430d522f475Smrg	int len = 0;
4431d522f475Smrg
443294644356Smrg	if (str == NULL)
443394644356Smrg	    break;
443494644356Smrg
4435d522f475Smrg	while (isdigit(CharOf(*cp)))
44360d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
4437037a25ddSmrg
4438d522f475Smrg	if (*cp == '/') {
4439037a25ddSmrg	    int lo, hi;
4440037a25ddSmrg
4441d522f475Smrg	    cp++;
4442d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
4443d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
44440d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
4445d522f475Smrg	    }
4446d522f475Smrg	}
4447d522f475Smrg	if (len > 0 && key < MAX_UDK) {
44483367019cSmrg	    str[len] = '\0';
4449d4fba8b9Smrg	    free(xw->work.user_keys[key].str);
44509a64e1c5Smrg	    xw->work.user_keys[key].str = str;
44519a64e1c5Smrg	    xw->work.user_keys[key].len = len;
4452d4fba8b9Smrg	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4453d522f475Smrg	} else {
4454d522f475Smrg	    free(str);
4455d522f475Smrg	}
4456d522f475Smrg	if (*cp == ';')
4457d522f475Smrg	    cp++;
4458d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
4459d522f475Smrg	    break;
4460d522f475Smrg    }
4461d522f475Smrg}
4462d522f475Smrg
4463fa3f02f3Smrg/*
4464fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
4465fa3f02f3Smrg * interspersing with control characters, but have the string already.
4466fa3f02f3Smrg */
4467fa3f02f3Smrgstatic void
4468fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
4469fa3f02f3Smrg{
4470fa3f02f3Smrg    const char *cp = *string;
4471fa3f02f3Smrg    ParmType nparam = 0;
4472fa3f02f3Smrg    int last_empty = 1;
4473fa3f02f3Smrg
4474fa3f02f3Smrg    memset(params, 0, sizeof(*params));
4475fa3f02f3Smrg    while (*cp != '\0') {
4476fa3f02f3Smrg	Char ch = CharOf(*cp++);
4477fa3f02f3Smrg
4478fa3f02f3Smrg	if (isdigit(ch)) {
4479fa3f02f3Smrg	    last_empty = 0;
4480fa3f02f3Smrg	    if (nparam < NPARAM) {
4481fa3f02f3Smrg		params->a_param[nparam] =
4482fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
4483fa3f02f3Smrg				+ (ch - '0'));
4484fa3f02f3Smrg	    }
4485fa3f02f3Smrg	} else if (ch == ';') {
4486fa3f02f3Smrg	    last_empty = 1;
4487fa3f02f3Smrg	    nparam++;
4488fa3f02f3Smrg	} else if (ch < 32) {
4489fa3f02f3Smrg	    /* EMPTY */ ;
4490fa3f02f3Smrg	} else {
4491fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
4492fa3f02f3Smrg	    params->a_final = ch;
4493fa3f02f3Smrg	    break;
4494fa3f02f3Smrg	}
4495fa3f02f3Smrg    }
4496fa3f02f3Smrg
4497fa3f02f3Smrg    *string = cp;
4498fa3f02f3Smrg    if (!last_empty)
4499fa3f02f3Smrg	nparam++;
4500fa3f02f3Smrg    if (nparam > NPARAM)
4501fa3f02f3Smrg	params->a_nparam = NPARAM;
4502fa3f02f3Smrg    else
4503fa3f02f3Smrg	params->a_nparam = nparam;
4504fa3f02f3Smrg}
4505fa3f02f3Smrg
4506d522f475Smrg#if OPT_TRACE
4507d522f475Smrg#define SOFT_WIDE 10
4508d522f475Smrg#define SOFT_HIGH 20
4509d522f475Smrg
4510d522f475Smrgstatic void
4511fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
4512d522f475Smrg{
4513d522f475Smrg    char DscsName[8];
4514d522f475Smrg    int len;
4515d522f475Smrg    int Pfn = params->a_param[0];
4516d522f475Smrg    int Pcn = params->a_param[1];
4517d522f475Smrg    int Pe = params->a_param[2];
4518d522f475Smrg    int Pcmw = params->a_param[3];
4519d522f475Smrg    int Pw = params->a_param[4];
4520d522f475Smrg    int Pt = params->a_param[5];
4521d522f475Smrg    int Pcmh = params->a_param[6];
4522d522f475Smrg    int Pcss = params->a_param[7];
4523d522f475Smrg
4524d522f475Smrg    int start_char = Pcn + 0x20;
4525d522f475Smrg    int char_wide = ((Pcmw == 0)
4526d522f475Smrg		     ? (Pcss ? 6 : 10)
4527d522f475Smrg		     : (Pcmw > 4
4528d522f475Smrg			? Pcmw
4529d522f475Smrg			: (Pcmw + 3)));
4530d522f475Smrg    int char_high = ((Pcmh == 0)
45313367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
4532d522f475Smrg			? 10
4533d522f475Smrg			: 20)
4534d522f475Smrg		     : Pcmh);
4535d522f475Smrg    Char ch;
4536d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
4537d522f475Smrg    Bool first = True;
4538d522f475Smrg    Bool prior = False;
4539d522f475Smrg    int row = 0, col = 0;
4540d522f475Smrg
4541d522f475Smrg    TRACE(("Parsing DECDLD\n"));
4542d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
4543d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
4544d522f475Smrg    TRACE(("  erase control %d\n", Pe));
4545d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
4546d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
4547d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
4548d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
4549d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
4550d522f475Smrg
4551d522f475Smrg    if (Pfn > 1
4552d522f475Smrg	|| Pcn > 95
4553d522f475Smrg	|| Pe > 2
4554d522f475Smrg	|| Pcmw > 10
4555d522f475Smrg	|| Pcmw == 1
4556d522f475Smrg	|| Pt > 2
4557d522f475Smrg	|| Pcmh > 20
4558d522f475Smrg	|| Pcss > 1
4559d522f475Smrg	|| char_wide > SOFT_WIDE
4560d522f475Smrg	|| char_high > SOFT_HIGH) {
4561d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4562d522f475Smrg	return;
4563d522f475Smrg    }
4564d522f475Smrg
4565d522f475Smrg    len = 0;
4566d522f475Smrg    while (*string != '\0') {
4567d522f475Smrg	ch = CharOf(*string++);
4568d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4569d522f475Smrg	    if (len < 2)
4570b7c89284Ssnj		DscsName[len++] = (char) ch;
4571d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4572b7c89284Ssnj	    DscsName[len++] = (char) ch;
4573d522f475Smrg	    break;
4574d522f475Smrg	}
4575d522f475Smrg    }
4576d522f475Smrg    DscsName[len] = 0;
4577d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4578d522f475Smrg
4579d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4580d522f475Smrg    while (*string != '\0') {
4581d522f475Smrg	if (first) {
4582d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4583d522f475Smrg	    if (prior) {
4584d522f475Smrg		for (row = 0; row < char_high; ++row) {
4585d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4586d522f475Smrg		}
4587d522f475Smrg	    }
4588d522f475Smrg	    prior = False;
4589d522f475Smrg	    first = False;
4590d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4591d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4592d522f475Smrg		    bits[row][col] = '.';
4593d522f475Smrg		}
4594d522f475Smrg	    }
4595d522f475Smrg	    row = col = 0;
4596d522f475Smrg	}
4597d522f475Smrg	ch = CharOf(*string++);
4598d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4599d522f475Smrg	    int n;
4600d522f475Smrg
4601b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4602d522f475Smrg	    for (n = 0; n < 6; ++n) {
4603b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4604d522f475Smrg	    }
4605d522f475Smrg	    col += 1;
4606d522f475Smrg	    prior = True;
4607d522f475Smrg	} else if (ch == '/') {
4608d522f475Smrg	    row += 6;
4609d522f475Smrg	    col = 0;
4610d522f475Smrg	} else if (ch == ';') {
4611d522f475Smrg	    first = True;
4612d522f475Smrg	    ++start_char;
4613d522f475Smrg	}
4614d522f475Smrg    }
4615d522f475Smrg}
4616d522f475Smrg#else
4617d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4618d522f475Smrg#endif
4619d522f475Smrg
4620d4fba8b9Smrg#if OPT_DEC_RECTOPS
4621d4fba8b9Smrgstatic const char *
4622d4fba8b9Smrgskip_params(const char *cp)
4623d4fba8b9Smrg{
4624d4fba8b9Smrg    while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4625d4fba8b9Smrg	++cp;
4626d4fba8b9Smrg    return cp;
4627d4fba8b9Smrg}
4628d4fba8b9Smrg
4629d4fba8b9Smrgstatic int
4630d4fba8b9Smrgparse_int_param(const char **cp)
4631d4fba8b9Smrg{
4632d4fba8b9Smrg    int result = 0;
4633d4fba8b9Smrg    const char *s = *cp;
4634d4fba8b9Smrg    while (*s != '\0') {
4635d4fba8b9Smrg	if (*s == ';') {
4636d4fba8b9Smrg	    ++s;
4637d4fba8b9Smrg	    break;
4638d4fba8b9Smrg	} else if (*s >= '0' && *s <= '9') {
4639d4fba8b9Smrg	    result = (result * 10) + (*s++ - '0');
4640d4fba8b9Smrg	} else {
4641d4fba8b9Smrg	    s += strlen(s);
4642d4fba8b9Smrg	}
4643d4fba8b9Smrg    }
4644d4fba8b9Smrg    TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
4645d4fba8b9Smrg    *cp = s;
4646d4fba8b9Smrg    return result;
4647d4fba8b9Smrg}
4648d4fba8b9Smrg
4649d4fba8b9Smrgstatic int
4650d4fba8b9Smrgparse_chr_param(const char **cp)
4651d4fba8b9Smrg{
4652d4fba8b9Smrg    int result = 0;
4653d4fba8b9Smrg    const char *s = *cp;
4654d4fba8b9Smrg    if (*s != '\0') {
4655d4fba8b9Smrg	if ((result = CharOf(*s++)) != 0) {
4656d4fba8b9Smrg	    if (*s == ';') {
4657d4fba8b9Smrg		++s;
4658d4fba8b9Smrg	    } else if (*s != '\0') {
4659d4fba8b9Smrg		result = 0;
4660d4fba8b9Smrg	    }
4661d4fba8b9Smrg	}
4662d4fba8b9Smrg    }
4663d4fba8b9Smrg    TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
4664d4fba8b9Smrg    *cp = s;
4665d4fba8b9Smrg    return result;
4666d4fba8b9Smrg}
4667d4fba8b9Smrg
4668d4fba8b9Smrgstatic void
4669d4fba8b9Smrgrestore_DECCIR(XtermWidget xw, const char *cp)
4670d4fba8b9Smrg{
4671d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
4672d4fba8b9Smrg    int value;
4673d4fba8b9Smrg
4674d4fba8b9Smrg    /* row */
4675d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4676d4fba8b9Smrg	return;
4677d4fba8b9Smrg    screen->cur_row = (value - 1);
4678d4fba8b9Smrg
4679d4fba8b9Smrg    /* column */
4680d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4681d4fba8b9Smrg	return;
4682d4fba8b9Smrg    screen->cur_col = (value - 1);
4683d4fba8b9Smrg
4684d4fba8b9Smrg    /* page */
4685d4fba8b9Smrg    if (parse_int_param(&cp) != 1)
4686d4fba8b9Smrg	return;
4687d4fba8b9Smrg
4688d4fba8b9Smrg    /* rendition */
4689d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4690d4fba8b9Smrg	return;
4691d4fba8b9Smrg    UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4692d4fba8b9Smrg    xw->flags |= (value & 8) ? INVERSE : 0;
4693d4fba8b9Smrg    xw->flags |= (value & 4) ? BLINK : 0;
4694d4fba8b9Smrg    xw->flags |= (value & 2) ? UNDERLINE : 0;
4695d4fba8b9Smrg    xw->flags |= (value & 1) ? BOLD : 0;
4696d4fba8b9Smrg
4697d4fba8b9Smrg    /* attributes */
4698d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4699d4fba8b9Smrg	return;
4700d4fba8b9Smrg    screen->protected_mode &= ~DEC_PROTECT;
4701d4fba8b9Smrg    screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4702d4fba8b9Smrg
4703d4fba8b9Smrg    /* flags */
4704d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4705d4fba8b9Smrg	return;
4706d4fba8b9Smrg    screen->do_wrap = (value & 8) ? True : False;
4707d4fba8b9Smrg    screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4708d4fba8b9Smrg    UIntClr(xw->flags, ORIGIN);
4709d4fba8b9Smrg    xw->flags |= (value & 1) ? ORIGIN : 0;
4710d4fba8b9Smrg
4711d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4712d4fba8b9Smrg	return;
4713d4fba8b9Smrg    screen->curgl = (Char) value;
4714d4fba8b9Smrg
4715d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4716d4fba8b9Smrg	return;
4717d4fba8b9Smrg    screen->curgr = (Char) value;
4718d4fba8b9Smrg
4719d4fba8b9Smrg    /* character-set size */
4720d4fba8b9Smrg    if (parse_chr_param(&cp) != 0x4f)	/* works for xterm */
4721d4fba8b9Smrg	return;
4722d4fba8b9Smrg
4723d4fba8b9Smrg    /* SCS designators */
4724d4fba8b9Smrg    for (value = 0; value < NUM_GSETS; ++value) {
4725d4fba8b9Smrg	if (*cp == '%') {
4726d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '%', *++cp);
4727d4fba8b9Smrg	} else if (*cp != '\0') {
4728d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4729d4fba8b9Smrg	} else {
4730d4fba8b9Smrg	    return;
4731d4fba8b9Smrg	}
4732d4fba8b9Smrg	cp++;
4733d4fba8b9Smrg    }
4734d4fba8b9Smrg
4735d4fba8b9Smrg    TRACE(("...done DECCIR\n"));
4736d4fba8b9Smrg}
4737d4fba8b9Smrg
4738d4fba8b9Smrgstatic void
4739d4fba8b9Smrgrestore_DECTABSR(XtermWidget xw, const char *cp)
4740d4fba8b9Smrg{
4741d4fba8b9Smrg    int stop = 0;
4742d4fba8b9Smrg    Bool fail = False;
4743d4fba8b9Smrg
4744d4fba8b9Smrg    TabZonk(xw->tabs);
4745d4fba8b9Smrg    while (*cp != '\0' && !fail) {
4746d4fba8b9Smrg	if ((*cp) >= '0' && (*cp) <= '9') {
4747d4fba8b9Smrg	    stop = (stop * 10) + ((*cp) - '0');
4748d4fba8b9Smrg	} else if (*cp == '/') {
4749d4fba8b9Smrg	    --stop;
4750d4fba8b9Smrg	    if (OkTAB(stop)) {
4751d4fba8b9Smrg		TabSet(xw->tabs, stop);
4752d4fba8b9Smrg		stop = 0;
4753d4fba8b9Smrg	    } else {
4754d4fba8b9Smrg		fail = True;
4755d4fba8b9Smrg	    }
4756d4fba8b9Smrg	} else {
4757d4fba8b9Smrg	    fail = True;
4758d4fba8b9Smrg	}
4759d4fba8b9Smrg	++cp;
4760d4fba8b9Smrg    }
4761d4fba8b9Smrg    --stop;
4762d4fba8b9Smrg    if (OkTAB(stop))
4763d4fba8b9Smrg	TabSet(xw->tabs, stop);
4764d4fba8b9Smrg
4765d4fba8b9Smrg    TRACE(("...done DECTABSR\n"));
4766d4fba8b9Smrg}
4767d4fba8b9Smrg#endif
4768d4fba8b9Smrg
4769d522f475Smrgvoid
4770fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4771d522f475Smrg{
4772cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4773d522f475Smrg    char reply[BUFSIZ];
4774cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4775d522f475Smrg    Bool okay;
4776d522f475Smrg    ANSI params;
4777d4fba8b9Smrg#if OPT_DEC_RECTOPS
4778d4fba8b9Smrg    char psarg = '0';
4779d4fba8b9Smrg#endif
4780d522f475Smrg
4781cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4782d522f475Smrg
4783d522f475Smrg    if (dcslen != strlen(cp))
4784d522f475Smrg	/* shouldn't have nulls in the string */
4785d522f475Smrg	return;
4786d522f475Smrg
4787d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4788d522f475Smrg    case '$':			/* DECRQSS */
4789d522f475Smrg	okay = True;
4790d522f475Smrg
4791d522f475Smrg	cp++;
4792d4fba8b9Smrg	if (*cp == 'q') {
4793d4fba8b9Smrg	    *reply = '\0';
4794d4fba8b9Smrg	    cp++;
4795d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4796d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCA\n"));
4797d522f475Smrg		sprintf(reply, "%d%s",
4798d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4799d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4800d522f475Smrg			cp);
4801d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
48023367019cSmrg		if (screen->vtXX_level < 2) {
48033367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
48043367019cSmrg		    break;
48053367019cSmrg		}
4806d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCL\n"));
4807d522f475Smrg		sprintf(reply, "%d%s%s",
4808d522f475Smrg			(screen->vtXX_level ?
4809d522f475Smrg			 screen->vtXX_level : 1) + 60,
4810d522f475Smrg			(screen->vtXX_level >= 2)
4811d522f475Smrg			? (screen->control_eight_bits
4812d522f475Smrg			   ? ";0" : ";1")
4813d522f475Smrg			: "",
4814d522f475Smrg			cp);
4815d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4816d4fba8b9Smrg		TRACE(("DECRQSS -> DECSTBM\n"));
4817d522f475Smrg		sprintf(reply, "%d;%dr",
4818d522f475Smrg			screen->top_marg + 1,
4819d522f475Smrg			screen->bot_marg + 1);
48203367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
48213367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
4822d4fba8b9Smrg		    TRACE(("DECRQSS -> DECSLRM\n"));
48233367019cSmrg		    sprintf(reply, "%d;%ds",
48243367019cSmrg			    screen->lft_marg + 1,
48253367019cSmrg			    screen->rgt_marg + 1);
4826037a25ddSmrg		} else {
4827037a25ddSmrg		    okay = False;
48283367019cSmrg		}
4829d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4830d4fba8b9Smrg		TRACE(("DECRQSS -> SGR\n"));
4831d4fba8b9Smrg		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4832d522f475Smrg		strcat(reply, "m");
4833712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
48343367019cSmrg		int code = STEADY_BLOCK;
48353367019cSmrg		if (isCursorUnderline(screen))
48363367019cSmrg		    code = STEADY_UNDERLINE;
48373367019cSmrg		else if (isCursorBar(screen))
48383367019cSmrg		    code = STEADY_BAR;
48393367019cSmrg#if OPT_BLINK_CURS
484094644356Smrg		if (screen->cursor_blink_esc != 0)
48413367019cSmrg		    code -= 1;
48423367019cSmrg#endif
4843d4fba8b9Smrg		TRACE(("reply DECSCUSR\n"));
48443367019cSmrg		sprintf(reply, "%d%s", code, cp);
4845d4fba8b9Smrg	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4846d4fba8b9Smrg		sprintf(reply, "%d%s",
4847d4fba8b9Smrg			((screen->max_row > 24) ? screen->max_row : 24),
4848d4fba8b9Smrg			cp);
4849d4fba8b9Smrg		TRACE(("reply DECSLPP\n"));
4850d4fba8b9Smrg	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4851d4fba8b9Smrg		TRACE(("reply DECSCPP\n"));
4852d4fba8b9Smrg		sprintf(reply, "%d%s",
4853d4fba8b9Smrg			((xw->flags & IN132COLUMNS) ? 132 : 80),
4854d4fba8b9Smrg			cp);
485550027b5bSmrg	    } else
485650027b5bSmrg#if OPT_STATUS_LINE
485750027b5bSmrg	    if (!strcmp(cp, "$}")) {	/* DECSASD */
485850027b5bSmrg		TRACE(("reply DECSASD\n"));
485950027b5bSmrg		sprintf(reply, "%d%s",
486050027b5bSmrg			screen->status_active,
486150027b5bSmrg			cp);
486250027b5bSmrg	    } else if (!strcmp(cp, "$~")) {	/* DECSSDT */
486350027b5bSmrg		TRACE(("reply DECSASD\n"));
486450027b5bSmrg		sprintf(reply, "%d%s",
486550027b5bSmrg			screen->status_type,
486650027b5bSmrg			cp);
486750027b5bSmrg	    } else
486850027b5bSmrg#endif
486950027b5bSmrg	    if (!strcmp(cp, "*|")) {	/* DECSNLS */
4870d4fba8b9Smrg		TRACE(("reply DECSNLS\n"));
4871d4fba8b9Smrg		sprintf(reply, "%d%s",
4872d4fba8b9Smrg			screen->max_row + 1,
4873d4fba8b9Smrg			cp);
48740d92cbfdSchristos	    } else {
4875d4fba8b9Smrg		okay = False;
487622d8e007Schristos	    }
4877d4fba8b9Smrg
4878d4fba8b9Smrg	    unparseputc1(xw, ANSI_DCS);
4879d4fba8b9Smrg	    unparseputc(xw, okay ? '1' : '0');
4880d4fba8b9Smrg	    unparseputc(xw, '$');
4881d4fba8b9Smrg	    unparseputc(xw, 'r');
4882d4fba8b9Smrg	    cp = reply;
4883d4fba8b9Smrg	    unparseputs(xw, cp);
4884d4fba8b9Smrg	    unparseputc1(xw, ANSI_ST);
4885d522f475Smrg	} else {
4886d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4887d522f475Smrg	}
4888d522f475Smrg	break;
4889d522f475Smrg    case '+':
4890d522f475Smrg	cp++;
4891cd3331d0Smrg	switch (*cp) {
4892d4fba8b9Smrg#if OPT_TCAP_QUERY
4893d1603babSmrg	case 'p':		/* XTSETTCAP */
4894cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4895cd3331d0Smrg		set_termcap(xw, cp + 1);
4896cd3331d0Smrg	    }
4897cd3331d0Smrg	    break;
4898d1603babSmrg	case 'q':		/* XTGETTCAP */
4899cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4900cd3331d0Smrg		Bool fkey;
4901cd3331d0Smrg		unsigned state;
4902cd3331d0Smrg		int code;
4903cd3331d0Smrg		const char *tmp;
4904cd3331d0Smrg		const char *parsed = ++cp;
4905d522f475Smrg
4906cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4907d522f475Smrg
4908cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4909b7c89284Ssnj
4910cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4911d522f475Smrg
4912cd3331d0Smrg		unparseputc(xw, '+');
4913cd3331d0Smrg		unparseputc(xw, 'r');
4914d522f475Smrg
4915cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4916cd3331d0Smrg		    if (cp == parsed)
4917cd3331d0Smrg			break;	/* no data found, error */
4918d522f475Smrg
4919cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4920cd3331d0Smrg			unparseputc(xw, *tmp);
4921d522f475Smrg
4922cd3331d0Smrg		    if (code >= 0) {
4923cd3331d0Smrg			unparseputc(xw, '=');
4924cd3331d0Smrg			screen->tc_query_code = code;
4925cd3331d0Smrg			screen->tc_query_fkey = fkey;
4926d522f475Smrg#if OPT_ISO_COLORS
4927cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4928cd3331d0Smrg			 * number of colors) */
4929cd3331d0Smrg			if (code == XK_COLORS) {
4930d4fba8b9Smrg			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
4931d4fba8b9Smrg			} else
4932d4fba8b9Smrg#if OPT_DIRECT_COLOR
4933d4fba8b9Smrg			if (code == XK_RGB) {
4934d4fba8b9Smrg			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
4935d4fba8b9Smrg				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
4936d4fba8b9Smrg				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
4937d4fba8b9Smrg				    unparseputn(xw, xw->rgb_widths[0]);
4938d4fba8b9Smrg				} else {
4939d4fba8b9Smrg				    char temp[1024];
4940d1603babSmrg				    sprintf(temp, "%u/%u/%u",
4941d4fba8b9Smrg					    xw->rgb_widths[0],
4942d4fba8b9Smrg					    xw->rgb_widths[1],
4943d4fba8b9Smrg					    xw->rgb_widths[2]);
4944d4fba8b9Smrg				    unparseputs(xw, temp);
4945d4fba8b9Smrg				}
4946d4fba8b9Smrg			    } else {
4947d4fba8b9Smrg				unparseputs(xw, "-1");
4948d4fba8b9Smrg			    }
4949cd3331d0Smrg			} else
4950d4fba8b9Smrg#endif
4951cd3331d0Smrg#endif
4952cd3331d0Smrg			if (code == XK_TCAPNAME) {
4953c219fbebSmrg			    unparseputs(xw, resource.term_name);
4954cd3331d0Smrg			} else {
4955cd3331d0Smrg			    XKeyEvent event;
4956d4fba8b9Smrg			    memset(&event, 0, sizeof(event));
4957cd3331d0Smrg			    event.state = state;
4958cd3331d0Smrg			    Input(xw, &event, False);
4959cd3331d0Smrg			}
4960cd3331d0Smrg			screen->tc_query_code = -1;
4961cd3331d0Smrg		    } else {
4962cd3331d0Smrg			break;	/* no match found, error */
4963d522f475Smrg		    }
4964d522f475Smrg
4965d522f475Smrg		    cp = parsed;
4966cd3331d0Smrg		    if (*parsed == ';') {
4967cd3331d0Smrg			unparseputc(xw, *parsed++);
4968cd3331d0Smrg			cp = parsed;
4969cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4970cd3331d0Smrg		    }
4971d522f475Smrg		}
4972cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4973d522f475Smrg	    }
4974cd3331d0Smrg	    break;
4975d4fba8b9Smrg#endif
4976d4fba8b9Smrg#if OPT_XRES_QUERY
4977d1603babSmrg	case 'Q':		/* XTGETXRES */
4978d4fba8b9Smrg	    ++cp;
4979d4fba8b9Smrg	    if (AllowXResOps(xw)) {
4980d4fba8b9Smrg		Boolean first = True;
4981d1603babSmrg		okay = True;
4982d1603babSmrg		while (*cp != '\0' && okay) {
4983d4fba8b9Smrg		    const char *parsed = 0;
4984d4fba8b9Smrg		    const char *tmp;
4985d4fba8b9Smrg		    char *name = x_decode_hex(cp, &parsed);
4986d4fba8b9Smrg		    char *value;
4987d4fba8b9Smrg		    char *result;
4988d4fba8b9Smrg		    if (cp == parsed || name == NULL) {
4989d4fba8b9Smrg			free(name);
4990d4fba8b9Smrg			break;	/* no data found, error */
4991d4fba8b9Smrg		    }
4992d1603babSmrg		    if ((cp - parsed) > 1024) {
4993d1603babSmrg			free(name);
4994d1603babSmrg			break;	/* ignore improbable resource */
4995d1603babSmrg		    }
4996d4fba8b9Smrg		    TRACE(("query-feature '%s'\n", name));
4997d4fba8b9Smrg		    if ((value = vt100ResourceToString(xw, name)) != 0) {
4998d4fba8b9Smrg			okay = True;	/* valid */
4999d4fba8b9Smrg		    } else {
5000d4fba8b9Smrg			okay = False;	/* invalid */
5001d4fba8b9Smrg		    }
5002d4fba8b9Smrg		    if (first) {
5003d4fba8b9Smrg			unparseputc1(xw, ANSI_DCS);
5004d4fba8b9Smrg			unparseputc(xw, okay ? '1' : '0');
5005d4fba8b9Smrg			unparseputc(xw, '+');
5006d4fba8b9Smrg			unparseputc(xw, 'R');
5007d4fba8b9Smrg			first = False;
5008d4fba8b9Smrg		    }
5009d4fba8b9Smrg
5010d4fba8b9Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
5011d4fba8b9Smrg			unparseputc(xw, *tmp);
5012d4fba8b9Smrg
5013d4fba8b9Smrg		    if (value != 0) {
5014d4fba8b9Smrg			unparseputc1(xw, '=');
5015d4fba8b9Smrg			result = x_encode_hex(value);
5016d4fba8b9Smrg			unparseputs(xw, result);
5017d4fba8b9Smrg		    } else {
5018d4fba8b9Smrg			result = NULL;
5019d4fba8b9Smrg		    }
5020d4fba8b9Smrg
5021d4fba8b9Smrg		    free(name);
5022d4fba8b9Smrg		    free(value);
5023d4fba8b9Smrg		    free(result);
5024d4fba8b9Smrg
5025d4fba8b9Smrg		    cp = parsed;
5026d4fba8b9Smrg		    if (*parsed == ';') {
5027d4fba8b9Smrg			unparseputc(xw, *parsed++);
5028d4fba8b9Smrg			cp = parsed;
5029d4fba8b9Smrg		    }
5030d4fba8b9Smrg		}
5031d4fba8b9Smrg		if (!first)
5032d4fba8b9Smrg		    unparseputc1(xw, ANSI_ST);
5033d4fba8b9Smrg	    }
5034d4fba8b9Smrg	    break;
5035d4fba8b9Smrg#endif
5036d522f475Smrg	}
5037d522f475Smrg	break;
5038d4fba8b9Smrg#if OPT_DEC_RECTOPS
5039d4fba8b9Smrg    case '1':
5040d4fba8b9Smrg	/* FALLTHRU */
5041d4fba8b9Smrg    case '2':
5042d4fba8b9Smrg	if (*skip_params(cp) == '$') {
5043d4fba8b9Smrg	    psarg = *cp++;
5044d4fba8b9Smrg	    if ((*cp++ == '$')
5045d4fba8b9Smrg		&& (*cp++ == 't')
5046d4fba8b9Smrg		&& (screen->vtXX_level >= 3)) {
5047d4fba8b9Smrg		switch (psarg) {
5048d4fba8b9Smrg		case '1':
5049d4fba8b9Smrg		    TRACE(("DECRSPS (DECCIR)\n"));
5050d4fba8b9Smrg		    restore_DECCIR(xw, cp);
5051d4fba8b9Smrg		    break;
5052d4fba8b9Smrg		case '2':
5053d4fba8b9Smrg		    TRACE(("DECRSPS (DECTABSR)\n"));
5054d4fba8b9Smrg		    restore_DECTABSR(xw, cp);
5055d4fba8b9Smrg		    break;
5056d4fba8b9Smrg		}
5057d4fba8b9Smrg	    }
5058d4fba8b9Smrg	    break;
5059d4fba8b9Smrg	}
5060d522f475Smrg#endif
5061d4fba8b9Smrg	/* FALLTHRU */
5062d522f475Smrg    default:
5063d4fba8b9Smrg	if (optRegisGraphics(screen) ||
5064d4fba8b9Smrg	    optSixelGraphics(screen) ||
5065fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
50660d92cbfdSchristos	    parse_ansi_params(&params, &cp);
50670d92cbfdSchristos	    switch (params.a_final) {
5068d4fba8b9Smrg	    case 'p':		/* ReGIS */
50699a64e1c5Smrg#if OPT_REGIS_GRAPHICS
5070d4fba8b9Smrg		if (optRegisGraphics(screen)) {
5071fa3f02f3Smrg		    parse_regis(xw, &params, cp);
5072fa3f02f3Smrg		}
50739a64e1c5Smrg#else
50749a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
50759a64e1c5Smrg#endif
5076fa3f02f3Smrg		break;
5077d4fba8b9Smrg	    case 'q':		/* sixel */
50789a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
5079d4fba8b9Smrg		if (optSixelGraphics(screen)) {
5080037a25ddSmrg		    (void) parse_sixel(xw, &params, cp);
5081fa3f02f3Smrg		}
50829a64e1c5Smrg#else
50839a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
5084fa3f02f3Smrg#endif
50859a64e1c5Smrg		break;
50860d92cbfdSchristos	    case '|':		/* DECUDK */
50879a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
50889a64e1c5Smrg		    if (params.a_param[0] == 0)
50899a64e1c5Smrg			reset_decudk(xw);
50909a64e1c5Smrg		    parse_decudk(xw, cp);
50919a64e1c5Smrg		}
50920d92cbfdSchristos		break;
509394644356Smrg	    case L_CURL:	/* DECDLD */
50949a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
50959a64e1c5Smrg		    parse_decdld(&params, cp);
50969a64e1c5Smrg		}
50970d92cbfdSchristos		break;
50980d92cbfdSchristos	    }
5099d522f475Smrg	}
5100d522f475Smrg	break;
5101d522f475Smrg    }
5102d522f475Smrg    unparse_end(xw);
5103d522f475Smrg}
5104d522f475Smrg
5105cb4a1343Smrg#if OPT_DEC_RECTOPS
5106cb4a1343Smrgenum {
5107cb4a1343Smrg    mdUnknown = 0,
5108cb4a1343Smrg    mdMaybeSet = 1,
5109cb4a1343Smrg    mdMaybeReset = 2,
5110cb4a1343Smrg    mdAlwaysSet = 3,
5111cb4a1343Smrg    mdAlwaysReset = 4
5112cb4a1343Smrg};
5113cb4a1343Smrg
5114cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
51153367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
5116cb4a1343Smrg
5117cb4a1343Smrg/*
5118cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
5119cb4a1343Smrg * 0 - not recognized
5120cb4a1343Smrg * 1 - set
5121cb4a1343Smrg * 2 - reset
5122cb4a1343Smrg * 3 - permanently set
5123cb4a1343Smrg * 4 - permanently reset
5124cb4a1343Smrg * Only one mode can be reported at a time.
5125cb4a1343Smrg */
5126cb4a1343Smrgvoid
5127d4fba8b9Smrgdo_ansi_rqm(XtermWidget xw, int nparams, int *params)
5128cb4a1343Smrg{
5129cb4a1343Smrg    ANSI reply;
5130cb4a1343Smrg    int count = 0;
5131cb4a1343Smrg
5132d4fba8b9Smrg    TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5133cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5134037a25ddSmrg
5135cb4a1343Smrg    if (nparams >= 1) {
5136d4fba8b9Smrg	int result = mdUnknown;
5137037a25ddSmrg
5138d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5139cb4a1343Smrg	switch (params[0]) {
5140cb4a1343Smrg	case 1:		/* GATM */
5141cb4a1343Smrg	    result = mdAlwaysReset;
5142cb4a1343Smrg	    break;
5143cb4a1343Smrg	case 2:
5144cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
5145cb4a1343Smrg	    break;
5146cb4a1343Smrg	case 3:		/* CRM */
5147cb4a1343Smrg	    result = mdMaybeReset;
5148cb4a1343Smrg	    break;
5149cb4a1343Smrg	case 4:
5150cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
5151cb4a1343Smrg	    break;
5152cb4a1343Smrg	case 5:		/* SRTM */
5153cb4a1343Smrg	case 7:		/* VEM */
5154cb4a1343Smrg	case 10:		/* HEM */
5155cb4a1343Smrg	case 11:		/* PUM */
5156cb4a1343Smrg	    result = mdAlwaysReset;
5157cb4a1343Smrg	    break;
5158cb4a1343Smrg	case 12:
5159cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
5160cb4a1343Smrg	    break;
5161cb4a1343Smrg	case 13:		/* FEAM */
5162cb4a1343Smrg	case 14:		/* FETM */
5163cb4a1343Smrg	case 15:		/* MATM */
5164cb4a1343Smrg	case 16:		/* TTM */
5165cb4a1343Smrg	case 17:		/* SATM */
5166cb4a1343Smrg	case 18:		/* TSM */
5167cb4a1343Smrg	case 19:		/* EBM */
5168cb4a1343Smrg	    result = mdAlwaysReset;
5169cb4a1343Smrg	    break;
5170cb4a1343Smrg	case 20:
5171cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
5172cb4a1343Smrg	    break;
5173cb4a1343Smrg	}
5174cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5175cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5176cb4a1343Smrg    }
5177cb4a1343Smrg    reply.a_type = ANSI_CSI;
5178cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5179cb4a1343Smrg    reply.a_inters = '$';
5180cb4a1343Smrg    reply.a_final = 'y';
5181cb4a1343Smrg    unparseseq(xw, &reply);
5182cb4a1343Smrg}
5183cb4a1343Smrg
5184cb4a1343Smrgvoid
5185d4fba8b9Smrgdo_dec_rqm(XtermWidget xw, int nparams, int *params)
5186cb4a1343Smrg{
5187cb4a1343Smrg    ANSI reply;
5188cb4a1343Smrg    int count = 0;
5189cb4a1343Smrg
5190d4fba8b9Smrg    TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5191cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5192037a25ddSmrg
5193cb4a1343Smrg    if (nparams >= 1) {
5194cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
5195d4fba8b9Smrg	int result = mdUnknown;
5196cb4a1343Smrg
5197d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5198d4fba8b9Smrg	switch ((DECSET_codes) params[0]) {
5199fa3f02f3Smrg	case srm_DECCKM:
5200cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5201cb4a1343Smrg	    break;
5202fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
5203cb4a1343Smrg#if OPT_VT52_MODE
52043367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
5205cb4a1343Smrg#else
5206cb4a1343Smrg	    result = mdMaybeSet;
5207cb4a1343Smrg#endif
5208cb4a1343Smrg	    break;
5209fa3f02f3Smrg	case srm_DECCOLM:
5210cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
5211cb4a1343Smrg	    break;
5212fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
5213cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5214cb4a1343Smrg	    break;
5215fa3f02f3Smrg	case srm_DECSCNM:
5216cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5217cb4a1343Smrg	    break;
5218fa3f02f3Smrg	case srm_DECOM:
5219cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
5220cb4a1343Smrg	    break;
5221fa3f02f3Smrg	case srm_DECAWM:
5222cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
5223cb4a1343Smrg	    break;
5224fa3f02f3Smrg	case srm_DECARM:
5225cb4a1343Smrg	    result = mdAlwaysReset;
5226cb4a1343Smrg	    break;
5227fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
5228cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5229cb4a1343Smrg	    break;
5230cb4a1343Smrg#if OPT_TOOLBAR
5231fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
5232cb4a1343Smrg	    result = MdBool(resource.toolBar);
5233cb4a1343Smrg	    break;
5234cb4a1343Smrg#endif
5235cb4a1343Smrg#if OPT_BLINK_CURS
5236d4fba8b9Smrg	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5237d4fba8b9Smrg	    result = MdBool(screen->cursor_blink_esc);
5238d4fba8b9Smrg	    break;
5239d4fba8b9Smrg	case srm_CURSOR_BLINK_OPS:
5240d4fba8b9Smrg	    switch (screen->cursor_blink) {
5241d4fba8b9Smrg	    case cbTrue:
5242d4fba8b9Smrg		result = mdMaybeSet;
5243d4fba8b9Smrg		break;
5244d4fba8b9Smrg	    case cbFalse:
5245d4fba8b9Smrg		result = mdMaybeReset;
5246d4fba8b9Smrg		break;
5247d4fba8b9Smrg	    case cbAlways:
5248d4fba8b9Smrg		result = mdAlwaysSet;
5249d4fba8b9Smrg		break;
5250d4fba8b9Smrg	    case cbLAST:
5251d4fba8b9Smrg		/* FALLTHRU */
5252d4fba8b9Smrg	    case cbNever:
5253d4fba8b9Smrg		result = mdAlwaysReset;
5254d4fba8b9Smrg		break;
5255d4fba8b9Smrg	    }
5256d4fba8b9Smrg	    break;
5257d4fba8b9Smrg	case srm_XOR_CURSOR_BLINKS:
5258d4fba8b9Smrg	    result = (screen->cursor_blink_xor
5259d4fba8b9Smrg		      ? mdAlwaysSet
5260d4fba8b9Smrg		      : mdAlwaysReset);
5261cb4a1343Smrg	    break;
5262cb4a1343Smrg#endif
5263fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
5264712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
5265cb4a1343Smrg	    break;
5266fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
5267712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
5268cb4a1343Smrg	    break;
5269fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5270cb4a1343Smrg	    result = MdBool(screen->cursor_set);
5271cb4a1343Smrg	    break;
5272fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
5273cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5274cb4a1343Smrg	    break;
5275cb4a1343Smrg#if OPT_SHIFT_FONTS
5276fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
5277cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
5278cb4a1343Smrg	    break;
5279cb4a1343Smrg#endif
5280cb4a1343Smrg#if OPT_TEK4014
5281fa3f02f3Smrg	case srm_DECTEK:
5282cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
5283cb4a1343Smrg	    break;
5284cb4a1343Smrg#endif
5285fa3f02f3Smrg	case srm_132COLS:
5286cb4a1343Smrg	    result = MdBool(screen->c132);
5287cb4a1343Smrg	    break;
5288fa3f02f3Smrg	case srm_CURSES_HACK:
5289cb4a1343Smrg	    result = MdBool(screen->curses);
5290cb4a1343Smrg	    break;
5291fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
5292d4fba8b9Smrg	    if (screen->vtXX_level >= 2) {
5293d4fba8b9Smrg		result = MdFlag(xw->flags, NATIONAL);
5294d4fba8b9Smrg	    } else {
5295d4fba8b9Smrg		result = 0;
5296d4fba8b9Smrg	    }
5297cb4a1343Smrg	    break;
5298fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
5299cb4a1343Smrg	    result = MdBool(screen->marginbell);
5300cb4a1343Smrg	    break;
5301d4fba8b9Smrg#if OPT_PRINT_GRAPHICS
5302d4fba8b9Smrg	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5303d4fba8b9Smrg	    result = MdBool(screen->graphics_expanded_print_mode);
5304d4fba8b9Smrg	    break;
5305d4fba8b9Smrg#endif
5306fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
5307d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5308d4fba8b9Smrg		result = MdFlag(xw->flags, REVERSEWRAP);
5309cb4a1343Smrg	    break;
5310d4fba8b9Smrg#if defined(ALLOWLOGGING)
5311fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
5312d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5313d4fba8b9Smrg#if defined(ALLOWLOGFILEONOFF)
5314d4fba8b9Smrg		result = MdBool(screen->logging);
5315d4fba8b9Smrg#else
5316d4fba8b9Smrg		result = ((MdBool(screen->logging) == mdMaybeSet)
5317d4fba8b9Smrg			  ? mdAlwaysSet
5318d4fba8b9Smrg			  : mdAlwaysReset);
5319d4fba8b9Smrg#endif
5320d4fba8b9Smrg	    break;
5321d4fba8b9Smrg#elif OPT_PRINT_GRAPHICS
5322d4fba8b9Smrg	case srm_DECGPBM:	/* Graphics Print Background Mode */
5323d4fba8b9Smrg	    result = MdBool(screen->graphics_print_background_mode);
5324cb4a1343Smrg	    break;
5325cb4a1343Smrg#endif
5326fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5327cb4a1343Smrg	    /* FALLTHRU */
5328fa3f02f3Smrg	case srm_OPT_ALTBUF:
5329cb4a1343Smrg	    result = MdBool(screen->whichBuf);
5330cb4a1343Smrg	    break;
5331d4fba8b9Smrg	case srm_ALTBUF:
5332d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5333d4fba8b9Smrg		result = MdBool(screen->whichBuf);
5334d4fba8b9Smrg	    break;
5335fa3f02f3Smrg	case srm_DECNKM:
5336cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5337cb4a1343Smrg	    break;
5338fa3f02f3Smrg	case srm_DECBKM:
5339cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5340cb4a1343Smrg	    break;
5341fa3f02f3Smrg	case srm_DECLRMM:
5342d4fba8b9Smrg	    if (screen->vtXX_level >= 4) {	/* VT420 */
5343d4fba8b9Smrg		result = MdFlag(xw->flags, LEFT_RIGHT);
5344d4fba8b9Smrg	    } else {
5345d4fba8b9Smrg		result = 0;
5346d4fba8b9Smrg	    }
53473367019cSmrg	    break;
5348fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
5349fa3f02f3Smrg	case srm_DECSDM:
5350fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5351fa3f02f3Smrg	    break;
5352fa3f02f3Smrg#endif
5353fa3f02f3Smrg	case srm_DECNCSM:
5354d4fba8b9Smrg	    if (screen->vtXX_level >= 5) {	/* VT510 */
5355d4fba8b9Smrg		result = MdFlag(xw->flags, NOCLEAR_COLM);
5356d4fba8b9Smrg	    } else {
5357d4fba8b9Smrg		result = 0;
5358d4fba8b9Smrg	    }
53593367019cSmrg	    break;
5360d4fba8b9Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5361cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5362cb4a1343Smrg	    break;
5363fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5364cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5365cb4a1343Smrg	    break;
5366fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
5367cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5368cb4a1343Smrg	    break;
5369fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
5370cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5371cb4a1343Smrg	    break;
5372cb4a1343Smrg#if OPT_FOCUS_EVENT
5373fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
5374cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
5375cb4a1343Smrg	    break;
5376cb4a1343Smrg#endif
5377fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
53783367019cSmrg	    /* FALLTHRU */
5379fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
53803367019cSmrg	    /* FALLTHRU */
5381fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
5382d4fba8b9Smrg	    /* FALLTHRU */
5383d4fba8b9Smrg	case srm_PIXEL_POSITION_MOUSE:
53843367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
53853367019cSmrg	    break;
5386fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
53873367019cSmrg	    result = MdBool(screen->alternateScroll);
5388cb4a1343Smrg	    break;
5389fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
5390cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
5391cb4a1343Smrg	    break;
5392fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5393cb4a1343Smrg	    result = MdBool(screen->scrollkey);
5394cb4a1343Smrg	    break;
5395fa3f02f3Smrg	case srm_EIGHT_BIT_META:
53963367019cSmrg	    result = MdBool(screen->eight_bit_meta);
5397cb4a1343Smrg	    break;
5398cb4a1343Smrg#if OPT_NUM_LOCK
5399fa3f02f3Smrg	case srm_REAL_NUMLOCK:
5400cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
5401cb4a1343Smrg	    break;
5402fa3f02f3Smrg	case srm_META_SENDS_ESC:
5403cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
5404cb4a1343Smrg	    break;
5405cb4a1343Smrg#endif
5406fa3f02f3Smrg	case srm_DELETE_IS_DEL:
5407d4fba8b9Smrg	    result = MdBool(xtermDeleteIsDEL(xw));
5408cb4a1343Smrg	    break;
5409cb4a1343Smrg#if OPT_NUM_LOCK
5410fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
5411cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
5412cb4a1343Smrg	    break;
5413cb4a1343Smrg#endif
5414fa3f02f3Smrg	case srm_KEEP_SELECTION:
5415cb4a1343Smrg	    result = MdBool(screen->keepSelection);
5416cb4a1343Smrg	    break;
5417fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
5418cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
5419cb4a1343Smrg	    break;
5420fa3f02f3Smrg	case srm_BELL_IS_URGENT:
5421cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
5422cb4a1343Smrg	    break;
5423fa3f02f3Smrg	case srm_POP_ON_BELL:
5424cb4a1343Smrg	    result = MdBool(screen->poponbell);
5425cb4a1343Smrg	    break;
5426d4fba8b9Smrg	case srm_KEEP_CLIPBOARD:
5427d4fba8b9Smrg	    result = MdBool(screen->keepClipboard);
5428d4fba8b9Smrg	    break;
5429d4fba8b9Smrg	case srm_ALLOW_ALTBUF:
5430d4fba8b9Smrg	    result = MdBool(xw->misc.titeInhibit);
5431d4fba8b9Smrg	    break;
5432d4fba8b9Smrg	case srm_SAVE_CURSOR:
5433cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
5434cb4a1343Smrg	    break;
5435cb4a1343Smrg#if OPT_TCAP_FKEYS
5436fa3f02f3Smrg	case srm_TCAP_FKEYS:
5437cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5438cb4a1343Smrg	    break;
5439cb4a1343Smrg#endif
5440cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
5441fa3f02f3Smrg	case srm_SUN_FKEYS:
5442cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5443cb4a1343Smrg	    break;
5444cb4a1343Smrg#endif
5445cb4a1343Smrg#if OPT_HP_FUNC_KEYS
5446fa3f02f3Smrg	case srm_HP_FKEYS:
5447cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5448cb4a1343Smrg	    break;
5449cb4a1343Smrg#endif
5450cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
5451fa3f02f3Smrg	case srm_SCO_FKEYS:
5452cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5453cb4a1343Smrg	    break;
5454cb4a1343Smrg#endif
5455fa3f02f3Smrg	case srm_LEGACY_FKEYS:
5456cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5457cb4a1343Smrg	    break;
5458cb4a1343Smrg#if OPT_SUNPC_KBD
5459fa3f02f3Smrg	case srm_VT220_FKEYS:
5460cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5461cb4a1343Smrg	    break;
5462cb4a1343Smrg#endif
5463d4fba8b9Smrg#if OPT_PASTE64 || OPT_READLINE
5464d4fba8b9Smrg	case srm_PASTE_IN_BRACKET:
5465d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5466d4fba8b9Smrg	    break;
5467d4fba8b9Smrg#endif
5468cb4a1343Smrg#if OPT_READLINE
5469fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
5470d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5471cb4a1343Smrg	    break;
5472fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
5473d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5474cb4a1343Smrg	    break;
5475fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
5476d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5477cb4a1343Smrg	    break;
5478fa3f02f3Smrg	case srm_PASTE_QUOTE:
5479d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5480cb4a1343Smrg	    break;
5481fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
5482d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5483cb4a1343Smrg	    break;
5484cb4a1343Smrg#endif /* OPT_READLINE */
5485d4fba8b9Smrg#if OPT_GRAPHICS
54869a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
54879a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
54889a64e1c5Smrg	    break;
54899a64e1c5Smrg#endif
54909a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
54919a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
54929a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
54939a64e1c5Smrg	    break;
54949a64e1c5Smrg#endif
54959a64e1c5Smrg	default:
54969a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
54979a64e1c5Smrg		   params[0]));
5498cb4a1343Smrg	}
5499cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5500cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5501d4fba8b9Smrg	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5502cb4a1343Smrg    }
5503cb4a1343Smrg    reply.a_type = ANSI_CSI;
5504cb4a1343Smrg    reply.a_pintro = '?';
5505cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5506cb4a1343Smrg    reply.a_inters = '$';
5507cb4a1343Smrg    reply.a_final = 'y';
5508cb4a1343Smrg    unparseseq(xw, &reply);
5509cb4a1343Smrg}
5510cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
5511cb4a1343Smrg
5512d522f475Smrgchar *
55139a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
5514d522f475Smrg{
5515d4fba8b9Smrg    char *result = NULL;
5516d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
55179a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
5518d4fba8b9Smrg	result = xw->work.user_keys[keycode].str;
5519d4fba8b9Smrg	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5520d4fba8b9Smrg    } else {
5521d4fba8b9Smrg	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5522d4fba8b9Smrg    }
5523d4fba8b9Smrg    return result;
5524d4fba8b9Smrg}
5525d4fba8b9Smrg
5526d4fba8b9Smrg#if OPT_REPORT_ICONS
5527d4fba8b9Smrgvoid
5528d4fba8b9Smrgreport_icons(const char *fmt, ...)
5529d4fba8b9Smrg{
5530d4fba8b9Smrg    if (resource.reportIcons) {
5531d4fba8b9Smrg	va_list ap;
5532d4fba8b9Smrg	va_start(ap, fmt);
5533d4fba8b9Smrg	vfprintf(stdout, fmt, ap);
5534d4fba8b9Smrg	va_end(ap);
5535d4fba8b9Smrg#if OPT_TRACE
5536d4fba8b9Smrg	va_start(ap, fmt);
5537d4fba8b9Smrg	TraceVA(fmt, ap);
5538d4fba8b9Smrg	va_end(ap);
5539d4fba8b9Smrg#endif
5540d522f475Smrg    }
5541d522f475Smrg}
5542d4fba8b9Smrg#endif
5543d522f475Smrg
55443367019cSmrg#ifdef HAVE_LIBXPM
55453367019cSmrg
55463367019cSmrg#ifndef PIXMAP_ROOTDIR
55473367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
55483367019cSmrg#endif
55493367019cSmrg
55503367019cSmrgtypedef struct {
55513367019cSmrg    const char *name;
55523367019cSmrg    const char *const *data;
55533367019cSmrg} XPM_DATA;
55543367019cSmrg
55553367019cSmrgstatic char *
5556d4fba8b9Smrgx_find_icon(char **work, int *state, const char *filename, const char *suffix)
55573367019cSmrg{
55583367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
55593367019cSmrg    const char *larger = "_48x48";
55603367019cSmrg    char *result = 0;
55613367019cSmrg
55623367019cSmrg    if (*state >= 0) {
55633367019cSmrg	if ((*state & 1) == 0)
55643367019cSmrg	    suffix = "";
55653367019cSmrg	if ((*state & 2) == 0)
55663367019cSmrg	    larger = "";
55673367019cSmrg	if ((*state & 4) == 0) {
55683367019cSmrg	    prefix = "";
55693367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
55703367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
55713367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
55723367019cSmrg	    *state = -1;
55733367019cSmrg	} else if (*state >= 8) {
55743367019cSmrg	    *state = -1;
55753367019cSmrg	}
55763367019cSmrg    }
55773367019cSmrg
55783367019cSmrg    if (*state >= 0) {
5579037a25ddSmrg	size_t length;
5580037a25ddSmrg
5581d4fba8b9Smrg	FreeAndNull(*work);
55823367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
55833367019cSmrg	    strlen(suffix);
55843367019cSmrg	if ((result = malloc(length)) != 0) {
55853367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
55863367019cSmrg	    *work = result;
55873367019cSmrg	}
55883367019cSmrg	*state += 1;
55893367019cSmrg    }
5590d4fba8b9Smrg    TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
55913367019cSmrg    return result;
55923367019cSmrg}
55933367019cSmrg
55943367019cSmrg#if OPT_BUILTIN_XPMS
5595d4fba8b9Smrg
55963367019cSmrgstatic const XPM_DATA *
5597d4fba8b9Smrgbuilt_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
55983367019cSmrg{
55993367019cSmrg    const XPM_DATA *result = 0;
56003367019cSmrg    if (!IsEmpty(find)) {
56013367019cSmrg	Cardinal n;
56023367019cSmrg	for (n = 0; n < length; ++n) {
56033367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
56043367019cSmrg		result = table + n;
5605d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[n].name));
56063367019cSmrg		break;
56073367019cSmrg	    }
56083367019cSmrg	}
56093367019cSmrg
56103367019cSmrg	/*
56113367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
56123367019cSmrg	 * which are all _HHxWW format.
56133367019cSmrg	 */
56143367019cSmrg	if (result == 0) {
56153367019cSmrg	    const char *base = table[0].name;
56163367019cSmrg	    const char *last = strchr(base, '_');
56173367019cSmrg	    if (last != 0
56183367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
56193367019cSmrg		result = table + length - 1;
5620d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[0].name));
56213367019cSmrg	    }
56223367019cSmrg	}
56233367019cSmrg    }
56243367019cSmrg    return result;
56253367019cSmrg}
5626d4fba8b9Smrg#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
56273367019cSmrg#endif /* OPT_BUILTIN_XPMS */
56283367019cSmrg
56293367019cSmrgtypedef enum {
56303367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
56313367019cSmrg    ,eHintNone
56323367019cSmrg    ,eHintSearch
56333367019cSmrg} ICON_HINT;
56343367019cSmrg#endif /* HAVE_LIBXPM */
56353367019cSmrg
56363367019cSmrgint
56373367019cSmrggetVisualDepth(XtermWidget xw)
56383367019cSmrg{
56393367019cSmrg    int result = 0;
56403367019cSmrg
5641fa3f02f3Smrg    if (getVisualInfo(xw)) {
5642fa3f02f3Smrg	result = xw->visInfo->depth;
56433367019cSmrg    }
56443367019cSmrg    return result;
56453367019cSmrg}
56463367019cSmrg
56473367019cSmrg/*
56483367019cSmrg * WM_ICON_SIZE should be honored if possible.
56493367019cSmrg */
56503367019cSmrgvoid
5651d4fba8b9SmrgxtermLoadIcon(XtermWidget xw, const char *icon_hint)
56523367019cSmrg{
56533367019cSmrg#ifdef HAVE_LIBXPM
56543367019cSmrg    Display *dpy = XtDisplay(xw);
56553367019cSmrg    Pixmap myIcon = 0;
56563367019cSmrg    Pixmap myMask = 0;
56573367019cSmrg    char *workname = 0;
5658d4fba8b9Smrg    ICON_HINT hint = eHintDefault;
5659fa3f02f3Smrg#include <builtin_icons.h>
56603367019cSmrg
5661d4fba8b9Smrg    ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5662d4fba8b9Smrg    if (!IsEmpty(icon_hint)) {
5663d4fba8b9Smrg	if (!x_strcasecmp(icon_hint, "none")) {
5664d4fba8b9Smrg	    hint = eHintNone;
5665d4fba8b9Smrg	} else {
5666d4fba8b9Smrg	    hint = eHintSearch;
5667d4fba8b9Smrg	}
5668d4fba8b9Smrg    }
56693367019cSmrg
56703367019cSmrg    if (hint == eHintSearch) {
56713367019cSmrg	int state = 0;
5672d4fba8b9Smrg	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) {
56733367019cSmrg	    Pixmap resIcon = 0;
56743367019cSmrg	    Pixmap shapemask = 0;
56753367019cSmrg	    XpmAttributes attributes;
5676d4fba8b9Smrg	    struct stat sb;
56773367019cSmrg
56783367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
56793367019cSmrg	    attributes.valuemask = XpmDepth;
56803367019cSmrg
5681d4fba8b9Smrg	    if (IsEmpty(workname)
5682d4fba8b9Smrg		|| lstat(workname, &sb) != 0
5683d4fba8b9Smrg		|| !S_ISREG(sb.st_mode)) {
5684d4fba8b9Smrg		TRACE(("...failure (no such file)\n"));
5685d4fba8b9Smrg	    } else {
5686d4fba8b9Smrg		int rc = XpmReadFileToPixmap(dpy,
5687d4fba8b9Smrg					     DefaultRootWindow(dpy),
5688d4fba8b9Smrg					     workname,
5689d4fba8b9Smrg					     &resIcon,
5690d4fba8b9Smrg					     &shapemask,
5691d4fba8b9Smrg					     &attributes);
5692d4fba8b9Smrg		if (rc == XpmSuccess) {
5693d4fba8b9Smrg		    myIcon = resIcon;
5694d4fba8b9Smrg		    myMask = shapemask;
5695d4fba8b9Smrg		    TRACE(("...success\n"));
5696d4fba8b9Smrg		    ReportIcons(("found/loaded icon-file %s\n", workname));
5697d4fba8b9Smrg		    break;
5698d4fba8b9Smrg		} else {
5699d4fba8b9Smrg		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5700d4fba8b9Smrg		}
57013367019cSmrg	    }
57023367019cSmrg	}
57033367019cSmrg    }
57043367019cSmrg
57053367019cSmrg    /*
57063367019cSmrg     * If no external file was found, look for the name in the built-in table.
57073367019cSmrg     * If that fails, just use the biggest mini-icon.
57083367019cSmrg     */
57093367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
57103367019cSmrg	char **data;
57113367019cSmrg#if OPT_BUILTIN_XPMS
57123367019cSmrg	const XPM_DATA *myData = 0;
5713d4fba8b9Smrg	myData = BuiltInXPM(mini_xterm_xpms);
57143367019cSmrg	if (myData == 0)
5715d4fba8b9Smrg	    myData = BuiltInXPM(filled_xterm_xpms);
57163367019cSmrg	if (myData == 0)
5717d4fba8b9Smrg	    myData = BuiltInXPM(xterm_color_xpms);
57183367019cSmrg	if (myData == 0)
5719d4fba8b9Smrg	    myData = BuiltInXPM(xterm_xpms);
57203367019cSmrg	if (myData == 0)
57213367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
572294644356Smrg	data = (char **) myData->data;
57233367019cSmrg#else
57243367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
57253367019cSmrg#endif
57263367019cSmrg	if (XpmCreatePixmapFromData(dpy,
57273367019cSmrg				    DefaultRootWindow(dpy),
57283367019cSmrg				    data,
5729d4fba8b9Smrg				    &myIcon, &myMask, 0) == 0) {
5730d4fba8b9Smrg	    ReportIcons(("loaded built-in pixmap icon\n"));
5731d4fba8b9Smrg	} else {
57323367019cSmrg	    myIcon = 0;
57333367019cSmrg	    myMask = 0;
57343367019cSmrg	}
57353367019cSmrg    }
57363367019cSmrg
57373367019cSmrg    if (myIcon != 0) {
57383367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
57393367019cSmrg	if (!hints)
57403367019cSmrg	    hints = XAllocWMHints();
57413367019cSmrg
57423367019cSmrg	if (hints) {
57433367019cSmrg	    hints->flags |= IconPixmapHint;
57443367019cSmrg	    hints->icon_pixmap = myIcon;
57453367019cSmrg	    if (myMask) {
57463367019cSmrg		hints->flags |= IconMaskHint;
57473367019cSmrg		hints->icon_mask = myMask;
57483367019cSmrg	    }
57493367019cSmrg
57503367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
57513367019cSmrg	    XFree(hints);
5752d4fba8b9Smrg	    ReportIcons(("updated window-manager hints\n"));
57533367019cSmrg	}
57543367019cSmrg    }
57553367019cSmrg
5756d4fba8b9Smrg    free(workname);
57573367019cSmrg
57583367019cSmrg#else
57593367019cSmrg    (void) xw;
5760d4fba8b9Smrg    (void) icon_hint;
57613367019cSmrg#endif
57623367019cSmrg}
57633367019cSmrg
57643367019cSmrgvoid
5765cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
5766d522f475Smrg{
5767d522f475Smrg    Arg args[1];
5768cd3331d0Smrg    Boolean changed = True;
5769d522f475Smrg    Widget w = CURRENT_EMU();
5770d522f475Smrg    Widget top = SHELL_OF(w);
5771d522f475Smrg
5772d4fba8b9Smrg    char *my_attr = NULL;
5773d4fba8b9Smrg    char *old_value = value;
5774d4fba8b9Smrg#if OPT_WIDE_CHARS
5775d4fba8b9Smrg    Boolean titleIsUTF8;
5776d4fba8b9Smrg#endif
5777d522f475Smrg
5778b7c89284Ssnj    if (!AllowTitleOps(xw))
5779d522f475Smrg	return;
5780d522f475Smrg
5781d4fba8b9Smrg    /*
5782d4fba8b9Smrg     * Ignore empty or too-long requests.
5783d4fba8b9Smrg     */
5784d4fba8b9Smrg    if (value == 0 || strlen(value) > 1000)
5785d4fba8b9Smrg	return;
5786d4fba8b9Smrg
5787cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
5788cd3331d0Smrg	const char *temp;
5789cd3331d0Smrg	char *test;
5790cd3331d0Smrg
5791d4fba8b9Smrg	/* this allocates a new string, if no error is detected */
5792cd3331d0Smrg	value = x_decode_hex(value, &temp);
5793d4fba8b9Smrg	if (value == 0 || *temp != '\0') {
57943367019cSmrg	    free(value);
5795cd3331d0Smrg	    return;
57963367019cSmrg	}
5797cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
5798cd3331d0Smrg	    if (CharOf(*test) < 32) {
5799cd3331d0Smrg		*test = '\0';
5800cd3331d0Smrg		break;
5801cd3331d0Smrg	    }
5802cd3331d0Smrg	}
5803cd3331d0Smrg    }
5804d4fba8b9Smrg#if OPT_WIDE_CHARS
5805d522f475Smrg    /*
5806d4fba8b9Smrg     * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
5807d4fba8b9Smrg     * the WM_NAME property, rather than doing this directly.  That relies on
5808d4fba8b9Smrg     * the application to tell it if the format should be something other than
5809d4fba8b9Smrg     * STRING, i.e., by setting the XtNtitleEncoding resource.
5810d4fba8b9Smrg     *
5811d4fba8b9Smrg     * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
5812d4fba8b9Smrg     * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
5813d4fba8b9Smrg     * added UTF8_STRING (the documentation for that was discarded by an Xorg
5814d4fba8b9Smrg     * developer, although the source-code provides this feature).
5815d4fba8b9Smrg     *
5816d4fba8b9Smrg     * Since X11R5, if the X11 library fails to store a text property as
5817d4fba8b9Smrg     * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
5818d4fba8b9Smrg     * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
5819d4fba8b9Smrg     * case, limit the resulting characters to the printable ISO-8859-1 set.
5820d522f475Smrg     */
5821d4fba8b9Smrg    titleIsUTF8 = isValidUTF8((Char *) value);
5822d4fba8b9Smrg    if (IsSetUtf8Title(xw) && titleIsUTF8) {
5823d4fba8b9Smrg	char *testc = malloc(strlen(value) + 1);
5824d4fba8b9Smrg	Char *nextc = (Char *) value;
5825d4fba8b9Smrg	Boolean ok8bit = True;
5826d522f475Smrg
5827d4fba8b9Smrg	if (testc != NULL) {
5828d4fba8b9Smrg	    /*
5829d4fba8b9Smrg	     * Check if the data fits in STRING.  Along the way, replace
5830d4fba8b9Smrg	     * control characters.
5831d4fba8b9Smrg	     */
5832d4fba8b9Smrg	    Char *lastc = (Char *) testc;
5833d4fba8b9Smrg	    while (*nextc != '\0') {
5834d4fba8b9Smrg		unsigned ch;
5835d4fba8b9Smrg		nextc = convertFromUTF8(nextc, &ch);
5836d4fba8b9Smrg		if (ch > 255) {
5837d4fba8b9Smrg		    ok8bit = False;
5838d4fba8b9Smrg		} else if (!IsLatin1(ch)) {
5839d4fba8b9Smrg		    ch = OnlyLatin1(ch);
5840d4fba8b9Smrg		}
5841d4fba8b9Smrg		*lastc++ = (Char) ch;
5842d4fba8b9Smrg	    }
5843d4fba8b9Smrg	    *lastc = '\0';
5844d4fba8b9Smrg	    if (ok8bit) {
5845d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
5846d4fba8b9Smrg		if (value != old_value)
5847d4fba8b9Smrg		    free(value);
5848d4fba8b9Smrg		value = testc;
5849d4fba8b9Smrg		titleIsUTF8 = False;
5850d4fba8b9Smrg	    } else {
5851d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
5852d4fba8b9Smrg		       "\t%s\n", value));
5853d4fba8b9Smrg		free(testc);
5854d4fba8b9Smrg		nextc = (Char *) value;
5855d4fba8b9Smrg		while (*nextc != '\0') {
5856d4fba8b9Smrg		    unsigned ch;
5857d4fba8b9Smrg		    Char *skip = convertFromUTF8(nextc, &ch);
5858d4fba8b9Smrg		    if (iswcntrl((wint_t) ch)) {
5859d4fba8b9Smrg			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
5860d4fba8b9Smrg		    }
5861d4fba8b9Smrg		    nextc = skip;
5862d4fba8b9Smrg		}
5863cd3331d0Smrg	    }
5864d522f475Smrg	}
5865d4fba8b9Smrg    } else
5866d4fba8b9Smrg#endif
5867d4fba8b9Smrg    {
5868d4fba8b9Smrg	Char *c1 = (Char *) value;
5869d4fba8b9Smrg
5870d4fba8b9Smrg	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
5871d4fba8b9Smrg	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
5872d4fba8b9Smrg	    *c1 = (Char) OnlyLatin1(*c1);
5873d4fba8b9Smrg	}
5874d4fba8b9Smrg    }
5875d4fba8b9Smrg
5876d4fba8b9Smrg    my_attr = x_strdup(attribute);
5877d4fba8b9Smrg
5878d4fba8b9Smrg    ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
5879d522f475Smrg
5880d522f475Smrg#if OPT_WIDE_CHARS
5881d4fba8b9Smrg    /*
5882d4fba8b9Smrg     * If we're running in UTF-8 mode, and have not been told that the
5883d4fba8b9Smrg     * title string is in UTF-8, it is likely that non-ASCII text in the
5884d4fba8b9Smrg     * string will be rejected because it is not printable in the current
5885d4fba8b9Smrg     * locale.  So we convert it to UTF-8, allowing the X library to
5886d4fba8b9Smrg     * convert it back.
5887d4fba8b9Smrg     */
5888d4fba8b9Smrg    TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
5889d4fba8b9Smrg    if (xtermEnvUTF8() && !titleIsUTF8) {
5890d4fba8b9Smrg	size_t limit = strlen(value);
5891d4fba8b9Smrg	Char *c1 = (Char *) value;
5892d4fba8b9Smrg	int n;
5893cd3331d0Smrg
5894d4fba8b9Smrg	for (n = 0; c1[n] != '\0'; ++n) {
5895d4fba8b9Smrg	    if (c1[n] > 127) {
5896d4fba8b9Smrg		Char *converted;
5897d4fba8b9Smrg		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
5898d4fba8b9Smrg		    Char *temp = converted;
5899d4fba8b9Smrg		    while (*c1 != 0) {
5900d4fba8b9Smrg			temp = convertToUTF8(temp, *c1++);
5901d522f475Smrg		    }
5902d4fba8b9Smrg		    *temp = 0;
5903d4fba8b9Smrg		    if (value != old_value)
5904d4fba8b9Smrg			free(value);
5905d4fba8b9Smrg		    value = (char *) converted;
5906d4fba8b9Smrg		    ReportIcons(("...converted{%s}\n", value));
5907d522f475Smrg		}
5908d4fba8b9Smrg		break;
5909d522f475Smrg	    }
5910d522f475Smrg	}
5911d4fba8b9Smrg    }
5912d522f475Smrg#endif
5913d522f475Smrg
5914d522f475Smrg#if OPT_SAME_NAME
5915d4fba8b9Smrg    /* If the attribute isn't going to change, then don't bother... */
5916d4fba8b9Smrg    if (resource.sameName) {
5917d4fba8b9Smrg	char *buf = 0;
5918d4fba8b9Smrg	XtSetArg(args[0], my_attr, &buf);
5919d4fba8b9Smrg	XtGetValues(top, args, 1);
5920d4fba8b9Smrg	TRACE(("...comparing{%s}\n", NonNull(buf)));
5921d4fba8b9Smrg	if (buf != 0 && strcmp(value, buf) == 0)
5922d4fba8b9Smrg	    changed = False;
5923d4fba8b9Smrg    }
5924d522f475Smrg#endif /* OPT_SAME_NAME */
5925d522f475Smrg
5926d4fba8b9Smrg    if (changed) {
5927d4fba8b9Smrg	ReportIcons(("...updating %s\n", my_attr));
5928d4fba8b9Smrg	ReportIcons(("...value is %s\n", value));
5929d4fba8b9Smrg	XtSetArg(args[0], my_attr, value);
5930d4fba8b9Smrg	XtSetValues(top, args, 1);
5931d4fba8b9Smrg    }
5932d522f475Smrg#if OPT_WIDE_CHARS
5933d4fba8b9Smrg    if (xtermEnvUTF8()) {
5934d4fba8b9Smrg	Display *dpy = XtDisplay(xw);
5935d4fba8b9Smrg	const char *propname = (!strcmp(my_attr, XtNtitle)
5936d4fba8b9Smrg				? "_NET_WM_NAME"
5937d4fba8b9Smrg				: "_NET_WM_ICON_NAME");
5938d4fba8b9Smrg	Atom my_atom = XInternAtom(dpy, propname, False);
5939d4fba8b9Smrg
5940d4fba8b9Smrg	if (my_atom != None) {
5941d4fba8b9Smrg	    changed = True;
5942d4fba8b9Smrg
5943d4fba8b9Smrg	    if (IsSetUtf8Title(xw)) {
5944d4fba8b9Smrg#if OPT_SAME_NAME
5945d4fba8b9Smrg		if (resource.sameName) {
5946d4fba8b9Smrg		    Atom actual_type;
5947d4fba8b9Smrg		    Atom requested_type = XA_UTF8_STRING(dpy);
5948d4fba8b9Smrg		    int actual_format = 0;
5949d4fba8b9Smrg		    long long_length = 1024;
5950d4fba8b9Smrg		    unsigned long nitems = 0;
5951d4fba8b9Smrg		    unsigned long bytes_after = 0;
5952d4fba8b9Smrg		    unsigned char *prop = 0;
5953d4fba8b9Smrg
5954d4fba8b9Smrg		    if (xtermGetWinProp(dpy,
5955d4fba8b9Smrg					VShellWindow(xw),
5956d4fba8b9Smrg					my_atom,
5957d4fba8b9Smrg					0L,
5958d4fba8b9Smrg					long_length,
5959d4fba8b9Smrg					requested_type,
5960d4fba8b9Smrg					&actual_type,
5961d4fba8b9Smrg					&actual_format,
5962d4fba8b9Smrg					&nitems,
5963d4fba8b9Smrg					&bytes_after,
596450027b5bSmrg					&prop)) {
596550027b5bSmrg			if (actual_type == requested_type
596650027b5bSmrg			    && actual_format == 8
596750027b5bSmrg			    && prop != 0
596850027b5bSmrg			    && nitems == strlen(value)
596950027b5bSmrg			    && memcmp(value, prop, nitems) == 0) {
597050027b5bSmrg			    changed = False;
597150027b5bSmrg			}
597250027b5bSmrg			XFree(prop);
5973cd3331d0Smrg		    }
5974cd3331d0Smrg		}
5975d4fba8b9Smrg#endif /* OPT_SAME_NAME */
5976d4fba8b9Smrg		if (changed) {
5977d4fba8b9Smrg		    ReportIcons(("...updating %s\n", propname));
5978d4fba8b9Smrg		    ReportIcons(("...value is %s\n", value));
5979d4fba8b9Smrg		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
5980d4fba8b9Smrg				    XA_UTF8_STRING(dpy), 8,
5981d4fba8b9Smrg				    PropModeReplace,
5982d4fba8b9Smrg				    (Char *) value,
5983d4fba8b9Smrg				    (int) strlen(value));
5984d4fba8b9Smrg		}
5985d4fba8b9Smrg	    } else {
5986d4fba8b9Smrg		ReportIcons(("...deleting %s\n", propname));
5987d4fba8b9Smrg		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5988d522f475Smrg	    }
5989d522f475Smrg	}
5990d522f475Smrg    }
5991d4fba8b9Smrg#endif
5992d4fba8b9Smrg    if (value != old_value) {
59933367019cSmrg	free(value);
59943367019cSmrg    }
59953367019cSmrg    free(my_attr);
59963367019cSmrg
5997cd3331d0Smrg    return;
5998d522f475Smrg}
5999d522f475Smrg
6000d522f475Smrgvoid
6001b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
6002d522f475Smrg{
6003cd3331d0Smrg    if (name == 0) {
60043367019cSmrg	name = emptyString;
60053367019cSmrg    }
60063367019cSmrg    if (!showZIconBeep(xw, name))
6007b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
6008d522f475Smrg}
6009d522f475Smrg
6010d522f475Smrgvoid
6011b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
6012d522f475Smrg{
6013b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
6014d522f475Smrg}
6015d522f475Smrg
6016712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
6017d522f475Smrg
6018d522f475Smrgvoid
6019d522f475SmrgChangeXprop(char *buf)
6020d522f475Smrg{
6021d522f475Smrg    Display *dpy = XtDisplay(toplevel);
6022d522f475Smrg    Window w = XtWindow(toplevel);
6023d522f475Smrg    XTextProperty text_prop;
6024d522f475Smrg    Atom aprop;
6025d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
6026d522f475Smrg
6027d522f475Smrg    if (pchEndPropName)
6028d522f475Smrg	*pchEndPropName = '\0';
6029d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
6030d522f475Smrg    if (pchEndPropName == NULL) {
6031d522f475Smrg	/* no "=value" given, so delete the property */
6032d522f475Smrg	XDeleteProperty(dpy, w, aprop);
6033d522f475Smrg    } else {
6034d522f475Smrg	text_prop.value = pchEndPropName + 1;
6035d522f475Smrg	text_prop.encoding = XA_STRING;
6036d522f475Smrg	text_prop.format = 8;
6037d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
6038d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
6039d522f475Smrg    }
6040d522f475Smrg}
6041d522f475Smrg
6042d522f475Smrg/***====================================================================***/
6043d522f475Smrg
6044d522f475Smrg/*
6045d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
6046d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
6047d522f475Smrg */
6048d522f475Smrgvoid
60499a64e1c5SmrgReverseOldColors(XtermWidget xw)
6050d522f475Smrg{
60519a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
6052d522f475Smrg    Pixel tmpPix;
6053d522f475Smrg    char *tmpName;
6054d522f475Smrg
6055d522f475Smrg    if (pOld) {
6056d4fba8b9Smrg	/* change text cursor, if necessary */
6057d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
6058d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
6059d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
60609a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
6061d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
6062d522f475Smrg	    }
6063d522f475Smrg	    if (pOld->names[TEXT_BG]) {
6064d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
6065d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
6066d522f475Smrg		}
6067d522f475Smrg	    }
6068d522f475Smrg	}
6069d522f475Smrg
6070d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
6071d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
6072d522f475Smrg
6073d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
6074d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
6075d522f475Smrg
6076d522f475Smrg#if OPT_TEK4014
6077d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
6078d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
6079d522f475Smrg#endif
6080d4fba8b9Smrg	FreeMarkGCs(xw);
6081d522f475Smrg    }
6082d522f475Smrg    return;
6083d522f475Smrg}
6084d522f475Smrg
6085d522f475SmrgBool
6086d522f475SmrgAllocateTermColor(XtermWidget xw,
6087d522f475Smrg		  ScrnColors * pNew,
6088d522f475Smrg		  int ndx,
6089cd3331d0Smrg		  const char *name,
6090cd3331d0Smrg		  Bool always)
6091d522f475Smrg{
6092cd3331d0Smrg    Bool result = False;
6093d522f475Smrg
6094cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
6095cd3331d0Smrg	XColor def;
6096cd3331d0Smrg	char *newName;
6097cd3331d0Smrg
6098712a7ff4Smrg	result = True;
6099712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
6100712a7ff4Smrg	    def.pixel = xw->old_foreground;
6101712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
6102712a7ff4Smrg	    def.pixel = xw->old_background;
61033367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
6104712a7ff4Smrg	    result = False;
6105712a7ff4Smrg	}
6106712a7ff4Smrg
6107712a7ff4Smrg	if (result
6108cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
6109712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
6110cd3331d0Smrg		free(pNew->names[ndx]);
6111712a7ff4Smrg	    }
6112cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
6113cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
6114712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
6115712a7ff4Smrg		   ndx, newName, def.pixel));
6116cd3331d0Smrg	} else {
6117cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
6118712a7ff4Smrg	    result = False;
6119cd3331d0Smrg	}
6120cd3331d0Smrg    }
6121cd3331d0Smrg    return result;
6122d522f475Smrg}
6123d522f475Smrg/***====================================================================***/
6124d522f475Smrg
6125d522f475Smrg/* ARGSUSED */
6126d522f475Smrgvoid
6127cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
6128d522f475Smrg{
61293367019cSmrg    if_DEBUG({
61303367019cSmrg	xtermWarning(s, a);
61313367019cSmrg    });
6132d522f475Smrg}
6133d522f475Smrg
6134d522f475Smrgconst char *
6135d522f475SmrgSysErrorMsg(int code)
6136d522f475Smrg{
613794644356Smrg    static const char unknown[] = "unknown error";
6138d4fba8b9Smrg    const char *s = strerror(code);
6139d522f475Smrg    return s ? s : unknown;
6140d522f475Smrg}
6141d522f475Smrg
6142d522f475Smrgconst char *
6143d522f475SmrgSysReasonMsg(int code)
6144d522f475Smrg{
6145d522f475Smrg    /* *INDENT-OFF* */
6146d522f475Smrg    static const struct {
6147d522f475Smrg	int code;
6148d522f475Smrg	const char *name;
6149d522f475Smrg    } table[] = {
6150d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
6151d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
6152d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
6153d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
6154d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
6155d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
6156d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
6157d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
6158d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
6159d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
6160d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
6161d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
6162d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
6163d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
6164d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
6165d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
6166d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
6167d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
6168d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
6169d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
6170d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6171d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
6172d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
6173d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6174d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6175d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6176d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
6177d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6178d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6179d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
6180d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6181d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6182d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6183d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
6184d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6185d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6186d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6187d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6188d522f475Smrg    };
6189d522f475Smrg    /* *INDENT-ON* */
6190d522f475Smrg
6191d522f475Smrg    Cardinal n;
6192d522f475Smrg    const char *result = "?";
6193d522f475Smrg
6194d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
6195d522f475Smrg	if (code == table[n].code) {
6196d522f475Smrg	    result = table[n].name;
6197d522f475Smrg	    break;
6198d522f475Smrg	}
6199d522f475Smrg    }
6200d522f475Smrg    return result;
6201d522f475Smrg}
6202d522f475Smrg
6203d522f475Smrgvoid
6204d522f475SmrgSysError(int code)
6205d522f475Smrg{
6206d522f475Smrg    int oerrno = errno;
6207d522f475Smrg
6208c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6209d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6210d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6211d522f475Smrg
6212d522f475Smrg    Cleanup(code);
6213d522f475Smrg}
6214d522f475Smrg
6215d522f475Smrgvoid
62163367019cSmrgNormalExit(void)
6217d522f475Smrg{
6218d522f475Smrg    static Bool cleaning;
6219d522f475Smrg
6220d522f475Smrg    /*
6221d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
6222d522f475Smrg     */
62233367019cSmrg    if (cleaning) {
62243367019cSmrg	hold_screen = 0;
62253367019cSmrg	return;
62263367019cSmrg    }
6227d522f475Smrg
62283367019cSmrg    cleaning = True;
62293367019cSmrg    need_cleanup = False;
6230d522f475Smrg
62313367019cSmrg    if (hold_screen) {
62323367019cSmrg	hold_screen = 2;
62333367019cSmrg	while (hold_screen) {
6234d4fba8b9Smrg	    xtermFlushDbe(term);
6235d4fba8b9Smrg	    xevents(term);
6236d4fba8b9Smrg	    Sleep(EVENT_DELAY);
6237d522f475Smrg	}
62383367019cSmrg    }
6239d522f475Smrg#if OPT_SESSION_MGT
62403367019cSmrg    if (resource.sessionMgt) {
62413367019cSmrg	XtVaSetValues(toplevel,
62423367019cSmrg		      XtNjoinSession, False,
62433367019cSmrg		      (void *) 0);
6244d522f475Smrg    }
62453367019cSmrg#endif
62463367019cSmrg    Cleanup(0);
62473367019cSmrg}
62483367019cSmrg
6249d4fba8b9Smrg#if USE_DOUBLE_BUFFER
6250d4fba8b9Smrgvoid
6251d4fba8b9SmrgxtermFlushDbe(XtermWidget xw)
6252d4fba8b9Smrg{
6253d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
6254d4fba8b9Smrg    if (resource.buffered && screen->needSwap) {
6255d4fba8b9Smrg	XdbeSwapInfo swap;
6256d4fba8b9Smrg	swap.swap_window = VWindow(screen);
6257d4fba8b9Smrg	swap.swap_action = XdbeCopied;
6258d4fba8b9Smrg	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6259d4fba8b9Smrg	XFlush(XtDisplay(xw));
6260d4fba8b9Smrg	screen->needSwap = 0;
6261d4fba8b9Smrg	ScrollBarDrawThumb(xw, 2);
6262d4fba8b9Smrg	X_GETTIMEOFDAY(&screen->buffered_at);
6263d4fba8b9Smrg    }
6264d4fba8b9Smrg}
6265d4fba8b9Smrg
6266d4fba8b9Smrgvoid
6267d4fba8b9SmrgxtermTimedDbe(XtermWidget xw)
6268d4fba8b9Smrg{
6269d4fba8b9Smrg    if (resource.buffered) {
6270d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
6271d4fba8b9Smrg	struct timeval now;
6272d4fba8b9Smrg	long elapsed;
6273d4fba8b9Smrg	long limit = DbeMsecs(xw);
6274d4fba8b9Smrg
6275d4fba8b9Smrg	X_GETTIMEOFDAY(&now);
6276d4fba8b9Smrg	if (screen->buffered_at.tv_sec) {
6277d4fba8b9Smrg	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6278d4fba8b9Smrg		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6279d4fba8b9Smrg	} else {
6280d4fba8b9Smrg	    elapsed = limit;
6281d4fba8b9Smrg	}
6282d4fba8b9Smrg	if (elapsed >= limit) {
6283d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
6284d4fba8b9Smrg	    xtermFlushDbe(xw);
6285d4fba8b9Smrg	}
6286d4fba8b9Smrg    }
6287d4fba8b9Smrg}
6288d4fba8b9Smrg#endif
6289d4fba8b9Smrg
62903367019cSmrg/*
62913367019cSmrg * cleanup by sending SIGHUP to client processes
62923367019cSmrg */
62933367019cSmrgvoid
62943367019cSmrgCleanup(int code)
62953367019cSmrg{
62963367019cSmrg    TScreen *screen = TScreenOf(term);
62973367019cSmrg
62983367019cSmrg    TRACE(("Cleanup %d\n", code));
6299d522f475Smrg
6300d522f475Smrg    if (screen->pid > 1) {
6301d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
6302d522f475Smrg    }
6303d522f475Smrg    Exit(code);
6304d522f475Smrg}
6305d522f475Smrg
6306fa3f02f3Smrg#ifndef S_IXOTH
6307fa3f02f3Smrg#define S_IXOTH 1
6308fa3f02f3Smrg#endif
6309fa3f02f3Smrg
6310fa3f02f3SmrgBoolean
6311fa3f02f3SmrgvalidProgram(const char *pathname)
6312fa3f02f3Smrg{
6313fa3f02f3Smrg    Boolean result = False;
6314fa3f02f3Smrg    struct stat sb;
6315fa3f02f3Smrg
6316fa3f02f3Smrg    if (!IsEmpty(pathname)
6317fa3f02f3Smrg	&& *pathname == '/'
6318fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
6319fa3f02f3Smrg	&& stat(pathname, &sb) == 0
6320fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
6321fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
6322fa3f02f3Smrg	result = True;
6323fa3f02f3Smrg    }
6324fa3f02f3Smrg    return result;
6325fa3f02f3Smrg}
6326fa3f02f3Smrg
6327d522f475Smrg#ifndef VMS
63283367019cSmrg#ifndef PATH_MAX
63293367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
63303367019cSmrg#endif
6331d522f475Smrgchar *
6332d522f475SmrgxtermFindShell(char *leaf, Bool warning)
6333d522f475Smrg{
63343367019cSmrg    char *s0;
6335d522f475Smrg    char *s;
6336d522f475Smrg    char *d;
6337d522f475Smrg    char *tmp;
6338d522f475Smrg    char *result = leaf;
63393367019cSmrg    Bool allocated = False;
6340d522f475Smrg
6341d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
63423367019cSmrg
63433367019cSmrg    if (!strncmp("./", result, (size_t) 2)
63443367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
63453367019cSmrg	size_t need = PATH_MAX;
63463367019cSmrg	size_t used = strlen(result) + 2;
63473367019cSmrg	char *buffer = malloc(used + need);
63483367019cSmrg	if (buffer != 0) {
63493367019cSmrg	    if (getcwd(buffer, need) != 0) {
63503367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
63513367019cSmrg		result = buffer;
63523367019cSmrg		allocated = True;
63533367019cSmrg	    } else {
63543367019cSmrg		free(buffer);
63553367019cSmrg	    }
63563367019cSmrg	}
63573367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
6358d522f475Smrg	/* find it in $PATH */
63593367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
63600d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
6361d522f475Smrg		Bool found = False;
6362d522f475Smrg		while (*s != '\0') {
6363d522f475Smrg		    strcpy(tmp, s);
6364d522f475Smrg		    for (d = tmp;; ++d) {
6365d522f475Smrg			if (*d == ':' || *d == '\0') {
6366d522f475Smrg			    int skip = (*d != '\0');
6367d522f475Smrg			    *d = '/';
6368d522f475Smrg			    strcpy(d + 1, leaf);
6369d522f475Smrg			    if (skip)
6370d522f475Smrg				++d;
6371d522f475Smrg			    s += (d - tmp);
6372fa3f02f3Smrg			    if (validProgram(tmp)) {
6373d522f475Smrg				result = x_strdup(tmp);
6374d522f475Smrg				found = True;
63753367019cSmrg				allocated = True;
6376d522f475Smrg			    }
6377d522f475Smrg			    break;
6378d522f475Smrg			}
6379d522f475Smrg		    }
6380d522f475Smrg		    if (found)
6381d522f475Smrg			break;
6382d522f475Smrg		}
6383d522f475Smrg		free(tmp);
6384d522f475Smrg	    }
63853367019cSmrg	    free(s0);
6386d522f475Smrg	}
6387d522f475Smrg    }
6388d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
6389fa3f02f3Smrg    if (!validProgram(result)) {
6390d522f475Smrg	if (warning)
63913367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
63923367019cSmrg	if (allocated)
63933367019cSmrg	    free(result);
6394d522f475Smrg	result = 0;
6395d522f475Smrg    }
63963367019cSmrg    /* be consistent, so that caller can always free the result */
63973367019cSmrg    if (result != 0 && !allocated)
63983367019cSmrg	result = x_strdup(result);
6399d522f475Smrg    return result;
6400d522f475Smrg}
6401d522f475Smrg#endif /* VMS */
6402d522f475Smrg
64030d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6404d522f475Smrg
64053367019cSmrg/*
64063367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
64073367019cSmrg * This could happen on some older machines due to the uneven standardization
64083367019cSmrg * process for the two functions.
64093367019cSmrg *
64103367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
64113367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
64123367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
64133367019cSmrg * could copy environ.
64143367019cSmrg */
64153367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
64163367019cSmrg#undef HAVE_PUTENV
64173367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
64183367019cSmrg#undef HAVE_UNSETENV
64193367019cSmrg#endif
64203367019cSmrg
6421d522f475Smrg/*
6422d522f475Smrg * copy the environment before Setenv'ing.
6423d522f475Smrg */
6424d522f475Smrgvoid
6425d522f475SmrgxtermCopyEnv(char **oldenv)
6426d522f475Smrg{
64273367019cSmrg#ifdef HAVE_PUTENV
64283367019cSmrg    (void) oldenv;
64293367019cSmrg#else
6430d522f475Smrg    unsigned size;
6431d522f475Smrg    char **newenv;
6432d522f475Smrg
6433d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
6434d522f475Smrg	;
6435d522f475Smrg    }
6436d522f475Smrg
6437d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
6438d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
6439d522f475Smrg    environ = newenv;
64403367019cSmrg#endif
64413367019cSmrg}
64423367019cSmrg
64433367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
64443367019cSmrgstatic int
64453367019cSmrgfindEnv(const char *var, int *lengthp)
64463367019cSmrg{
64473367019cSmrg    char *test;
64483367019cSmrg    int envindex = 0;
64493367019cSmrg    size_t len = strlen(var);
64503367019cSmrg    int found = -1;
64513367019cSmrg
64523367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
64533367019cSmrg
64543367019cSmrg    while ((test = environ[envindex]) != NULL) {
64553367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
64563367019cSmrg	    found = envindex;
64573367019cSmrg	    break;
64583367019cSmrg	}
64593367019cSmrg	envindex++;
64603367019cSmrg    }
64613367019cSmrg    *lengthp = envindex;
64623367019cSmrg    return found;
6463d522f475Smrg}
64643367019cSmrg#endif
6465d522f475Smrg
6466d522f475Smrg/*
6467d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6468d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
6469d522f475Smrg * This procedure assumes the memory for the first level of environ
6470d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
6471d522f475Smrg * to have to do a realloc().
6472d522f475Smrg */
6473d522f475Smrgvoid
6474cd3331d0SmrgxtermSetenv(const char *var, const char *value)
6475d522f475Smrg{
6476d522f475Smrg    if (value != 0) {
64773367019cSmrg#ifdef HAVE_PUTENV
64783367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
64793367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
64803367019cSmrg	if (both) {
64813367019cSmrg	    sprintf(both, "%s=%s", var, value);
64823367019cSmrg	    putenv(both);
64833367019cSmrg	}
64843367019cSmrg#else
6485d522f475Smrg	size_t len = strlen(var);
64863367019cSmrg	int envindex;
64873367019cSmrg	int found = findEnv(var, &envindex);
6488d522f475Smrg
6489d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6490d522f475Smrg
6491d522f475Smrg	if (found < 0) {
6492d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
6493d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
6494d522f475Smrg
6495d522f475Smrg	    if (need > have) {
6496d522f475Smrg		char **newenv;
6497d522f475Smrg		newenv = TypeMallocN(char *, need);
6498d522f475Smrg		if (newenv == 0) {
64993367019cSmrg		    xtermWarning("Cannot increase environment\n");
6500d522f475Smrg		    return;
6501d522f475Smrg		}
6502d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
6503d522f475Smrg		free(environ);
6504d522f475Smrg		environ = newenv;
6505d522f475Smrg	    }
6506d522f475Smrg
6507d522f475Smrg	    found = envindex;
6508d522f475Smrg	    environ[found + 1] = NULL;
6509d522f475Smrg	}
6510d522f475Smrg
6511d4fba8b9Smrg	environ[found] = malloc(2 + len + strlen(value));
6512d522f475Smrg	if (environ[found] == 0) {
65133367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
6514d522f475Smrg	    return;
6515d522f475Smrg	}
6516d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
65173367019cSmrg#endif
6518d522f475Smrg    }
6519d522f475Smrg}
6520d522f475Smrg
65213367019cSmrgvoid
65223367019cSmrgxtermUnsetenv(const char *var)
65233367019cSmrg{
65243367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
65253367019cSmrg#ifdef HAVE_UNSETENV
65263367019cSmrg    unsetenv(var);
65273367019cSmrg#else
65283367019cSmrg    {
65293367019cSmrg	int ignore;
65303367019cSmrg	int item = findEnv(var, &ignore);
65313367019cSmrg	if (item >= 0) {
65323367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
65333367019cSmrg		++item;
65343367019cSmrg	    }
65353367019cSmrg	}
65363367019cSmrg    }
65373367019cSmrg#endif
65383367019cSmrg}
65393367019cSmrg
6540d522f475Smrg/*ARGSUSED*/
6541d522f475Smrgint
65429a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
6543d522f475Smrg{
65443367019cSmrg    xtermWarning("warning, error event received:\n");
6545d4fba8b9Smrg    TRACE_X_ERR(d, ev);
6546d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6547d522f475Smrg    Exit(ERROR_XERROR);
6548d522f475Smrg    return 0;			/* appease the compiler */
6549d522f475Smrg}
6550d522f475Smrg
6551712a7ff4Smrgvoid
6552712a7ff4Smrgice_error(IceConn iceConn)
6553712a7ff4Smrg{
6554712a7ff4Smrg    (void) iceConn;
6555712a7ff4Smrg
65563367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
65573367019cSmrg		 (long) getpid(), errno);
6558712a7ff4Smrg
6559712a7ff4Smrg    Exit(ERROR_ICEERROR);
6560712a7ff4Smrg}
6561712a7ff4Smrg
6562d522f475Smrg/*ARGSUSED*/
6563d522f475Smrgint
6564fa3f02f3Smrgxioerror(Display *dpy)
6565d522f475Smrg{
6566d522f475Smrg    int the_error = errno;
6567d522f475Smrg
65683367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
65693367019cSmrg		 the_error, SysErrorMsg(the_error),
65703367019cSmrg		 DisplayString(dpy));
6571d522f475Smrg
6572d522f475Smrg    Exit(ERROR_XIOERROR);
6573d522f475Smrg    return 0;			/* appease the compiler */
6574d522f475Smrg}
6575d522f475Smrg
6576728ff447Schristosvoid
6577d522f475Smrgxt_error(String message)
6578d522f475Smrg{
65793367019cSmrg    xtermWarning("Xt error: %s\n", message);
6580d522f475Smrg
6581d522f475Smrg    /*
6582d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
6583d522f475Smrg     */
6584d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
65853367019cSmrg	xtermWarning("DISPLAY is not set\n");
6586d522f475Smrg    }
6587d522f475Smrg    exit(1);
6588d522f475Smrg}
6589d522f475Smrg
6590d522f475Smrgint
6591d522f475SmrgXStrCmp(char *s1, char *s2)
6592d522f475Smrg{
6593d522f475Smrg    if (s1 && s2)
6594d522f475Smrg	return (strcmp(s1, s2));
6595d522f475Smrg    if (s1 && *s1)
6596d522f475Smrg	return (1);
6597d522f475Smrg    if (s2 && *s2)
6598d522f475Smrg	return (-1);
6599d522f475Smrg    return (0);
6600d522f475Smrg}
6601d522f475Smrg
6602d522f475Smrg#if OPT_TEK4014
6603d522f475Smrgstatic void
6604fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
6605d522f475Smrg{
6606d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
6607d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
6608d522f475Smrg    XWithdrawWindow(dpy, w, scr);
6609d522f475Smrg    return;
6610d522f475Smrg}
6611d522f475Smrg#endif
6612d522f475Smrg
6613d522f475Smrgvoid
6614d522f475Smrgset_vt_visibility(Bool on)
6615d522f475Smrg{
6616c219fbebSmrg    XtermWidget xw = term;
6617c219fbebSmrg    TScreen *screen = TScreenOf(xw);
6618d522f475Smrg
6619d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
6620d522f475Smrg    if (on) {
6621c219fbebSmrg	if (!screen->Vshow && xw) {
6622c219fbebSmrg	    VTInit(xw);
6623c219fbebSmrg	    XtMapWidget(XtParent(xw));
6624d522f475Smrg#if OPT_TOOLBAR
6625d522f475Smrg	    /* we need both of these during initialization */
6626c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
6627d522f475Smrg	    ShowToolbar(resource.toolBar);
6628d522f475Smrg#endif
6629d522f475Smrg	    screen->Vshow = True;
6630d522f475Smrg	}
6631d522f475Smrg    }
6632d522f475Smrg#if OPT_TEK4014
6633d522f475Smrg    else {
6634c219fbebSmrg	if (screen->Vshow && xw) {
6635c219fbebSmrg	    withdraw_window(XtDisplay(xw),
6636c219fbebSmrg			    VShellWindow(xw),
6637c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
6638d522f475Smrg	    screen->Vshow = False;
6639d522f475Smrg	}
6640d522f475Smrg    }
6641d522f475Smrg    set_vthide_sensitivity();
6642d522f475Smrg    set_tekhide_sensitivity();
6643d522f475Smrg    update_vttekmode();
6644d522f475Smrg    update_tekshow();
6645d522f475Smrg    update_vtshow();
6646d522f475Smrg#endif
6647d522f475Smrg    return;
6648d522f475Smrg}
6649d522f475Smrg
6650d522f475Smrg#if OPT_TEK4014
6651d522f475Smrgvoid
6652d522f475Smrgset_tek_visibility(Bool on)
6653d522f475Smrg{
6654d4fba8b9Smrg    XtermWidget xw = term;
6655d4fba8b9Smrg
6656d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
6657d522f475Smrg
6658d522f475Smrg    if (on) {
6659d4fba8b9Smrg	if (!TEK4014_SHOWN(xw)) {
6660cd3331d0Smrg	    if (tekWidget == 0) {
6661cd3331d0Smrg		TekInit();	/* will exit on failure */
6662cd3331d0Smrg	    }
6663cd3331d0Smrg	    if (tekWidget != 0) {
6664cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
6665cd3331d0Smrg		XtRealizeWidget(tekParent);
6666cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
6667d522f475Smrg#if OPT_TOOLBAR
6668cd3331d0Smrg		/* we need both of these during initialization */
6669cd3331d0Smrg		XtMapWidget(tekParent);
6670cd3331d0Smrg		XtMapWidget(tekWidget);
6671d522f475Smrg#endif
6672cd3331d0Smrg		XtOverrideTranslations(tekParent,
6673cd3331d0Smrg				       XtParseTranslationTable
6674cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6675cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
6676cd3331d0Smrg				       XtWindow(tekParent),
6677cd3331d0Smrg				       &wm_delete_window, 1);
6678d4fba8b9Smrg		TEK4014_SHOWN(xw) = True;
6679cd3331d0Smrg	    }
6680d522f475Smrg	}
6681d522f475Smrg    } else {
6682d4fba8b9Smrg	if (TEK4014_SHOWN(xw) && tekWidget) {
6683d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
6684d522f475Smrg			    TShellWindow,
6685d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6686d4fba8b9Smrg	    TEK4014_SHOWN(xw) = False;
6687d522f475Smrg	}
6688d522f475Smrg    }
6689d522f475Smrg    set_tekhide_sensitivity();
6690d522f475Smrg    set_vthide_sensitivity();
6691d522f475Smrg    update_vtshow();
6692d522f475Smrg    update_tekshow();
6693d522f475Smrg    update_vttekmode();
6694d522f475Smrg    return;
6695d522f475Smrg}
6696d522f475Smrg
6697d522f475Smrgvoid
6698d522f475Smrgend_tek_mode(void)
6699d522f475Smrg{
6700cd3331d0Smrg    XtermWidget xw = term;
6701cd3331d0Smrg
6702cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
6703cd3331d0Smrg	FlushLog(xw);
6704dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
6705dfb07bc7Smrg	xtermSetWinSize(xw);
6706d522f475Smrg	longjmp(Tekend, 1);
6707d522f475Smrg    }
6708d522f475Smrg    return;
6709d522f475Smrg}
6710d522f475Smrg
6711d522f475Smrgvoid
6712d522f475Smrgend_vt_mode(void)
6713d522f475Smrg{
6714cd3331d0Smrg    XtermWidget xw = term;
6715cd3331d0Smrg
6716cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
6717cd3331d0Smrg	FlushLog(xw);
6718d4fba8b9Smrg	set_tek_visibility(True);
6719cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
6720dfb07bc7Smrg	TekSetWinSize(tekWidget);
6721d522f475Smrg	longjmp(VTend, 1);
6722d522f475Smrg    }
6723d522f475Smrg    return;
6724d522f475Smrg}
6725d522f475Smrg
6726d522f475Smrgvoid
6727d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
6728d522f475Smrg{
6729d522f475Smrg    if (tovt) {
6730d522f475Smrg	if (tekRefreshList)
6731d522f475Smrg	    TekRefresh(tekWidget);
6732d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
6733d522f475Smrg    } else {
6734d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
6735d522f475Smrg    }
6736d522f475Smrg}
6737d522f475Smrg
6738d522f475Smrgvoid
6739d522f475Smrghide_vt_window(void)
6740d522f475Smrg{
6741d522f475Smrg    set_vt_visibility(False);
6742d522f475Smrg    if (!TEK4014_ACTIVE(term))
6743d522f475Smrg	switch_modes(False);	/* switch to tek mode */
6744d522f475Smrg}
6745d522f475Smrg
6746d522f475Smrgvoid
6747d522f475Smrghide_tek_window(void)
6748d522f475Smrg{
6749d522f475Smrg    set_tek_visibility(False);
6750d522f475Smrg    tekRefreshList = (TekLink *) 0;
6751d522f475Smrg    if (TEK4014_ACTIVE(term))
6752d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
6753d522f475Smrg}
6754d522f475Smrg#endif /* OPT_TEK4014 */
6755d522f475Smrg
6756d522f475Smrgstatic const char *
6757d522f475Smrgskip_punct(const char *s)
6758d522f475Smrg{
6759d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
6760d522f475Smrg	++s;
6761d522f475Smrg    }
6762d522f475Smrg    return s;
6763d522f475Smrg}
6764d522f475Smrg
6765d522f475Smrgstatic int
6766d522f475Smrgcmp_options(const void *a, const void *b)
6767d522f475Smrg{
6768d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
6769d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
6770d522f475Smrg    return strcmp(s1, s2);
6771d522f475Smrg}
6772d522f475Smrg
6773d522f475Smrgstatic int
6774d522f475Smrgcmp_resources(const void *a, const void *b)
6775d522f475Smrg{
6776d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
6777d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
6778d522f475Smrg}
6779d522f475Smrg
6780d522f475SmrgXrmOptionDescRec *
6781d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
6782d522f475Smrg{
6783d522f475Smrg    static XrmOptionDescRec *res_array = 0;
6784d522f475Smrg
6785d522f475Smrg#ifdef NO_LEAKS
6786cd3331d0Smrg    if (descs == 0) {
6787d4fba8b9Smrg	FreeAndNull(res_array);
6788d522f475Smrg    } else
6789d522f475Smrg#endif
6790d522f475Smrg    if (res_array == 0) {
6791d522f475Smrg	Cardinal j;
6792d522f475Smrg
6793d522f475Smrg	/* make a sorted index to 'resources' */
6794d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
6795cd3331d0Smrg	if (res_array != 0) {
6796cd3331d0Smrg	    for (j = 0; j < res_count; j++)
6797cd3331d0Smrg		res_array[j] = descs[j];
6798cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
6799cd3331d0Smrg	}
6800d522f475Smrg    }
6801d522f475Smrg    return res_array;
6802d522f475Smrg}
6803d522f475Smrg
6804d522f475Smrg/*
6805d522f475Smrg * The first time this is called, construct sorted index to the main program's
6806d522f475Smrg * list of options, taking into account the on/off options which will be
6807d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
6808d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
6809d522f475Smrg */
6810d522f475SmrgOptionHelp *
6811d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
6812d522f475Smrg{
6813d522f475Smrg    static OptionHelp *opt_array = 0;
6814d522f475Smrg
6815d522f475Smrg#ifdef NO_LEAKS
6816d522f475Smrg    if (descs == 0 && opt_array != 0) {
6817d522f475Smrg	sortedOptDescs(descs, numDescs);
6818d4fba8b9Smrg	FreeAndNull(opt_array);
6819d522f475Smrg	return 0;
6820d522f475Smrg    } else if (options == 0 || descs == 0) {
6821d522f475Smrg	return 0;
6822d522f475Smrg    }
6823d522f475Smrg#endif
6824d522f475Smrg
6825d522f475Smrg    if (opt_array == 0) {
6826cd3331d0Smrg	size_t opt_count, j;
6827d522f475Smrg#if OPT_TRACE
6828d522f475Smrg	Cardinal k;
6829d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
6830d522f475Smrg	int code;
6831cd3331d0Smrg	const char *mesg;
6832d522f475Smrg#else
6833d522f475Smrg	(void) descs;
6834d522f475Smrg	(void) numDescs;
6835d522f475Smrg#endif
6836d522f475Smrg
6837d522f475Smrg	/* count 'options' and make a sorted index to it */
6838d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
6839d522f475Smrg	    ;
6840d522f475Smrg	}
6841d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
6842d522f475Smrg	for (j = 0; j < opt_count; j++)
6843d522f475Smrg	    opt_array[j] = options[j];
6844d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
6845d522f475Smrg
6846d522f475Smrg	/* supply the "turn on/off" strings if needed */
6847d522f475Smrg#if OPT_TRACE
6848d522f475Smrg	for (j = 0; j < opt_count; j++) {
6849712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
6850c219fbebSmrg		char temp[80];
6851cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
6852d522f475Smrg		for (k = 0; k < numDescs; ++k) {
6853cd3331d0Smrg		    const char *value = res_array[k].value;
6854d522f475Smrg		    if (res_array[k].option[0] == '-') {
6855d522f475Smrg			code = -1;
6856d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
6857d522f475Smrg			code = 1;
6858d522f475Smrg		    } else {
6859d522f475Smrg			code = 0;
6860d522f475Smrg		    }
68613367019cSmrg		    sprintf(temp, "%.*s",
68623367019cSmrg			    (int) sizeof(temp) - 2,
68633367019cSmrg			    opt_array[j].desc);
6864c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
6865d522f475Smrg			code = -code;
6866d522f475Smrg		    if (code != 0
6867d522f475Smrg			&& res_array[k].value != 0
6868d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
6869d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
6870d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
6871d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
6872d522f475Smrg			    mesg = "turn on/off";
6873d522f475Smrg			} else {
6874d522f475Smrg			    mesg = "turn off/on";
6875d522f475Smrg			}
6876d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
6877d522f475Smrg			       mesg,
6878d522f475Smrg			       res_array[k].option,
6879d522f475Smrg			       res_array[k].value,
6880d522f475Smrg			       opt_array[j].opt,
6881d522f475Smrg			       opt_array[j].desc));
6882d522f475Smrg			break;
6883d522f475Smrg		    }
6884d522f475Smrg		}
6885d522f475Smrg	    }
6886d522f475Smrg	}
6887d522f475Smrg#endif
6888d522f475Smrg    }
6889d522f475Smrg    return opt_array;
6890d522f475Smrg}
6891d522f475Smrg
6892d522f475Smrg/*
6893d522f475Smrg * Report the character-type locale that xterm was started in.
6894d522f475Smrg */
68953367019cSmrgString
6896d522f475SmrgxtermEnvLocale(void)
6897d522f475Smrg{
68983367019cSmrg    static String result;
6899d522f475Smrg
6900d522f475Smrg    if (result == 0) {
6901d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
6902cd3331d0Smrg	    result = x_strdup("C");
6903cd3331d0Smrg	} else {
6904cd3331d0Smrg	    result = x_strdup(result);
6905d522f475Smrg	}
6906d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
6907d522f475Smrg    }
6908d522f475Smrg    return result;
6909d522f475Smrg}
6910d522f475Smrg
6911d522f475Smrgchar *
6912d522f475SmrgxtermEnvEncoding(void)
6913d522f475Smrg{
6914d522f475Smrg    static char *result;
6915d522f475Smrg
6916d522f475Smrg    if (result == 0) {
6917d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6918d522f475Smrg	result = nl_langinfo(CODESET);
6919d522f475Smrg#else
6920d4fba8b9Smrg	const char *locale = xtermEnvLocale();
6921d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
6922d4fba8b9Smrg	    result = x_strdup("ASCII");
6923d522f475Smrg	} else {
6924d4fba8b9Smrg	    result = x_strdup("ISO-8859-1");
6925d522f475Smrg	}
6926d522f475Smrg#endif
6927d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
6928d522f475Smrg    }
6929d522f475Smrg    return result;
6930d522f475Smrg}
6931d522f475Smrg
6932d522f475Smrg#if OPT_WIDE_CHARS
6933d522f475Smrg/*
6934d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
6935d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
6936d522f475Smrg * various library calls.
6937d522f475Smrg */
6938d522f475SmrgBool
6939d522f475SmrgxtermEnvUTF8(void)
6940d522f475Smrg{
6941d522f475Smrg    static Bool init = False;
6942d522f475Smrg    static Bool result = False;
6943d522f475Smrg
6944d522f475Smrg    if (!init) {
6945d522f475Smrg	init = True;
6946d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6947d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
6948d522f475Smrg#else
6949fa3f02f3Smrg	{
6950fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
6951fa3f02f3Smrg	    int n;
6952fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
6953fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
6954fa3f02f3Smrg	    }
6955fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
6956fa3f02f3Smrg		result = True;
6957fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
6958fa3f02f3Smrg		result = True;
6959fa3f02f3Smrg	    free(locale);
6960fa3f02f3Smrg	}
6961d522f475Smrg#endif
6962d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
6963d522f475Smrg    }
6964d522f475Smrg    return result;
6965d522f475Smrg}
6966d522f475Smrg#endif /* OPT_WIDE_CHARS */
6967d522f475Smrg
6968b7c89284Ssnj/*
6969b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
6970b7c89284Ssnj */
6971b7c89284SsnjXtermWidget
6972b7c89284SsnjgetXtermWidget(Widget w)
6973b7c89284Ssnj{
6974b7c89284Ssnj    XtermWidget xw;
6975b7c89284Ssnj
6976b7c89284Ssnj    if (w == 0) {
6977b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
6978b7c89284Ssnj	if (!IsXtermWidget(xw)) {
6979b7c89284Ssnj	    xw = 0;
6980b7c89284Ssnj	}
6981b7c89284Ssnj    } else if (IsXtermWidget(w)) {
6982b7c89284Ssnj	xw = (XtermWidget) w;
6983b7c89284Ssnj    } else {
6984b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
6985b7c89284Ssnj    }
6986b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
6987b7c89284Ssnj    return xw;
6988b7c89284Ssnj}
69893367019cSmrg
69903367019cSmrg#if OPT_SESSION_MGT
6991636d5e9fSmrg
6992636d5e9fSmrg#if OPT_TRACE
6993636d5e9fSmrgstatic void
6994636d5e9fSmrgtrace_1_SM(const char *tag, String name)
6995636d5e9fSmrg{
6996636d5e9fSmrg    Arg args[1];
6997636d5e9fSmrg    char *buf = 0;
6998636d5e9fSmrg
6999636d5e9fSmrg    XtSetArg(args[0], name, &buf);
7000636d5e9fSmrg    XtGetValues(toplevel, args, 1);
7001636d5e9fSmrg
7002636d5e9fSmrg    if (strstr(name, "Path") || strstr(name, "Directory")) {
7003636d5e9fSmrg	TRACE(("%s %s: %s\n", tag, name, NonNull(buf)));
7004636d5e9fSmrg    } else if (strstr(name, "Command")) {
7005636d5e9fSmrg	if (buf != NULL) {
7006636d5e9fSmrg	    char **vec = (char **) (void *) buf;
7007636d5e9fSmrg	    int n;
7008636d5e9fSmrg	    TRACE(("%s %s:\n", tag, name));
7009636d5e9fSmrg	    for (n = 0; vec[n] != NULL; ++n) {
7010636d5e9fSmrg		TRACE((" arg[%d] = %s\n", n, vec[n]));
7011636d5e9fSmrg	    }
7012636d5e9fSmrg	} else {
7013636d5e9fSmrg	    TRACE(("%s %s: %p\n", tag, name, buf));
7014636d5e9fSmrg	}
7015636d5e9fSmrg    } else {
7016636d5e9fSmrg	TRACE(("%s %s: %p\n", tag, name, buf));
7017636d5e9fSmrg    }
7018636d5e9fSmrg}
7019636d5e9fSmrg
7020636d5e9fSmrgstatic void
7021636d5e9fSmrgtrace_SM_props(void)
7022636d5e9fSmrg{
7023636d5e9fSmrg    /* *INDENT-OFF* */
7024636d5e9fSmrg    static struct { String app, cls; } table[] = {
7025636d5e9fSmrg	{ XtNcurrentDirectory,	XtCCurrentDirectory },
7026636d5e9fSmrg	{ XtNdieCallback,	XtNdiscardCommand },
7027636d5e9fSmrg	{ XtCDiscardCommand,	XtNenvironment },
7028636d5e9fSmrg	{ XtCEnvironment,	XtNinteractCallback },
7029636d5e9fSmrg	{ XtNjoinSession,	XtCJoinSession },
7030636d5e9fSmrg	{ XtNprogramPath,	XtCProgramPath },
7031636d5e9fSmrg	{ XtNresignCommand,	XtCResignCommand },
7032636d5e9fSmrg	{ XtNrestartCommand,	XtCRestartCommand },
7033636d5e9fSmrg	{ XtNrestartStyle,	XtCRestartStyle },
7034636d5e9fSmrg	{ XtNsaveCallback,	XtNsaveCompleteCallback },
7035636d5e9fSmrg	{ XtNsessionID,		XtCSessionID },
7036636d5e9fSmrg	{ XtNshutdownCommand,	XtCShutdownCommand },
7037636d5e9fSmrg    };
7038636d5e9fSmrg    /* *INDENT-ON* */
7039636d5e9fSmrg    Cardinal n;
7040636d5e9fSmrg    TRACE(("Session properties:\n"));
7041636d5e9fSmrg    for (n = 0; n < XtNumber(table); ++n) {
7042636d5e9fSmrg	trace_1_SM("app", table[n].app);
7043636d5e9fSmrg	trace_1_SM("cls", table[n].cls);
7044636d5e9fSmrg    }
7045636d5e9fSmrg}
7046636d5e9fSmrg#define TRACE_SM_PROPS()	trace_SM_props()
7047636d5e9fSmrg#else
7048636d5e9fSmrg#define TRACE_SM_PROPS()	/* nothing */
7049636d5e9fSmrg#endif
7050636d5e9fSmrg
70513367019cSmrgstatic void
70523367019cSmrgdie_callback(Widget w GCC_UNUSED,
70533367019cSmrg	     XtPointer client_data GCC_UNUSED,
70543367019cSmrg	     XtPointer call_data GCC_UNUSED)
70553367019cSmrg{
7056c48a5815Smrg    TRACE(("die_callback client=%p, call=%p\n",
7057c48a5815Smrg	   (void *) client_data,
7058c48a5815Smrg	   (void *) call_data));
7059636d5e9fSmrg    TRACE_SM_PROPS();
70603367019cSmrg    NormalExit();
70613367019cSmrg}
70623367019cSmrg
70633367019cSmrgstatic void
70643367019cSmrgsave_callback(Widget w GCC_UNUSED,
70653367019cSmrg	      XtPointer client_data GCC_UNUSED,
70663367019cSmrg	      XtPointer call_data)
70673367019cSmrg{
70683367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
7069636d5e9fSmrg    TRACE(("save_callback:\n"));
7070636d5e9fSmrg    TRACE(("... save_type            <-%d\n", token->save_type));
7071636d5e9fSmrg    TRACE(("... interact_style       <-%d\n", token->interact_style));
7072636d5e9fSmrg    TRACE(("... shutdown             <-%s\n", BtoS(token->shutdown)));
7073636d5e9fSmrg    TRACE(("... fast                 <-%s\n", BtoS(token->fast)));
7074636d5e9fSmrg    TRACE(("... cancel_shutdown      <-%s\n", BtoS(token->cancel_shutdown)));
7075636d5e9fSmrg    TRACE(("... phase                <-%d\n", token->phase));
7076636d5e9fSmrg    TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type));
7077636d5e9fSmrg    TRACE(("... request_cancel       ->%s\n", BtoS(token->request_cancel)));
7078636d5e9fSmrg    TRACE(("... request_next_phase   ->%s\n", BtoS(token->request_next_phase)));
7079636d5e9fSmrg    TRACE(("... save_success         ->%s\n", BtoS(token->save_success)));
7080636d5e9fSmrg    xtermUpdateRestartCommand(term);
7081636d5e9fSmrg    /* we have nothing more to save */
70823367019cSmrg    token->save_success = True;
70833367019cSmrg}
70843367019cSmrg
70853367019cSmrgstatic void
70863367019cSmrgicewatch(IceConn iceConn,
70873367019cSmrg	 IcePointer clientData GCC_UNUSED,
70883367019cSmrg	 Bool opening,
70893367019cSmrg	 IcePointer * watchData GCC_UNUSED)
70903367019cSmrg{
70913367019cSmrg    if (opening) {
70923367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
70933367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
70943367019cSmrg    } else {
70953367019cSmrg	ice_fd = -1;
70963367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
70973367019cSmrg    }
70983367019cSmrg}
70993367019cSmrg
71003367019cSmrgvoid
71013367019cSmrgxtermOpenSession(void)
71023367019cSmrg{
71033367019cSmrg    if (resource.sessionMgt) {
71043367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
71053367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
71063367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
7107636d5e9fSmrg
7108636d5e9fSmrg	TRACE_SM_PROPS();
71093367019cSmrg    }
71103367019cSmrg}
71113367019cSmrg
71123367019cSmrgvoid
71133367019cSmrgxtermCloseSession(void)
71143367019cSmrg{
71153367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
71163367019cSmrg}
7117636d5e9fSmrg
7118636d5e9fSmrgtypedef enum {
7119636d5e9fSmrg    B_ARG = 0,
7120636d5e9fSmrg    I_ARG,
7121636d5e9fSmrg    D_ARG,
7122636d5e9fSmrg    S_ARG
7123636d5e9fSmrg} ParamType;
7124636d5e9fSmrg
7125636d5e9fSmrg#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) }
7126636d5e9fSmrg#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) }
7127636d5e9fSmrg#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) }
7128636d5e9fSmrg#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) }
7129636d5e9fSmrg
7130636d5e9fSmrgtypedef struct {
7131636d5e9fSmrg    const char name[30];
7132636d5e9fSmrg    ParamType type;
7133636d5e9fSmrg    Cardinal offset;
7134636d5e9fSmrg} FontParams;
7135636d5e9fSmrg
7136636d5e9fSmrg/* *INDENT-OFF* */
7137636d5e9fSmrgstatic const FontParams fontParams[] = {
7138636d5e9fSmrg    Iarg(XtNinitialFont,     screen.menu_font_number),	/* "-fc" */
7139636d5e9fSmrg    Barg(XtNallowBoldFonts,  screen.allowBoldFonts),	/* menu */
7140636d5e9fSmrg#if OPT_BOX_CHARS
7141636d5e9fSmrg    Barg(XtNforceBoxChars,   screen.force_box_chars),	/* "-fbx" */
7142636d5e9fSmrg    Barg(XtNforcePackedFont, screen.force_packed),	/* menu */
7143636d5e9fSmrg#endif
7144636d5e9fSmrg#if OPT_DEC_CHRSET
7145636d5e9fSmrg    Barg(XtNfontDoublesize,  screen.font_doublesize),	/* menu */
7146636d5e9fSmrg#endif
7147636d5e9fSmrg#if OPT_WIDE_CHARS
7148636d5e9fSmrg    Barg(XtNutf8Fonts,       screen.utf8_fonts),	/* menu */
7149636d5e9fSmrg#endif
7150636d5e9fSmrg#if OPT_RENDERFONT
7151636d5e9fSmrg    Darg(XtNfaceSize,        misc.face_size[0]),	/* "-fs" */
7152636d5e9fSmrg    Sarg(XtNfaceName,        misc.default_xft.f_n),	/* "-fa" */
7153636d5e9fSmrg    Sarg(XtNrenderFont,      misc.render_font_s),	/* (resource) */
7154636d5e9fSmrg#endif
7155636d5e9fSmrg};
7156636d5e9fSmrg/* *INDENT-ON* */
7157636d5e9fSmrg
7158636d5e9fSmrg#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2)
7159636d5e9fSmrg#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset)
7160636d5e9fSmrg
7161636d5e9fSmrg/*
7162636d5e9fSmrg * If no widget is given, no value is used.
7163636d5e9fSmrg */
7164636d5e9fSmrgstatic char *
7165636d5e9fSmrgformatFontParam(char *result, XtermWidget xw, const FontParams * parameter)
7166636d5e9fSmrg{
7167636d5e9fSmrg    sprintf(result, "%s*%s:", ProgramName, parameter->name);
7168636d5e9fSmrg    if (xw != None) {
7169636d5e9fSmrg	char *next = result + strlen(result);
7170636d5e9fSmrg	switch (parameter->type) {
7171636d5e9fSmrg	case B_ARG:
7172636d5e9fSmrg	    sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset)
7173636d5e9fSmrg		    ? "true"
7174636d5e9fSmrg		    : "false");
7175636d5e9fSmrg	    break;
7176636d5e9fSmrg	case I_ARG:
7177636d5e9fSmrg	    sprintf(next, "%d", TypedPtr(int));
7178636d5e9fSmrg	    break;
7179636d5e9fSmrg	case D_ARG:
7180636d5e9fSmrg	    sprintf(next, "%.1f", TypedPtr(float));
7181636d5e9fSmrg	    break;
7182636d5e9fSmrg	case S_ARG:
7183636d5e9fSmrg	    strcpy(next, TypedPtr(char *));
7184636d5e9fSmrg#if OPT_RENDERFONT
7185636d5e9fSmrg	    if (!strcmp(parameter->name, XtNfaceName)) {
7186636d5e9fSmrg		if (IsEmpty(next)
7187636d5e9fSmrg		    && xw->work.render_font) {
7188636d5e9fSmrg		    strcpy(next, DEFFACENAME_AUTO);
7189636d5e9fSmrg		}
7190636d5e9fSmrg	    } else if (!strcmp(parameter->name, XtNrenderFont)) {
7191636d5e9fSmrg		if (xw->work.render_font == erDefault
7192636d5e9fSmrg		    && IsEmpty(xw->misc.default_xft.f_n)) {
7193636d5e9fSmrg		    strcpy(next, "DefaultOff");
7194636d5e9fSmrg		}
7195636d5e9fSmrg	    }
7196636d5e9fSmrg#endif
7197636d5e9fSmrg	    break;
7198636d5e9fSmrg	}
7199636d5e9fSmrg    }
7200636d5e9fSmrg    return result;
7201636d5e9fSmrg}
7202636d5e9fSmrg
7203636d5e9fSmrg#if OPT_TRACE
7204636d5e9fSmrgstatic void
7205636d5e9fSmrgdumpFontParams(XtermWidget xw)
7206636d5e9fSmrg{
7207636d5e9fSmrg    char buffer[1024];
7208636d5e9fSmrg    Cardinal n;
7209636d5e9fSmrg
7210636d5e9fSmrg    TRACE(("FontParams:\n"));
7211636d5e9fSmrg    for (n = 0; n < XtNumber(fontParams); ++n) {
7212636d5e9fSmrg	TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n)));
7213636d5e9fSmrg    }
7214636d5e9fSmrg}
7215636d5e9fSmrg#else
7216636d5e9fSmrg#define dumpFontParams(xw)	/* nothing */
7217636d5e9fSmrg#endif
7218636d5e9fSmrg
7219636d5e9fSmrgstatic Boolean
7220636d5e9fSmrgfindFontParams(int argc, char **argv)
7221636d5e9fSmrg{
7222636d5e9fSmrg    Boolean result = False;
7223636d5e9fSmrg
7224636d5e9fSmrg    if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) {
7225636d5e9fSmrg	int n;
7226636d5e9fSmrg
7227636d5e9fSmrg	for (n = 0; n < RESTART_PARAMS; ++n) {
7228636d5e9fSmrg	    int my_index = argc - restart_params - n - 1;
7229636d5e9fSmrg	    int my_param = (RESTART_PARAMS - n - 1) / 2;
7230636d5e9fSmrg	    char *actual = argv[my_index];
7231636d5e9fSmrg	    char expect[1024];
7232636d5e9fSmrg	    Boolean value = (Boolean) ((n % 2) == 0);
7233636d5e9fSmrg
7234636d5e9fSmrg	    result = False;
7235636d5e9fSmrg	    TRACE(("...index: %d\n", my_index));
7236636d5e9fSmrg	    TRACE(("...param: %d\n", my_param));
7237636d5e9fSmrg	    TRACE(("...actual %s\n", actual));
7238636d5e9fSmrg	    if (IsEmpty(actual))
7239636d5e9fSmrg		break;
7240636d5e9fSmrg
7241636d5e9fSmrg	    if (value) {
7242636d5e9fSmrg		formatFontParam(expect, None, fontParams + my_param);
7243636d5e9fSmrg	    } else {
7244636d5e9fSmrg		strcpy(expect, "-xrm");
7245636d5e9fSmrg	    }
7246636d5e9fSmrg
7247636d5e9fSmrg	    TRACE(("...expect %s\n", expect));
7248636d5e9fSmrg
7249636d5e9fSmrg	    if (value) {
7250636d5e9fSmrg		if (strlen(expect) >= strlen(actual))
7251636d5e9fSmrg		    break;
7252636d5e9fSmrg		if (strncmp(expect, actual, strlen(expect)))
7253636d5e9fSmrg		    break;
7254636d5e9fSmrg	    } else {
7255636d5e9fSmrg		if (strcmp(actual, expect))
7256636d5e9fSmrg		    break;
7257636d5e9fSmrg	    }
7258636d5e9fSmrg	    TRACE(("fixme/ok:%d\n", n));
7259636d5e9fSmrg	    result = True;
7260636d5e9fSmrg	}
7261636d5e9fSmrg	TRACE(("findFontParams: %s (tested %d of %d parameters)\n",
7262636d5e9fSmrg	       BtoS(result), n + 1, RESTART_PARAMS));
7263636d5e9fSmrg    }
7264636d5e9fSmrg    return result;
7265636d5e9fSmrg}
7266636d5e9fSmrg
7267636d5e9fSmrgstatic int
7268c48a5815SmrginsertFontParams(XtermWidget xw, int *targetp, Bool first)
7269636d5e9fSmrg{
7270636d5e9fSmrg    int changed = 0;
7271636d5e9fSmrg    int n;
7272636d5e9fSmrg    int target = *targetp;
7273636d5e9fSmrg    char buffer[1024];
7274636d5e9fSmrg    const char *option = "-xrm";
7275636d5e9fSmrg
7276636d5e9fSmrg    for (n = 0; n < (int) XtNumber(fontParams); ++n) {
7277636d5e9fSmrg	formatFontParam(buffer, xw, fontParams + n);
7278636d5e9fSmrg	TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer));
7279636d5e9fSmrg	if (restart_command[target] == NULL)
7280636d5e9fSmrg	    restart_command[target] = x_strdup(option);
7281636d5e9fSmrg	++target;
7282636d5e9fSmrg	if (first) {
7283636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7284636d5e9fSmrg	    ++changed;
7285636d5e9fSmrg	} else if (restart_command[target] == NULL
7286636d5e9fSmrg		   || strcmp(restart_command[target], buffer)) {
7287636d5e9fSmrg	    free(restart_command[target]);
7288636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7289636d5e9fSmrg	    ++changed;
7290636d5e9fSmrg	}
7291636d5e9fSmrg	++target;
7292636d5e9fSmrg    }
7293636d5e9fSmrg    *targetp = target;
7294636d5e9fSmrg    return changed;
7295636d5e9fSmrg}
7296636d5e9fSmrg
7297636d5e9fSmrgvoid
7298636d5e9fSmrgxtermUpdateRestartCommand(XtermWidget xw)
7299636d5e9fSmrg{
7300636d5e9fSmrg    if (resource.sessionMgt) {
7301636d5e9fSmrg	Arg args[1];
7302636d5e9fSmrg	char **argv = 0;
7303636d5e9fSmrg
7304636d5e9fSmrg	XtSetArg(args[0], XtNrestartCommand, &argv);
7305636d5e9fSmrg	XtGetValues(toplevel, args, 1);
7306636d5e9fSmrg	if (argv != NULL) {
7307636d5e9fSmrg	    static int my_params = 0;
7308636d5e9fSmrg
7309636d5e9fSmrg	    int changes = 0;
7310636d5e9fSmrg	    Boolean first = False;
7311636d5e9fSmrg	    int argc;
7312636d5e9fSmrg	    int want;
7313636d5e9fSmrg	    int source, target;
7314636d5e9fSmrg
7315636d5e9fSmrg	    TRACE(("xtermUpdateRestartCommand\n"));
7316636d5e9fSmrg	    dumpFontParams(xw);
7317636d5e9fSmrg	    for (argc = 0; argv[argc] != NULL; ++argc) {
7318636d5e9fSmrg		TRACE((" arg[%d] = %s\n", argc, argv[argc]));
7319636d5e9fSmrg		;
7320636d5e9fSmrg	    }
7321636d5e9fSmrg	    want = argc - (restart_params + RESTART_PARAMS);
7322636d5e9fSmrg
7323636d5e9fSmrg	    TRACE((" argc:           %d\n", argc));
7324636d5e9fSmrg	    TRACE((" restart_params: %d\n", restart_params));
7325636d5e9fSmrg	    TRACE((" want to insert: %d\n", want));
7326636d5e9fSmrg
7327636d5e9fSmrg	    /*
7328636d5e9fSmrg	     * If we already have the font-choice option, do not add it again.
7329636d5e9fSmrg	     */
7330636d5e9fSmrg	    if (findFontParams(argc, argv)) {
7331636d5e9fSmrg		my_params = (want);
7332636d5e9fSmrg	    } else {
7333636d5e9fSmrg		first = True;
7334636d5e9fSmrg		my_params = (argc - restart_params);
7335636d5e9fSmrg	    }
7336636d5e9fSmrg	    TRACE((" my_params:      %d\n", my_params));
7337636d5e9fSmrg
7338636d5e9fSmrg	    if (my_params > argc) {
7339636d5e9fSmrg		TRACE((" re-allocate restartCommand\n"));
7340636d5e9fSmrg		FreeAndNull(restart_command);
7341636d5e9fSmrg	    }
7342636d5e9fSmrg
7343636d5e9fSmrg	    if (restart_command == NULL) {
7344636d5e9fSmrg		int need = argc + RESTART_PARAMS + 1;
7345636d5e9fSmrg
7346636d5e9fSmrg		restart_command = TypeCallocN(char *, need);
7347636d5e9fSmrg
7348636d5e9fSmrg		TRACE(("..inserting font-parameters\n"));
7349636d5e9fSmrg		for (source = target = 0; source < argc; ++source) {
7350636d5e9fSmrg		    if (source == my_params) {
7351636d5e9fSmrg			changes += insertFontParams(xw, &target, first);
7352636d5e9fSmrg			if (!first) {
7353636d5e9fSmrg			    source += (RESTART_PARAMS - 1);
7354636d5e9fSmrg			    continue;
7355636d5e9fSmrg			}
7356636d5e9fSmrg		    }
7357636d5e9fSmrg		    if (argv[source] == NULL)
7358636d5e9fSmrg			break;
7359636d5e9fSmrg		    restart_command[target++] = x_strdup(argv[source]);
7360636d5e9fSmrg		}
7361636d5e9fSmrg		restart_command[target] = NULL;
7362636d5e9fSmrg	    } else {
7363636d5e9fSmrg		TRACE(("..replacing font-parameters\n"));
7364636d5e9fSmrg		target = my_params;
7365636d5e9fSmrg		changes += insertFontParams(xw, &target, first);
7366636d5e9fSmrg	    }
7367636d5e9fSmrg	    if (changes) {
7368636d5e9fSmrg		TRACE(("..%d parameters changed\n", changes));
7369636d5e9fSmrg		XtSetArg(args[0], XtNrestartCommand, restart_command);
7370636d5e9fSmrg		XtSetValues(toplevel, args, 1);
7371636d5e9fSmrg	    } else {
7372636d5e9fSmrg		TRACE(("..NO parameters changed\n"));
7373636d5e9fSmrg	    }
7374636d5e9fSmrg	}
7375636d5e9fSmrg	TRACE_SM_PROPS();
7376636d5e9fSmrg    }
7377636d5e9fSmrg}
73783367019cSmrg#endif /* OPT_SESSION_MGT */
73793367019cSmrg
73803367019cSmrgWidget
73813367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
73823367019cSmrg		     String my_class,
73833367019cSmrg		     XrmOptionDescRec * options,
73843367019cSmrg		     Cardinal num_options,
73853367019cSmrg		     int *argc_in_out,
7386d4fba8b9Smrg		     char **argv_in_out,
7387fa3f02f3Smrg		     String *fallback_resources,
73883367019cSmrg		     WidgetClass widget_class,
73893367019cSmrg		     ArgList args,
73903367019cSmrg		     Cardinal num_args)
73913367019cSmrg{
73923367019cSmrg    Widget result;
73933367019cSmrg
73943367019cSmrg    XtSetErrorHandler(xt_error);
73953367019cSmrg#if OPT_SESSION_MGT
73963367019cSmrg    result = XtOpenApplication(app_context_return,
73973367019cSmrg			       my_class,
73983367019cSmrg			       options,
73993367019cSmrg			       num_options,
74003367019cSmrg			       argc_in_out,
74013367019cSmrg			       argv_in_out,
74023367019cSmrg			       fallback_resources,
74033367019cSmrg			       widget_class,
74043367019cSmrg			       args,
74053367019cSmrg			       num_args);
74063367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
74073367019cSmrg#else
74089a64e1c5Smrg    (void) widget_class;
74099a64e1c5Smrg    (void) args;
74109a64e1c5Smrg    (void) num_args;
74113367019cSmrg    result = XtAppInitialize(app_context_return,
74123367019cSmrg			     my_class,
74133367019cSmrg			     options,
74143367019cSmrg			     num_options,
74153367019cSmrg			     argc_in_out,
74163367019cSmrg			     argv_in_out,
74173367019cSmrg			     fallback_resources,
74183367019cSmrg			     NULL, 0);
74193367019cSmrg#endif /* OPT_SESSION_MGT */
7420e8264990Smrg    XtSetErrorHandler(NULL);
74213367019cSmrg
74223367019cSmrg    return result;
74233367019cSmrg}
74243367019cSmrg
7425d4fba8b9Smrg/*
7426d4fba8b9Smrg * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
7427d4fba8b9Smrg * our own error-handler.
7428d4fba8b9Smrg */
7429d4fba8b9Smrg/* ARGSUSED */
7430d4fba8b9Smrgint
7431d4fba8b9Smrgignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
7432d4fba8b9Smrg{
7433d4fba8b9Smrg    return 1;
7434d4fba8b9Smrg}
7435d4fba8b9Smrg
74363367019cSmrgstatic int x11_errors;
74373367019cSmrg
74383367019cSmrgstatic int
74399a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
74403367019cSmrg{
74413367019cSmrg    (void) display;
74423367019cSmrg    (void) error_event;
74433367019cSmrg    ++x11_errors;
74443367019cSmrg    return 0;
74453367019cSmrg}
74463367019cSmrg
74473367019cSmrgBoolean
7448fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
74493367019cSmrg{
74503367019cSmrg    Boolean result = False;
74513367019cSmrg    Status code;
74523367019cSmrg
74533367019cSmrg    memset(attrs, 0, sizeof(*attrs));
74543367019cSmrg    if (win != None) {
74553367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
74563367019cSmrg	x11_errors = 0;
74573367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
74583367019cSmrg	XSetErrorHandler(save);
74593367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
74603367019cSmrg	if (result) {
74613367019cSmrg	    TRACE_WIN_ATTRS(attrs);
74623367019cSmrg	} else {
74633367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
74643367019cSmrg	}
74653367019cSmrg    }
74663367019cSmrg    return result;
74673367019cSmrg}
74683367019cSmrg
74693367019cSmrgBoolean
7470fa3f02f3SmrgxtermGetWinProp(Display *display,
74713367019cSmrg		Window win,
74723367019cSmrg		Atom property,
74733367019cSmrg		long long_offset,
74743367019cSmrg		long long_length,
74753367019cSmrg		Atom req_type,
74769a64e1c5Smrg		Atom *actual_type_return,
74773367019cSmrg		int *actual_format_return,
74783367019cSmrg		unsigned long *nitems_return,
74793367019cSmrg		unsigned long *bytes_after_return,
74803367019cSmrg		unsigned char **prop_return)
74813367019cSmrg{
7482d4fba8b9Smrg    Boolean result = False;
74833367019cSmrg
74843367019cSmrg    if (win != None) {
74853367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
74863367019cSmrg	x11_errors = 0;
74873367019cSmrg	if (XGetWindowProperty(display,
74883367019cSmrg			       win,
74893367019cSmrg			       property,
74903367019cSmrg			       long_offset,
74913367019cSmrg			       long_length,
74923367019cSmrg			       False,
74933367019cSmrg			       req_type,
74943367019cSmrg			       actual_type_return,
74953367019cSmrg			       actual_format_return,
74963367019cSmrg			       nitems_return,
74973367019cSmrg			       bytes_after_return,
74983367019cSmrg			       prop_return) == Success
74993367019cSmrg	    && x11_errors == 0) {
75003367019cSmrg	    result = True;
75013367019cSmrg	}
75023367019cSmrg	XSetErrorHandler(save);
75033367019cSmrg    }
75043367019cSmrg    return result;
75053367019cSmrg}
75063367019cSmrg
75073367019cSmrgvoid
75083367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
75093367019cSmrg{
75103367019cSmrg    Display *dpy = XtDisplay(toplevel);
75113367019cSmrg    XWindowAttributes attrs;
75123367019cSmrg
75133367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
75143367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
75153367019cSmrg	XtermWidget xw = term;
75163367019cSmrg	TScreen *screen = TScreenOf(xw);
75173367019cSmrg
75183367019cSmrg	XtRealizeWidget(toplevel);
75193367019cSmrg
75203367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
75213367019cSmrg	       XtWindow(toplevel),
75223367019cSmrg	       winToEmbedInto));
75233367019cSmrg	XReparentWindow(dpy,
75243367019cSmrg			XtWindow(toplevel),
75253367019cSmrg			winToEmbedInto, 0, 0);
75263367019cSmrg
75273367019cSmrg	screen->embed_high = (Dimension) attrs.height;
75283367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
75293367019cSmrg    }
75303367019cSmrg}
753194644356Smrg
753294644356Smrgvoid
753394644356Smrgfree_string(String value)
753494644356Smrg{
753594644356Smrg    free((void *) value);
753694644356Smrg}
7537dfb07bc7Smrg
7538dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
7539d4fba8b9Smrgint
754050027b5bSmrgupdate_winsize(TScreen *screen, int rows, int cols, int height, int width)
7541dfb07bc7Smrg{
7542d4fba8b9Smrg    int code = -1;
7543dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
7544d4fba8b9Smrg    static int last_rows = -1;
7545d4fba8b9Smrg    static int last_cols = -1;
7546636d5e9fSmrg    static int last_high = -1;
7547636d5e9fSmrg    static int last_wide = -1;
7548636d5e9fSmrg
7549636d5e9fSmrg    TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n",
7550636d5e9fSmrg	   last_rows, last_cols, last_high, last_wide,
7551636d5e9fSmrg	   rows, cols, height, width));
7552dfb07bc7Smrg
7553636d5e9fSmrg    if (rows != last_rows
7554636d5e9fSmrg	|| cols != last_cols
7555636d5e9fSmrg	|| last_high != height
7556636d5e9fSmrg	|| last_wide != width) {
7557d4fba8b9Smrg	TTYSIZE_STRUCT ts;
7558d4fba8b9Smrg
7559d4fba8b9Smrg	last_rows = rows;
7560d4fba8b9Smrg	last_cols = cols;
7561636d5e9fSmrg	last_high = height;
7562636d5e9fSmrg	last_wide = width;
7563d4fba8b9Smrg	setup_winsize(ts, rows, cols, height, width);
756450027b5bSmrg	TRACE_RC(code, SET_TTYSIZE(screen->respond, ts));
7565d4fba8b9Smrg	trace_winsize(ts, "from SET_TTYSIZE");
7566d4fba8b9Smrg    }
7567dfb07bc7Smrg#endif
7568dfb07bc7Smrg
7569dfb07bc7Smrg    (void) rows;
7570dfb07bc7Smrg    (void) cols;
7571dfb07bc7Smrg    (void) height;
7572dfb07bc7Smrg    (void) width;
7573d4fba8b9Smrg
7574d4fba8b9Smrg    return code;
7575dfb07bc7Smrg}
7576dfb07bc7Smrg
7577dfb07bc7Smrg/*
7578dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
7579dfb07bc7Smrg * manipulation 18 and 19.
7580dfb07bc7Smrg */
7581dfb07bc7Smrgvoid
7582dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
7583dfb07bc7Smrg{
7584dfb07bc7Smrg#if OPT_TEK4014
7585dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
7586dfb07bc7Smrg#endif
7587dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
7588dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
7589dfb07bc7Smrg
7590dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
759150027b5bSmrg	    update_winsize(screen,
7592dfb07bc7Smrg			   MaxRows(screen),
7593dfb07bc7Smrg			   MaxCols(screen),
7594dfb07bc7Smrg			   Height(screen),
7595dfb07bc7Smrg			   Width(screen));
7596dfb07bc7Smrg	}
7597dfb07bc7Smrg}
7598d4fba8b9Smrg
7599d4fba8b9Smrg#if OPT_XTERM_SGR
7600d4fba8b9Smrg
7601d4fba8b9Smrg#if OPT_TRACE
7602d4fba8b9Smrgstatic char *
7603d4fba8b9SmrgtraceIFlags(IFlags flags)
7604d4fba8b9Smrg{
7605d4fba8b9Smrg    static char result[1000];
7606d4fba8b9Smrg    result[0] = '\0';
7607d4fba8b9Smrg#define DATA(name) if (flags & name) { strcat(result, " " #name); }
7608d4fba8b9Smrg    DATA(INVERSE);
7609d4fba8b9Smrg    DATA(UNDERLINE);
7610d4fba8b9Smrg    DATA(BOLD);
7611d4fba8b9Smrg    DATA(BLINK);
7612d4fba8b9Smrg    DATA(INVISIBLE);
7613d4fba8b9Smrg    DATA(BG_COLOR);
7614d4fba8b9Smrg    DATA(FG_COLOR);
7615d4fba8b9Smrg
7616d4fba8b9Smrg#if OPT_WIDE_ATTRS
7617d4fba8b9Smrg    DATA(ATR_FAINT);
7618d4fba8b9Smrg    DATA(ATR_ITALIC);
7619d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7620d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7621d4fba8b9Smrg    DATA(ATR_DIRECT_FG);
7622d4fba8b9Smrg    DATA(ATR_DIRECT_BG);
7623d4fba8b9Smrg#endif
7624d4fba8b9Smrg#undef DATA
7625d4fba8b9Smrg    return result;
7626d4fba8b9Smrg}
7627d4fba8b9Smrg
7628d4fba8b9Smrgstatic char *
7629d4fba8b9SmrgtraceIStack(unsigned flags)
7630d4fba8b9Smrg{
7631d4fba8b9Smrg    static char result[1000];
7632d4fba8b9Smrg    result[0] = '\0';
7633d4fba8b9Smrg#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7634d4fba8b9Smrg    DATA(INVERSE);
7635d4fba8b9Smrg    DATA(UNDERLINE);
7636d4fba8b9Smrg    DATA(BOLD);
7637d4fba8b9Smrg    DATA(BLINK);
7638d4fba8b9Smrg    DATA(INVISIBLE);
7639d4fba8b9Smrg#if OPT_ISO_COLORS
7640d4fba8b9Smrg    DATA(BG_COLOR);
7641d4fba8b9Smrg    DATA(FG_COLOR);
7642d4fba8b9Smrg#endif
7643d4fba8b9Smrg
7644d4fba8b9Smrg#if OPT_WIDE_ATTRS
7645d4fba8b9Smrg    DATA(ATR_FAINT);
7646d4fba8b9Smrg    DATA(ATR_ITALIC);
7647d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7648d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7649d4fba8b9Smrg    /* direct-colors are a special case of ISO-colors (see above) */
7650d4fba8b9Smrg#endif
7651d4fba8b9Smrg#undef DATA
7652d4fba8b9Smrg    return result;
7653d4fba8b9Smrg}
7654d4fba8b9Smrg#endif
7655d4fba8b9Smrg
7656d4fba8b9Smrgvoid
7657d4fba8b9SmrgxtermPushSGR(XtermWidget xw, int value)
7658d4fba8b9Smrg{
7659d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7660d4fba8b9Smrg
7661d4fba8b9Smrg    TRACE(("xtermPushSGR %d mask %#x %s\n",
7662d4fba8b9Smrg	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
7663d4fba8b9Smrg
7664d4fba8b9Smrg    if (s->used < MAX_SAVED_SGR) {
7665d4fba8b9Smrg	s->stack[s->used].mask = (IFlags) value;
7666d4fba8b9Smrg#define PUSH_FLAG(name) \
7667d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7668d4fba8b9Smrg	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
7669d4fba8b9Smrg#define PUSH_DATA(name) \
7670d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7671d4fba8b9Smrg	    TRACE(("...may pop %s %d\n", #name, xw->name))
7672d4fba8b9Smrg	PUSH_FLAG(flags);
7673d4fba8b9Smrg#if OPT_ISO_COLORS
7674d4fba8b9Smrg	PUSH_DATA(sgr_foreground);
7675d4fba8b9Smrg	PUSH_DATA(sgr_background);
7676d4fba8b9Smrg	PUSH_DATA(sgr_38_xcolors);
7677d4fba8b9Smrg#endif
7678d4fba8b9Smrg    }
7679d4fba8b9Smrg    s->used++;
7680d4fba8b9Smrg}
7681d4fba8b9Smrg
7682d4fba8b9Smrg#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
7683d4fba8b9Smrg
7684d4fba8b9Smrgvoid
7685d4fba8b9SmrgxtermReportSGR(XtermWidget xw, XTermRect *value)
7686d4fba8b9Smrg{
7687d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
7688d4fba8b9Smrg    char reply[BUFSIZ];
7689d4fba8b9Smrg    CellData working;
7690d4fba8b9Smrg    int row, col;
7691d4fba8b9Smrg    Boolean first = True;
7692d4fba8b9Smrg
7693d4fba8b9Smrg    TRACE(("xtermReportSGR %d,%d - %d,%d\n",
7694d4fba8b9Smrg	   value->top, value->left,
7695d4fba8b9Smrg	   value->bottom, value->right));
7696d4fba8b9Smrg
7697d4fba8b9Smrg    memset(&working, 0, sizeof(working));
7698d4fba8b9Smrg    for (row = value->top - 1; row < value->bottom; ++row) {
7699d4fba8b9Smrg	LineData *ld = getLineData(screen, row);
7700d4fba8b9Smrg	if (ld == 0)
7701d4fba8b9Smrg	    continue;
7702d4fba8b9Smrg	for (col = value->left - 1; col < value->right; ++col) {
7703d4fba8b9Smrg	    if (first) {
7704d4fba8b9Smrg		first = False;
7705d4fba8b9Smrg		saveCellData(screen, &working, 0, ld, NULL, col);
7706d4fba8b9Smrg	    }
7707d4fba8b9Smrg	    working.attribs &= ld->attribs[col];
7708d4fba8b9Smrg#if OPT_ISO_COLORS
7709d4fba8b9Smrg	    if (working.attribs & FG_COLOR
7710d4fba8b9Smrg		&& GetCellColorFG(working.color)
7711d4fba8b9Smrg		!= GetCellColorFG(ld->color[col])) {
7712d4fba8b9Smrg		IAttrClr(working.attribs, FG_COLOR);
7713d4fba8b9Smrg	    }
7714d4fba8b9Smrg	    if (working.attribs & BG_COLOR
7715d4fba8b9Smrg		&& GetCellColorBG(working.color)
7716d4fba8b9Smrg		!= GetCellColorBG(ld->color[col])) {
7717d4fba8b9Smrg		IAttrClr(working.attribs, BG_COLOR);
7718d4fba8b9Smrg	    }
7719d4fba8b9Smrg#endif
7720d4fba8b9Smrg	}
7721d4fba8b9Smrg    }
7722d4fba8b9Smrg    xtermFormatSGR(xw, reply,
7723d4fba8b9Smrg		   working.attribs,
7724d4fba8b9Smrg		   GetCellColorFG(working.color),
7725d4fba8b9Smrg		   GetCellColorBG(working.color));
7726d4fba8b9Smrg    unparseputc1(xw, ANSI_CSI);
7727d4fba8b9Smrg    unparseputs(xw, reply);
7728d4fba8b9Smrg    unparseputc(xw, 'm');
7729d4fba8b9Smrg    unparse_end(xw);
7730d4fba8b9Smrg}
7731d4fba8b9Smrg
7732d4fba8b9Smrgvoid
7733d4fba8b9SmrgxtermPopSGR(XtermWidget xw)
7734d4fba8b9Smrg{
7735d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7736d4fba8b9Smrg
7737d4fba8b9Smrg    TRACE(("xtermPopSGR %d\n", s->used));
7738d4fba8b9Smrg
7739d4fba8b9Smrg    if (s->used > 0) {
7740d4fba8b9Smrg	if (s->used-- <= MAX_SAVED_SGR) {
7741d4fba8b9Smrg	    IFlags mask = s->stack[s->used].mask;
7742d4fba8b9Smrg	    Boolean changed = False;
7743d4fba8b9Smrg
7744d4fba8b9Smrg	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
7745d4fba8b9Smrg	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
7746d4fba8b9Smrg	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
7747d4fba8b9Smrg#define POP_FLAG(name) \
7748d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7749d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7750d4fba8b9Smrg		    changed = True; \
7751d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7752d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7753d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7754d4fba8b9Smrg		} \
7755d4fba8b9Smrg	    }
7756d4fba8b9Smrg#define POP_FLAG2(name,part) \
7757d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7758d4fba8b9Smrg	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
7759d4fba8b9Smrg		    changed = True; \
7760d4fba8b9Smrg		    UIntClr(xw->flags, part); \
7761d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
7762d4fba8b9Smrg		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
7763d4fba8b9Smrg		} \
7764d4fba8b9Smrg	    }
7765d4fba8b9Smrg#define POP_DATA(name,value) \
7766d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7767d4fba8b9Smrg	        Bool always = False; \
7768d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7769d4fba8b9Smrg		    always = changed = True; \
7770d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7771d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7772d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7773d4fba8b9Smrg		} \
7774d4fba8b9Smrg		if (always || (xw->value != s->stack[s->used].value)) { \
7775d4fba8b9Smrg		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
7776d4fba8b9Smrg		    xw->value = s->stack[s->used].value; \
7777d4fba8b9Smrg		    changed = True; \
7778d4fba8b9Smrg		} \
7779d4fba8b9Smrg	    }
7780d4fba8b9Smrg	    POP_FLAG(BOLD);
7781d4fba8b9Smrg	    POP_FLAG(UNDERLINE);
7782d4fba8b9Smrg	    POP_FLAG(BLINK);
7783d4fba8b9Smrg	    POP_FLAG(INVERSE);
7784d4fba8b9Smrg	    POP_FLAG(INVISIBLE);
7785d4fba8b9Smrg#if OPT_WIDE_ATTRS
7786d4fba8b9Smrg	    if (xBIT(psATR_ITALIC - 1) & mask) {
7787d4fba8b9Smrg		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
7788d4fba8b9Smrg	    }
7789d4fba8b9Smrg	    POP_FLAG(ATR_ITALIC);
7790d4fba8b9Smrg	    POP_FLAG(ATR_FAINT);
7791d4fba8b9Smrg	    POP_FLAG(ATR_STRIKEOUT);
7792d4fba8b9Smrg	    POP_FLAG(ATR_DBL_UNDER);
7793d4fba8b9Smrg#endif
7794d4fba8b9Smrg#if OPT_ISO_COLORS
7795d4fba8b9Smrg	    POP_DATA(FG_COLOR, sgr_foreground);
7796d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_background);
7797d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_38_xcolors);
7798d4fba8b9Smrg#if OPT_DIRECT_COLOR
7799d4fba8b9Smrg	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
7800d4fba8b9Smrg	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
7801d4fba8b9Smrg#endif
7802d4fba8b9Smrg	    if (changed) {
7803d4fba8b9Smrg		setExtendedColors(xw);
7804d4fba8b9Smrg	    }
7805d4fba8b9Smrg#else
7806d4fba8b9Smrg	    (void) changed;
7807d4fba8b9Smrg#endif
7808d4fba8b9Smrg	}
7809d4fba8b9Smrg#if OPT_ISO_COLORS
7810d4fba8b9Smrg	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
7811d4fba8b9Smrg	       traceIFlags(xw->flags),
7812d4fba8b9Smrg	       xw->sgr_foreground,
7813d4fba8b9Smrg	       xw->sgr_background,
7814d4fba8b9Smrg	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
7815d4fba8b9Smrg#else
7816d4fba8b9Smrg	TRACE(("xtermP -> flags%s\n",
7817d4fba8b9Smrg	       traceIFlags(xw->flags)));
7818d4fba8b9Smrg#endif
7819d4fba8b9Smrg    }
7820d4fba8b9Smrg}
7821d4fba8b9Smrg
7822d4fba8b9Smrg#if OPT_ISO_COLORS
7823d4fba8b9Smrgstatic ColorSlot *
7824d4fba8b9SmrgallocColorSlot(XtermWidget xw, int slot)
7825d4fba8b9Smrg{
7826d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7827d4fba8b9Smrg    ColorSlot *result = NULL;
7828d4fba8b9Smrg
7829d4fba8b9Smrg    if (slot >= 0 && slot < MAX_SAVED_SGR) {
7830d4fba8b9Smrg	if (s->palettes[slot] == NULL) {
7831d4fba8b9Smrg	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
7832d4fba8b9Smrg						     sizeof(ColorSlot)
7833d4fba8b9Smrg						     + (sizeof(ColorRes)
7834d4fba8b9Smrg							* MAXCOLORS));
7835d4fba8b9Smrg	}
7836d4fba8b9Smrg	result = s->palettes[slot];
7837d4fba8b9Smrg    }
7838d4fba8b9Smrg    return result;
7839d4fba8b9Smrg}
7840d4fba8b9Smrg
7841d4fba8b9Smrgstatic void
7842d4fba8b9SmrgpopOldColors(XtermWidget xw, ScrnColors * source)
7843d4fba8b9Smrg{
7844d4fba8b9Smrg    Boolean changed = False;
7845d4fba8b9Smrg    ScrnColors *target = xw->work.oldColors;
7846d4fba8b9Smrg
7847d4fba8b9Smrg    if (source->which != target->which) {
7848d4fba8b9Smrg	changed = True;
7849d4fba8b9Smrg    } else {
7850d4fba8b9Smrg	int n;
7851d4fba8b9Smrg	for (n = 0; n < NCOLORS; ++n) {
7852d4fba8b9Smrg	    if (COLOR_DEFINED(source, n)) {
7853d4fba8b9Smrg		if (COLOR_DEFINED(target, n)) {
7854d4fba8b9Smrg		    if (source->colors[n] != target->colors[n]) {
7855d4fba8b9Smrg			changed = True;
7856d4fba8b9Smrg			break;
7857d4fba8b9Smrg		    }
7858d4fba8b9Smrg		} else {
7859d4fba8b9Smrg		    changed = True;
7860d4fba8b9Smrg		    break;
7861d4fba8b9Smrg		}
7862d4fba8b9Smrg	    } else if (COLOR_DEFINED(target, n)) {
7863d4fba8b9Smrg		changed = True;
7864d4fba8b9Smrg		break;
7865d4fba8b9Smrg	    }
7866d4fba8b9Smrg	}
7867d4fba8b9Smrg    }
7868d4fba8b9Smrg    if (changed) {
7869d4fba8b9Smrg	ChangeColors(xw, source);
7870d4fba8b9Smrg	UpdateOldColors(xw, source);
7871d4fba8b9Smrg    }
7872d4fba8b9Smrg}
7873d4fba8b9Smrg#endif /* OPT_ISO_COLORS */
7874d4fba8b9Smrg
7875d4fba8b9Smrg#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
7876d4fba8b9Smrg#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
7877d4fba8b9Smrg
7878d4fba8b9Smrg/*
7879d4fba8b9Smrg * By default, a "push" increments the stack after copying to the current
7880d4fba8b9Smrg * slot.  But a specific target allows one to copy into a specific slot.
7881d4fba8b9Smrg */
7882d4fba8b9Smrgvoid
7883d4fba8b9SmrgxtermPushColors(XtermWidget xw, int value)
7884d4fba8b9Smrg{
7885d4fba8b9Smrg#if OPT_ISO_COLORS
7886d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7887d4fba8b9Smrg    int pushed = s->used;
7888d4fba8b9Smrg    int actual = (value <= 0) ? pushed : (value - 1);
7889d4fba8b9Smrg
7890d4fba8b9Smrg    TRACE(("xtermPushColors %d:%d\n", actual, pushed));
7891d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7892d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7893d4fba8b9Smrg	ColorSlot *palette;
7894d4fba8b9Smrg
7895d4fba8b9Smrg	if ((palette = allocColorSlot(xw, actual)) != NULL) {
7896d4fba8b9Smrg	    GetColors(xw, &(palette->base));
7897d4fba8b9Smrg	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
7898d4fba8b9Smrg	    if (value < 0) {
7899d4fba8b9Smrg		s->used++;
7900d4fba8b9Smrg		if (s->last < s->used)
7901d4fba8b9Smrg		    s->last = s->used;
7902d4fba8b9Smrg	    } else {
7903d4fba8b9Smrg		s->used = value;
7904d4fba8b9Smrg	    }
7905d4fba8b9Smrg	}
7906d4fba8b9Smrg    }
7907d4fba8b9Smrg#else
7908d4fba8b9Smrg    (void) xw;
7909d4fba8b9Smrg    (void) value;
7910d4fba8b9Smrg#endif
7911d4fba8b9Smrg}
7912d4fba8b9Smrg
7913d4fba8b9Smrgvoid
7914d4fba8b9SmrgxtermPopColors(XtermWidget xw, int value)
7915d4fba8b9Smrg{
7916d4fba8b9Smrg#if OPT_ISO_COLORS
7917d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7918d4fba8b9Smrg    int popped = (s->used - 1);
7919d4fba8b9Smrg    int actual = (value <= 0) ? popped : (value - 1);
7920d4fba8b9Smrg
7921d4fba8b9Smrg    TRACE(("xtermPopColors %d:%d\n", actual, popped));
7922d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7923d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7924d4fba8b9Smrg	ColorSlot *palette;
7925d4fba8b9Smrg
7926d4fba8b9Smrg	if ((palette = s->palettes[actual]) != NULL) {
7927d4fba8b9Smrg	    Boolean changed = DiffColorSlot(screen->Acolors,
7928d4fba8b9Smrg					    palette->ansi,
7929d4fba8b9Smrg					    MAXCOLORS);
7930d4fba8b9Smrg
7931d4fba8b9Smrg	    GetOldColors(xw);
7932d4fba8b9Smrg	    popOldColors(xw, &(palette->base));
7933d4fba8b9Smrg	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
7934d4fba8b9Smrg	    s->used = actual;
7935d4fba8b9Smrg	    if (changed)
7936d4fba8b9Smrg		xtermRepaint(xw);
7937d4fba8b9Smrg	}
7938d4fba8b9Smrg    }
7939d4fba8b9Smrg#else
7940d4fba8b9Smrg    (void) xw;
7941d4fba8b9Smrg    (void) value;
7942d4fba8b9Smrg#endif
7943d4fba8b9Smrg}
7944d4fba8b9Smrg
7945d4fba8b9Smrgvoid
7946d4fba8b9SmrgxtermReportColors(XtermWidget xw)
7947d4fba8b9Smrg{
7948d4fba8b9Smrg    ANSI reply;
7949d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7950d4fba8b9Smrg
7951d4fba8b9Smrg    memset(&reply, 0, sizeof(reply));
7952d4fba8b9Smrg    reply.a_type = ANSI_CSI;
7953d4fba8b9Smrg    reply.a_pintro = '?';
7954d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->used;
7955d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->last;
7956d4fba8b9Smrg    reply.a_inters = '#';
7957d4fba8b9Smrg    reply.a_final = 'Q';
7958d4fba8b9Smrg    unparseseq(xw, &reply);
7959d4fba8b9Smrg}
7960d4fba8b9Smrg#endif /* OPT_XTERM_SGR */
7961