misc.c revision 50027b5b
150027b5bSmrg/* $XTermId: misc.c,v 1.1015 2022/02/18 09:08:10 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
450027b5bSmrg * Copyright 1999-2021,2022 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
102d522f475Smrg#ifdef VMS
103d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
104d522f475Smrg#ifdef ALLOWLOGFILEEXEC
105d522f475Smrg#undef ALLOWLOGFILEEXEC
106d522f475Smrg#endif
107d522f475Smrg#endif /* VMS */
108d522f475Smrg
109d4fba8b9Smrg#if USE_DOUBLE_BUFFER
110d4fba8b9Smrg#include <X11/extensions/Xdbe.h>
111d4fba8b9Smrg#endif
112d4fba8b9Smrg
113d4fba8b9Smrg#if OPT_WIDE_CHARS
114d4fba8b9Smrg#include <wctype.h>
115d4fba8b9Smrg#endif
116d4fba8b9Smrg
117d522f475Smrg#if OPT_TEK4014
118d522f475Smrg#define OUR_EVENT(event,Type) \
119d522f475Smrg		(event.type == Type && \
120d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
121d522f475Smrg		    (tekWidget && \
122d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
123d522f475Smrg#else
124d522f475Smrg#define OUR_EVENT(event,Type) \
125d522f475Smrg		(event.type == Type && \
126d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
127d522f475Smrg#endif
128d522f475Smrg
129d4fba8b9Smrg#define VB_DELAY    screen->visualBellDelay
130d4fba8b9Smrg#define EVENT_DELAY TScreenOf(term)->nextEventDelay
131d4fba8b9Smrg
1323367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
133d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
134d522f475Smrg
1353367019cSmrgstatic char emptyString[] = "";
1363367019cSmrg
137d522f475Smrg#if OPT_EXEC_XTERM
138d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
139d522f475Smrg   error; adapted from libc docs */
140d522f475Smrgstatic char *
141d522f475SmrgReadlink(const char *filename)
142d522f475Smrg{
143d522f475Smrg    char *buf = NULL;
144cd3331d0Smrg    size_t size = 100;
145d522f475Smrg
146d522f475Smrg    for (;;) {
147037a25ddSmrg	int n;
148037a25ddSmrg	char *tmp = TypeRealloc(char, size, buf);
149037a25ddSmrg	if (tmp == NULL) {
150037a25ddSmrg	    free(buf);
151037a25ddSmrg	    return NULL;
152037a25ddSmrg	}
153037a25ddSmrg	buf = tmp;
154d522f475Smrg	memset(buf, 0, size);
155d522f475Smrg
156cd3331d0Smrg	n = (int) readlink(filename, buf, size);
157d522f475Smrg	if (n < 0) {
158d522f475Smrg	    free(buf);
159d522f475Smrg	    return NULL;
160d522f475Smrg	}
161d522f475Smrg
162d522f475Smrg	if ((unsigned) n < size) {
163d522f475Smrg	    return buf;
164d522f475Smrg	}
165d522f475Smrg
166d522f475Smrg	size *= 2;
167d522f475Smrg    }
168d522f475Smrg}
169d522f475Smrg#endif /* OPT_EXEC_XTERM */
170d522f475Smrg
171d522f475Smrgstatic void
172d522f475SmrgSleep(int msec)
173d522f475Smrg{
174d522f475Smrg    static struct timeval select_timeout;
175d522f475Smrg
176d522f475Smrg    select_timeout.tv_sec = 0;
177d522f475Smrg    select_timeout.tv_usec = msec * 1000;
178d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
179d522f475Smrg}
180d522f475Smrg
181d522f475Smrgstatic void
1823367019cSmrgselectwindow(XtermWidget xw, int flag)
183d522f475Smrg{
1843367019cSmrg    TScreen *screen = TScreenOf(xw);
1853367019cSmrg
186d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
187d522f475Smrg
188d522f475Smrg#if OPT_TEK4014
1893367019cSmrg    if (TEK4014_ACTIVE(xw)) {
190d522f475Smrg	if (!Ttoggled)
191d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
192d522f475Smrg	screen->select |= flag;
193d522f475Smrg	if (!Ttoggled)
194d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
195d522f475Smrg    } else
196d522f475Smrg#endif
197d522f475Smrg    {
198d4fba8b9Smrg#if OPT_INPUT_METHOD
1993367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
2003367019cSmrg	if (input && input->xic)
2013367019cSmrg	    XSetICFocus(input->xic);
2023367019cSmrg#endif
203d522f475Smrg
204d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
205d4fba8b9Smrg	    HideCursor(xw);
206d522f475Smrg	screen->select |= flag;
207d522f475Smrg	if (screen->cursor_state)
208d4fba8b9Smrg	    ShowCursor(xw);
209d522f475Smrg    }
210cd3331d0Smrg    GetScrollLock(screen);
211d522f475Smrg}
212d522f475Smrg
213d522f475Smrgstatic void
2143367019cSmrgunselectwindow(XtermWidget xw, int flag)
215d522f475Smrg{
2163367019cSmrg    TScreen *screen = TScreenOf(xw);
2173367019cSmrg
218d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
219d522f475Smrg
2203367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
221d522f475Smrg	screen->hide_pointer = False;
2228f44fb3bSmrg	xtermDisplayPointer(xw);
223d522f475Smrg    }
224d522f475Smrg
225d4fba8b9Smrg    screen->select &= ~flag;
226d4fba8b9Smrg
227d522f475Smrg    if (!screen->always_highlight) {
228d522f475Smrg#if OPT_TEK4014
2293367019cSmrg	if (TEK4014_ACTIVE(xw)) {
230d522f475Smrg	    if (!Ttoggled)
231d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
232d522f475Smrg	    if (!Ttoggled)
233d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
234d522f475Smrg	} else
235d522f475Smrg#endif
236d522f475Smrg	{
237d4fba8b9Smrg#if OPT_INPUT_METHOD
2383367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2393367019cSmrg	    if (input && input->xic)
2403367019cSmrg		XUnsetICFocus(input->xic);
2413367019cSmrg#endif
242d522f475Smrg
243d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
244d4fba8b9Smrg		HideCursor(xw);
245d522f475Smrg	    if (screen->cursor_state)
246d4fba8b9Smrg		ShowCursor(xw);
247d522f475Smrg	}
248d522f475Smrg    }
249d522f475Smrg}
250d522f475Smrg
251d522f475Smrgstatic void
2529a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
253d522f475Smrg{
254d522f475Smrg    TScreen *screen = TScreenOf(xw);
255d522f475Smrg
256d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
257cd3331d0Smrg    TRACE_FOCUS(xw, ev);
258cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
259cd3331d0Smrg	ev->focus &&
260cd3331d0Smrg	!(screen->select & FOCUS))
2613367019cSmrg	selectwindow(xw, INWINDOW);
262d522f475Smrg}
263d522f475Smrg
264d522f475Smrgstatic void
2659a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
266d522f475Smrg{
267d522f475Smrg    TScreen *screen = TScreenOf(xw);
268d522f475Smrg
269d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
270cd3331d0Smrg    TRACE_FOCUS(xw, ev);
271cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
272cd3331d0Smrg	ev->focus &&
273cd3331d0Smrg	!(screen->select & FOCUS))
2743367019cSmrg	unselectwindow(xw, INWINDOW);
275d522f475Smrg}
276d522f475Smrg
277d522f475Smrg#ifndef XUrgencyHint
278d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
279d522f475Smrg#endif
280d522f475Smrg
281d522f475Smrgstatic void
282c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
283d522f475Smrg{
284c219fbebSmrg    TScreen *screen = TScreenOf(xw);
285c219fbebSmrg
286d522f475Smrg    if (screen->bellIsUrgent) {
287c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
288d522f475Smrg	if (h != 0) {
289c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
290d522f475Smrg		h->flags |= XUrgencyHint;
291d522f475Smrg	    } else {
292d522f475Smrg		h->flags &= ~XUrgencyHint;
293d522f475Smrg	    }
294c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
295d522f475Smrg	}
296d522f475Smrg    }
297d522f475Smrg}
298d522f475Smrg
299d522f475Smrgvoid
300d4fba8b9Smrgdo_xevents(XtermWidget xw)
301d522f475Smrg{
302d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
303d522f475Smrg
3043367019cSmrg    if (xtermAppPending()
305d522f475Smrg	||
306d522f475Smrg#if defined(VMS) || defined(__VMS)
307d522f475Smrg	screen->display->qlen > 0
308d522f475Smrg#else
309d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
310d522f475Smrg#endif
311d522f475Smrg	)
312d4fba8b9Smrg	xevents(xw);
313d522f475Smrg}
314d522f475Smrg
315d522f475Smrgvoid
3168f44fb3bSmrgxtermDisplayPointer(XtermWidget xw)
317d522f475Smrg{
318d522f475Smrg    TScreen *screen = TScreenOf(xw);
319d522f475Smrg
320d522f475Smrg    if (screen->Vshow) {
321d522f475Smrg	if (screen->hide_pointer) {
3228f44fb3bSmrg	    TRACE(("Display text pointer (hidden)\n"));
323d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
324d522f475Smrg	} else {
3258f44fb3bSmrg	    TRACE(("Display text pointer (visible)\n"));
326d522f475Smrg	    recolor_cursor(screen,
327d522f475Smrg			   screen->pointer_cursor,
328d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
329d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
330d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
331d522f475Smrg	}
332d522f475Smrg    }
333d522f475Smrg}
334d522f475Smrg
335d522f475Smrgvoid
336d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
337d522f475Smrg{
338d522f475Smrg    static int tried = -1;
339d522f475Smrg    TScreen *screen = TScreenOf(xw);
340d522f475Smrg
341d522f475Smrg#if OPT_TEK4014
342d522f475Smrg    if (TEK4014_SHOWN(xw))
343d522f475Smrg	enable = True;
344d522f475Smrg#endif
345d522f475Smrg
346d522f475Smrg    /*
347d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
348d522f475Smrg     * the mouse-mode:
349d522f475Smrg     */
350d522f475Smrg    if (!enable) {
351d522f475Smrg	switch (screen->pointer_mode) {
352d522f475Smrg	case pNever:
353d522f475Smrg	    enable = True;
354d522f475Smrg	    break;
355d522f475Smrg	case pNoMouse:
356d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
357d522f475Smrg		enable = True;
358d522f475Smrg	    break;
359d522f475Smrg	case pAlways:
3603367019cSmrg	case pFocused:
361d522f475Smrg	    break;
362d522f475Smrg	}
363d522f475Smrg    }
364d522f475Smrg
365d522f475Smrg    if (enable) {
366d522f475Smrg	if (screen->hide_pointer) {
367d522f475Smrg	    screen->hide_pointer = False;
3688f44fb3bSmrg	    xtermDisplayPointer(xw);
369d522f475Smrg	    switch (screen->send_mouse_pos) {
370d522f475Smrg	    case ANY_EVENT_MOUSE:
371d522f475Smrg		break;
372d522f475Smrg	    default:
373d522f475Smrg		MotionOff(screen, xw);
374d522f475Smrg		break;
375d522f475Smrg	    }
376d522f475Smrg	}
377d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
378d522f475Smrg	if (screen->hidden_cursor == 0) {
379d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
380d522f475Smrg	}
381d522f475Smrg	if (screen->hidden_cursor == 0) {
382d522f475Smrg	    tried = 1;
383d522f475Smrg	} else {
384d522f475Smrg	    tried = 0;
385d522f475Smrg	    screen->hide_pointer = True;
3868f44fb3bSmrg	    xtermDisplayPointer(xw);
387d522f475Smrg	    MotionOn(screen, xw);
388d522f475Smrg	}
389d522f475Smrg    }
390d522f475Smrg}
391d522f475Smrg
3923367019cSmrg/* true if p contains q */
3933367019cSmrg#define ExposeContains(p,q) \
3943367019cSmrg	    ((p)->y <= (q)->y \
3953367019cSmrg	  && (p)->x <= (q)->x \
3963367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
3973367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
3983367019cSmrg
3993367019cSmrgstatic XtInputMask
4009a64e1c5SmrgmergeExposeEvents(XEvent *target)
4013367019cSmrg{
4023367019cSmrg    XEvent next_event;
403037a25ddSmrg    XExposeEvent *p;
4043367019cSmrg
4053367019cSmrg    XtAppNextEvent(app_con, target);
4063367019cSmrg    p = (XExposeEvent *) target;
4073367019cSmrg
4083367019cSmrg    while (XtAppPending(app_con)
4093367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4103367019cSmrg	   && next_event.type == Expose) {
4113367019cSmrg	Boolean merge_this = False;
412d4fba8b9Smrg	XExposeEvent *q = (XExposeEvent *) (&next_event);
4133367019cSmrg
4143367019cSmrg	XtAppNextEvent(app_con, &next_event);
415d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4163367019cSmrg
4173367019cSmrg	/*
4183367019cSmrg	 * If either window is contained within the other, merge the events.
4193367019cSmrg	 * The traces show that there are also cases where a full repaint of
4203367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4213367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4223367019cSmrg	 * to skim several events ahead.
4233367019cSmrg	 */
4243367019cSmrg	if (p->window == q->window) {
4253367019cSmrg	    if (ExposeContains(p, q)) {
4263367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4273367019cSmrg		merge_this = True;
4283367019cSmrg		next_event = *target;
4293367019cSmrg	    } else if (ExposeContains(q, p)) {
4303367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4313367019cSmrg		merge_this = True;
4323367019cSmrg	    }
4333367019cSmrg	}
4343367019cSmrg	if (!merge_this) {
4353367019cSmrg	    XtDispatchEvent(target);
4363367019cSmrg	}
4373367019cSmrg	*target = next_event;
4383367019cSmrg    }
4393367019cSmrg    XtDispatchEvent(target);
4403367019cSmrg    return XtAppPending(app_con);
4413367019cSmrg}
4423367019cSmrg
4433367019cSmrg/*
4443367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4453367019cSmrg * event.  Remove that from the queue so we can look further.
4463367019cSmrg *
4473367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4483367019cSmrg * that.  If the adjacent events are for different windows, process the older
4493367019cSmrg * event and update the event used for comparing windows.  If they are for the
4503367019cSmrg * same window, only the newer event is of interest.
4513367019cSmrg *
4523367019cSmrg * Finally, process the (remaining) configure-notify event.
4533367019cSmrg */
4543367019cSmrgstatic XtInputMask
4559a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4563367019cSmrg{
4573367019cSmrg    XEvent next_event;
458037a25ddSmrg    XConfigureEvent *p;
4593367019cSmrg
4603367019cSmrg    XtAppNextEvent(app_con, target);
4613367019cSmrg    p = (XConfigureEvent *) target;
4623367019cSmrg
4633367019cSmrg    if (XtAppPending(app_con)
4643367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
4653367019cSmrg	&& next_event.type == ConfigureNotify) {
4663367019cSmrg	Boolean merge_this = False;
467d4fba8b9Smrg	XConfigureEvent *q = (XConfigureEvent *) (&next_event);
4683367019cSmrg
4693367019cSmrg	XtAppNextEvent(app_con, &next_event);
470d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4713367019cSmrg
4723367019cSmrg	if (p->window == q->window) {
4733367019cSmrg	    TRACE(("pending Configure...merged\n"));
4743367019cSmrg	    merge_this = True;
4753367019cSmrg	}
4763367019cSmrg	if (!merge_this) {
4773367019cSmrg	    TRACE(("pending Configure...skipped\n"));
4783367019cSmrg	    XtDispatchEvent(target);
4793367019cSmrg	}
4803367019cSmrg	*target = next_event;
4813367019cSmrg    }
4823367019cSmrg    XtDispatchEvent(target);
4833367019cSmrg    return XtAppPending(app_con);
4843367019cSmrg}
4853367019cSmrg
486d4fba8b9Smrg#define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
487d4fba8b9Smrg#define SameButtonEvent(a,b) ( \
488d4fba8b9Smrg	SAME(a,b,type) && \
489d4fba8b9Smrg	SAME(a,b,serial) && \
490d4fba8b9Smrg	SAME(a,b,send_event) && \
491d4fba8b9Smrg	SAME(a,b,display) && \
492d4fba8b9Smrg	SAME(a,b,window) && \
493d4fba8b9Smrg	SAME(a,b,root) && \
494d4fba8b9Smrg	SAME(a,b,subwindow) && \
495d4fba8b9Smrg	SAME(a,b,time) && \
496d4fba8b9Smrg	SAME(a,b,x) && \
497d4fba8b9Smrg	SAME(a,b,y) && \
498d4fba8b9Smrg	SAME(a,b,x_root) && \
499d4fba8b9Smrg	SAME(a,b,y_root) && \
500d4fba8b9Smrg	SAME(a,b,state) && \
501d4fba8b9Smrg	SAME(a,b,button) && \
502d4fba8b9Smrg	SAME(a,b,same_screen))
503d4fba8b9Smrg
504d4fba8b9Smrg/*
505d4fba8b9Smrg * Work around a bug in the X mouse code, which delivers duplicate events.
506d4fba8b9Smrg */
507d4fba8b9Smrgstatic XtInputMask
508d4fba8b9SmrgmergeButtonEvents(XEvent *target)
509d4fba8b9Smrg{
510d4fba8b9Smrg    XEvent next_event;
511d4fba8b9Smrg    XButtonEvent *p;
512d4fba8b9Smrg
513d4fba8b9Smrg    XtAppNextEvent(app_con, target);
514d4fba8b9Smrg    p = (XButtonEvent *) target;
515d4fba8b9Smrg
516d4fba8b9Smrg    if (XtAppPending(app_con)
517d4fba8b9Smrg	&& XtAppPeekEvent(app_con, &next_event)
518d4fba8b9Smrg	&& SameButtonEvent(target, &next_event)) {
519d4fba8b9Smrg	Boolean merge_this = False;
520d4fba8b9Smrg	XButtonEvent *q = (XButtonEvent *) (&next_event);
521d4fba8b9Smrg
522d4fba8b9Smrg	XtAppNextEvent(app_con, &next_event);
523d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
524d4fba8b9Smrg
525d4fba8b9Smrg	if (p->window == q->window) {
526d4fba8b9Smrg	    TRACE(("pending ButtonEvent...merged\n"));
527d4fba8b9Smrg	    merge_this = True;
528d4fba8b9Smrg	}
529d4fba8b9Smrg	if (!merge_this) {
530d4fba8b9Smrg	    TRACE(("pending ButtonEvent...skipped\n"));
531d4fba8b9Smrg	    XtDispatchEvent(target);
532d4fba8b9Smrg	}
533d4fba8b9Smrg	*target = next_event;
534d4fba8b9Smrg    }
535d4fba8b9Smrg    XtDispatchEvent(target);
536d4fba8b9Smrg    return XtAppPending(app_con);
537d4fba8b9Smrg}
538d4fba8b9Smrg
5393367019cSmrg/*
5403367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5413367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5423367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5433367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5443367019cSmrg * point.
5453367019cSmrg *
5463367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5473367019cSmrg */
5483367019cSmrgXtInputMask
5493367019cSmrgxtermAppPending(void)
5503367019cSmrg{
5513367019cSmrg    XtInputMask result = XtAppPending(app_con);
5523367019cSmrg    XEvent this_event;
553037a25ddSmrg    Boolean found = False;
5543367019cSmrg
5553367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
556037a25ddSmrg	found = True;
557d4fba8b9Smrg	TRACE_EVENT("pending", &this_event, (String *) 0, 0);
5583367019cSmrg	if (this_event.type == Expose) {
5593367019cSmrg	    result = mergeExposeEvents(&this_event);
5603367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5613367019cSmrg	    result = mergeConfigureEvents(&this_event);
562d4fba8b9Smrg	} else if (this_event.type == ButtonPress ||
563d4fba8b9Smrg		   this_event.type == ButtonRelease) {
564d4fba8b9Smrg	    result = mergeButtonEvents(&this_event);
5653367019cSmrg	} else {
5663367019cSmrg	    break;
5673367019cSmrg	}
5683367019cSmrg    }
569037a25ddSmrg
570037a25ddSmrg    /*
571037a25ddSmrg     * With NetBSD, closing a shell results in closing the X input event
572037a25ddSmrg     * stream, which interferes with the "-hold" option.  Wait a short time in
573037a25ddSmrg     * this case, to avoid max'ing the CPU.
574037a25ddSmrg     */
575037a25ddSmrg    if (hold_screen && caught_intr && !found) {
576d4fba8b9Smrg	Sleep(EVENT_DELAY);
577037a25ddSmrg    }
5783367019cSmrg    return result;
5793367019cSmrg}
5803367019cSmrg
581d522f475Smrgvoid
582d4fba8b9Smrgxevents(XtermWidget xw)
583d522f475Smrg{
584d522f475Smrg    TScreen *screen = TScreenOf(xw);
585d522f475Smrg    XEvent event;
586d522f475Smrg    XtInputMask input_mask;
587d522f475Smrg
588d522f475Smrg    if (need_cleanup)
5893367019cSmrg	NormalExit();
590d522f475Smrg
591d522f475Smrg    if (screen->scroll_amt)
592d522f475Smrg	FlushScroll(xw);
593d522f475Smrg    /*
594d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
595d522f475Smrg     * will process the timeout and return without blockng on the
596cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
597d522f475Smrg     * with select().
598d522f475Smrg     */
5993367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
600cd3331d0Smrg	if (input_mask & XtIMTimer)
601cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
602d522f475Smrg#if OPT_SESSION_MGT
603cd3331d0Smrg	/*
604cd3331d0Smrg	 * Session management events are alternative input events. Deal with
605cd3331d0Smrg	 * them in the same way.
606cd3331d0Smrg	 */
607cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
608cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
609d522f475Smrg#endif
610cd3331d0Smrg	else
611cd3331d0Smrg	    break;
612cd3331d0Smrg    }
613d522f475Smrg
614d522f475Smrg    /*
615d4fba8b9Smrg     * If there are no XEvents, don't wait around...
616d522f475Smrg     */
617d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
618d522f475Smrg	return;
619d522f475Smrg    do {
620d522f475Smrg	/*
621d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
622d522f475Smrg	 * We simply ignore all events except for those not passed down to
623d522f475Smrg	 * this function, e.g., those handled in in_put().
624d522f475Smrg	 */
625d522f475Smrg	if (screen->waitingForTrackInfo) {
626d4fba8b9Smrg	    Sleep(EVENT_DELAY);
627d522f475Smrg	    return;
628d522f475Smrg	}
629d522f475Smrg	XtAppNextEvent(app_con, &event);
630d522f475Smrg	/*
631d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
632d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
633d522f475Smrg	 * looking at the event ourselves we make sure that we can
634d522f475Smrg	 * do the right thing.
635d522f475Smrg	 */
636d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
637d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
638d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
639d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
640d4fba8b9Smrg	} else if (event.xany.type == MotionNotify
641d4fba8b9Smrg		   && event.xcrossing.window == XtWindow(xw)) {
642d4fba8b9Smrg	    switch (screen->send_mouse_pos) {
643d4fba8b9Smrg	    case ANY_EVENT_MOUSE:
644d522f475Smrg#if OPT_DEC_LOCATOR
645d4fba8b9Smrg	    case DEC_LOCATOR:
646d522f475Smrg#endif /* OPT_DEC_LOCATOR */
647d4fba8b9Smrg		SendMousePosition(xw, &event);
648d4fba8b9Smrg		xtermShowPointer(xw, True);
649d4fba8b9Smrg		continue;
650d4fba8b9Smrg	    case BTN_EVENT_MOUSE:
651d4fba8b9Smrg		SendMousePosition(xw, &event);
652d4fba8b9Smrg		xtermShowPointer(xw, True);
653d4fba8b9Smrg	    }
654d522f475Smrg	}
655d522f475Smrg
656cb4a1343Smrg	/*
657cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
658cb4a1343Smrg	 * mouse pointer back on.
659cb4a1343Smrg	 */
660cb4a1343Smrg	if (screen->hide_pointer) {
6613367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6623367019cSmrg		switch (event.xany.type) {
6633367019cSmrg		case MotionNotify:
6643367019cSmrg		    xtermShowPointer(xw, True);
6653367019cSmrg		    break;
6663367019cSmrg		}
6673367019cSmrg	    } else {
6683367019cSmrg		switch (event.xany.type) {
6693367019cSmrg		case KeyPress:
6703367019cSmrg		case KeyRelease:
6713367019cSmrg		case ButtonPress:
6723367019cSmrg		case ButtonRelease:
6733367019cSmrg		    /* also these... */
6743367019cSmrg		case Expose:
675037a25ddSmrg		case GraphicsExpose:
6763367019cSmrg		case NoExpose:
6773367019cSmrg		case PropertyNotify:
6783367019cSmrg		case ClientMessage:
6793367019cSmrg		    break;
6803367019cSmrg		default:
6813367019cSmrg		    xtermShowPointer(xw, True);
6823367019cSmrg		    break;
6833367019cSmrg		}
684cb4a1343Smrg	    }
685cb4a1343Smrg	}
686cb4a1343Smrg
687d522f475Smrg	if (!event.xany.send_event ||
688d522f475Smrg	    screen->allowSendEvents ||
689d522f475Smrg	    ((event.xany.type != KeyPress) &&
690d522f475Smrg	     (event.xany.type != KeyRelease) &&
691d522f475Smrg	     (event.xany.type != ButtonPress) &&
692d522f475Smrg	     (event.xany.type != ButtonRelease))) {
693d522f475Smrg
694d4fba8b9Smrg	    if (event.xany.type == MappingNotify) {
695d4fba8b9Smrg		XRefreshKeyboardMapping(&(event.xmapping));
696d4fba8b9Smrg		VTInitModifiers(xw);
697d4fba8b9Smrg	    }
698d522f475Smrg	    XtDispatchEvent(&event);
699d522f475Smrg	}
7003367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
701d522f475Smrg}
702d522f475Smrg
703d522f475Smrgstatic Cursor
704d522f475Smrgmake_hidden_cursor(XtermWidget xw)
705d522f475Smrg{
706d522f475Smrg    TScreen *screen = TScreenOf(xw);
707d522f475Smrg    Cursor c;
708d522f475Smrg    Display *dpy = screen->display;
709d522f475Smrg    XFontStruct *fn;
710d522f475Smrg
711d522f475Smrg    static XColor dummy;
712d522f475Smrg
713d522f475Smrg    /*
714d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
715d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
716b7c89284Ssnj     * server insists on drawing _something_.
717d522f475Smrg     */
718d522f475Smrg    TRACE(("Ask for nil2 font\n"));
7198f44fb3bSmrg    if ((fn = xtermLoadQueryFont(xw, "nil2")) == 0) {
720d522f475Smrg	TRACE(("...Ask for fixed font\n"));
7218f44fb3bSmrg	fn = xtermLoadQueryFont(xw, DEFFONT);
722d522f475Smrg    }
723d522f475Smrg
724d4fba8b9Smrg    if (fn != None) {
725d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
726d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
727d522f475Smrg	XFreeFont(dpy, fn);
728d522f475Smrg    } else {
729d4fba8b9Smrg	c = None;
730d522f475Smrg    }
731d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
732d4fba8b9Smrg    return c;
733d522f475Smrg}
734d522f475Smrg
735fa3f02f3Smrg/*
736fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
737fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
738fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
739fa3f02f3Smrg * until the window is initialized.
740fa3f02f3Smrg */
7418f44fb3bSmrg#ifdef HAVE_LIB_XCURSOR
742fa3f02f3Smrgvoid
743037a25ddSmrginit_colored_cursor(Display *dpy)
744fa3f02f3Smrg{
74594644356Smrg    static const char theme[] = "index.theme";
74694644356Smrg    static const char pattern[] = "xtermXXXXXX";
747fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
748fa3f02f3Smrg
749fa3f02f3Smrg    xterm_cursor_theme = 0;
750037a25ddSmrg    /*
751037a25ddSmrg     * The environment variable overrides a (possible) resource Xcursor.theme
752037a25ddSmrg     */
753fa3f02f3Smrg    if (IsEmpty(env)) {
754037a25ddSmrg	env = XGetDefault(dpy, "Xcursor", "theme");
7558f44fb3bSmrg	TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
7568f44fb3bSmrg    } else {
7578f44fb3bSmrg	TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
758037a25ddSmrg    }
7598f44fb3bSmrg
760037a25ddSmrg    /*
761037a25ddSmrg     * If neither found, provide our own default theme.
762037a25ddSmrg     */
763037a25ddSmrg    if (IsEmpty(env)) {
764037a25ddSmrg	const char *tmp_dir;
765037a25ddSmrg	char *filename;
766037a25ddSmrg	size_t needed;
767037a25ddSmrg
7688f44fb3bSmrg	TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
7698f44fb3bSmrg
770fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
771fa3f02f3Smrg	    tmp_dir = P_tmpdir;
772fa3f02f3Smrg	}
773fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
774fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
775fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
776fa3f02f3Smrg
777fa3f02f3Smrg#ifdef HAVE_MKDTEMP
778fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
779fa3f02f3Smrg#else
780fa3f02f3Smrg	    if (mktemp(filename) != 0
781fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
782fa3f02f3Smrg		xterm_cursor_theme = filename;
783fa3f02f3Smrg	    }
784fa3f02f3Smrg#endif
785d4fba8b9Smrg	    if (xterm_cursor_theme != filename)
786d4fba8b9Smrg		free(filename);
787fa3f02f3Smrg	    /*
788fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
789fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
790fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
791fa3f02f3Smrg	     */
792fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
793fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
794037a25ddSmrg		FILE *fp;
795037a25ddSmrg
796fa3f02f3Smrg		strcat(leaf, "/");
797fa3f02f3Smrg		strcat(leaf, theme);
7988f44fb3bSmrg
799fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
800fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
801fa3f02f3Smrg		    fclose(fp);
802fa3f02f3Smrg		    *leaf = '\0';
803fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
804fa3f02f3Smrg		    *leaf = '/';
8058f44fb3bSmrg
8068f44fb3bSmrg		    TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
8078f44fb3bSmrg			   xterm_cursor_theme));
8088f44fb3bSmrg		    atexit(cleanup_colored_cursor);
8098f44fb3bSmrg		} else {
8108f44fb3bSmrg		    FreeAndNull(xterm_cursor_theme);
811fa3f02f3Smrg		}
812fa3f02f3Smrg	    }
813fa3f02f3Smrg	}
814fa3f02f3Smrg    }
815fa3f02f3Smrg}
8168f44fb3bSmrg#endif /* HAVE_LIB_XCURSOR */
817fa3f02f3Smrg
818fa3f02f3Smrg/*
819fa3f02f3Smrg * Once done, discard the file and directory holding it.
820fa3f02f3Smrg */
821fa3f02f3Smrgvoid
822fa3f02f3Smrgcleanup_colored_cursor(void)
823fa3f02f3Smrg{
824fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
825fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
826fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
827fa3f02f3Smrg	struct stat sb;
828fa3f02f3Smrg	if (!IsEmpty(my_path)
829fa3f02f3Smrg	    && stat(my_path, &sb) == 0
830fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
831fa3f02f3Smrg	    unlink(xterm_cursor_theme);
832fa3f02f3Smrg	    rmdir(my_path);
833fa3f02f3Smrg	}
8348f44fb3bSmrg	FreeAndNull(xterm_cursor_theme);
835fa3f02f3Smrg    }
836fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
837fa3f02f3Smrg}
838fa3f02f3Smrg
839d522f475SmrgCursor
840d4fba8b9Smrgmake_colored_cursor(unsigned c_index,		/* index into font */
841d522f475Smrg		    unsigned long fg,	/* pixel value */
842d522f475Smrg		    unsigned long bg)	/* pixel value */
843d522f475Smrg{
844d522f475Smrg    TScreen *screen = TScreenOf(term);
845d4fba8b9Smrg    Cursor c = None;
846d522f475Smrg    Display *dpy = screen->display;
847d522f475Smrg
848d4fba8b9Smrg    TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
849d4fba8b9Smrg    if (!IsEmpty(screen->cursor_font_name)) {
850d4fba8b9Smrg	static XTermFonts myFont;
851d4fba8b9Smrg
852d4fba8b9Smrg	/* adapted from XCreateFontCursor(), which hardcodes the font name */
853d4fba8b9Smrg	TRACE(("loading cursor from alternate cursor font\n"));
8548f44fb3bSmrg	myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
8558f44fb3bSmrg	if (myFont.fs != NULL) {
856d4fba8b9Smrg	    if (!xtermMissingChar(c_index, &myFont)
857d4fba8b9Smrg		&& !xtermMissingChar(c_index + 1, &myFont)) {
858d4fba8b9Smrg#define DATA(c) { 0UL, c, c, c, 0, 0 }
859d4fba8b9Smrg		static XColor foreground = DATA(0);
860d4fba8b9Smrg		static XColor background = DATA(65535);
861d4fba8b9Smrg#undef DATA
862d4fba8b9Smrg
863d4fba8b9Smrg		/*
864d4fba8b9Smrg		 * Cursor fonts follow each shape glyph with a mask glyph; so
865d4fba8b9Smrg		 * that character position 0 contains a shape, 1 the mask for
866d4fba8b9Smrg		 * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
867d4fba8b9Smrg		 * contains defined names for each shape.
868d4fba8b9Smrg		 */
869d4fba8b9Smrg		c = XCreateGlyphCursor(dpy,
870d4fba8b9Smrg				       myFont.fs->fid,	/* source_font */
871d4fba8b9Smrg				       myFont.fs->fid,	/* mask_font */
872d4fba8b9Smrg				       c_index + 0,	/* source_char */
873d4fba8b9Smrg				       c_index + 1,	/* mask_char */
874d4fba8b9Smrg				       &foreground,
875d4fba8b9Smrg				       &background);
876d4fba8b9Smrg	    }
877d4fba8b9Smrg	    XFreeFont(dpy, myFont.fs);
878d4fba8b9Smrg	}
879d4fba8b9Smrg	if (c == None) {
880d4fba8b9Smrg	    xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
881d4fba8b9Smrg			 c_index, screen->cursor_font_name);
882d4fba8b9Smrg	}
883d4fba8b9Smrg    }
884d4fba8b9Smrg    if (c == None)
885d4fba8b9Smrg	c = XCreateFontCursor(dpy, c_index);
886d4fba8b9Smrg
887d522f475Smrg    if (c != None) {
888d522f475Smrg	recolor_cursor(screen, c, fg, bg);
889d522f475Smrg    }
890d4fba8b9Smrg    return c;
891d522f475Smrg}
892d522f475Smrg
8938f44fb3bSmrg/* adapted from <X11/cursorfont.h> */
8948f44fb3bSmrgstatic int
8958f44fb3bSmrgLookupCursorShape(const char *name)
8968f44fb3bSmrg{
8978f44fb3bSmrg#define DATA(name) { XC_##name, #name }
8988f44fb3bSmrg    static struct {
8998f44fb3bSmrg	int code;
9008f44fb3bSmrg	const char name[25];
9018f44fb3bSmrg    } table[] = {
9028f44fb3bSmrg	DATA(X_cursor),
9038f44fb3bSmrg	    DATA(arrow),
9048f44fb3bSmrg	    DATA(based_arrow_down),
9058f44fb3bSmrg	    DATA(based_arrow_up),
9068f44fb3bSmrg	    DATA(boat),
9078f44fb3bSmrg	    DATA(bogosity),
9088f44fb3bSmrg	    DATA(bottom_left_corner),
9098f44fb3bSmrg	    DATA(bottom_right_corner),
9108f44fb3bSmrg	    DATA(bottom_side),
9118f44fb3bSmrg	    DATA(bottom_tee),
9128f44fb3bSmrg	    DATA(box_spiral),
9138f44fb3bSmrg	    DATA(center_ptr),
9148f44fb3bSmrg	    DATA(circle),
9158f44fb3bSmrg	    DATA(clock),
9168f44fb3bSmrg	    DATA(coffee_mug),
9178f44fb3bSmrg	    DATA(cross),
9188f44fb3bSmrg	    DATA(cross_reverse),
9198f44fb3bSmrg	    DATA(crosshair),
9208f44fb3bSmrg	    DATA(diamond_cross),
9218f44fb3bSmrg	    DATA(dot),
9228f44fb3bSmrg	    DATA(dotbox),
9238f44fb3bSmrg	    DATA(double_arrow),
9248f44fb3bSmrg	    DATA(draft_large),
9258f44fb3bSmrg	    DATA(draft_small),
9268f44fb3bSmrg	    DATA(draped_box),
9278f44fb3bSmrg	    DATA(exchange),
9288f44fb3bSmrg	    DATA(fleur),
9298f44fb3bSmrg	    DATA(gobbler),
9308f44fb3bSmrg	    DATA(gumby),
9318f44fb3bSmrg	    DATA(hand1),
9328f44fb3bSmrg	    DATA(hand2),
9338f44fb3bSmrg	    DATA(heart),
9348f44fb3bSmrg	    DATA(icon),
9358f44fb3bSmrg	    DATA(iron_cross),
9368f44fb3bSmrg	    DATA(left_ptr),
9378f44fb3bSmrg	    DATA(left_side),
9388f44fb3bSmrg	    DATA(left_tee),
9398f44fb3bSmrg	    DATA(leftbutton),
9408f44fb3bSmrg	    DATA(ll_angle),
9418f44fb3bSmrg	    DATA(lr_angle),
9428f44fb3bSmrg	    DATA(man),
9438f44fb3bSmrg	    DATA(middlebutton),
9448f44fb3bSmrg	    DATA(mouse),
9458f44fb3bSmrg	    DATA(pencil),
9468f44fb3bSmrg	    DATA(pirate),
9478f44fb3bSmrg	    DATA(plus),
9488f44fb3bSmrg	    DATA(question_arrow),
9498f44fb3bSmrg	    DATA(right_ptr),
9508f44fb3bSmrg	    DATA(right_side),
9518f44fb3bSmrg	    DATA(right_tee),
9528f44fb3bSmrg	    DATA(rightbutton),
9538f44fb3bSmrg	    DATA(rtl_logo),
9548f44fb3bSmrg	    DATA(sailboat),
9558f44fb3bSmrg	    DATA(sb_down_arrow),
9568f44fb3bSmrg	    DATA(sb_h_double_arrow),
9578f44fb3bSmrg	    DATA(sb_left_arrow),
9588f44fb3bSmrg	    DATA(sb_right_arrow),
9598f44fb3bSmrg	    DATA(sb_up_arrow),
9608f44fb3bSmrg	    DATA(sb_v_double_arrow),
9618f44fb3bSmrg	    DATA(shuttle),
9628f44fb3bSmrg	    DATA(sizing),
9638f44fb3bSmrg	    DATA(spider),
9648f44fb3bSmrg	    DATA(spraycan),
9658f44fb3bSmrg	    DATA(star),
9668f44fb3bSmrg	    DATA(target),
9678f44fb3bSmrg	    DATA(tcross),
9688f44fb3bSmrg	    DATA(top_left_arrow),
9698f44fb3bSmrg	    DATA(top_left_corner),
9708f44fb3bSmrg	    DATA(top_right_corner),
9718f44fb3bSmrg	    DATA(top_side),
9728f44fb3bSmrg	    DATA(top_tee),
9738f44fb3bSmrg	    DATA(trek),
9748f44fb3bSmrg	    DATA(ul_angle),
9758f44fb3bSmrg	    DATA(umbrella),
9768f44fb3bSmrg	    DATA(ur_angle),
9778f44fb3bSmrg	    DATA(watch),
9788f44fb3bSmrg	    DATA(xterm),
9798f44fb3bSmrg    };
9808f44fb3bSmrg#undef DATA
9818f44fb3bSmrg    Cardinal j;
9828f44fb3bSmrg    int result = -1;
9838f44fb3bSmrg    if (!IsEmpty(name)) {
9848f44fb3bSmrg	for (j = 0; j < XtNumber(table); ++j) {
9858f44fb3bSmrg	    if (!strcmp(name, table[j].name)) {
9868f44fb3bSmrg		result = table[j].code;
9878f44fb3bSmrg		break;
9888f44fb3bSmrg	    }
9898f44fb3bSmrg	}
9908f44fb3bSmrg    }
9918f44fb3bSmrg    return result;
9928f44fb3bSmrg}
9938f44fb3bSmrg
9948f44fb3bSmrgvoid
9958f44fb3bSmrgxtermSetupPointer(XtermWidget xw, const char *theShape)
9968f44fb3bSmrg{
9978f44fb3bSmrg    TScreen *screen = TScreenOf(xw);
9988f44fb3bSmrg    unsigned shape = XC_xterm;
9998f44fb3bSmrg    int other = LookupCursorShape(theShape);
10008f44fb3bSmrg    unsigned which;
10018f44fb3bSmrg
10028f44fb3bSmrg    if (other >= 0 && other < XC_num_glyphs)
10038f44fb3bSmrg	shape = (unsigned) other;
10048f44fb3bSmrg
10058f44fb3bSmrg    TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
10068f44fb3bSmrg	   NonNull(theShape)));
10078f44fb3bSmrg
10088f44fb3bSmrg    which = (unsigned) (shape / 2);
10098f44fb3bSmrg    if (xw->work.pointer_cursors[which] == None) {
10108f44fb3bSmrg	TRACE(("creating text pointer cursor from shape %d\n", shape));
10118f44fb3bSmrg	xw->work.pointer_cursors[which] =
10128f44fb3bSmrg	    make_colored_cursor(shape,
10138f44fb3bSmrg				T_COLOR(screen, MOUSE_FG),
10148f44fb3bSmrg				T_COLOR(screen, MOUSE_BG));
10158f44fb3bSmrg    } else {
10168f44fb3bSmrg	TRACE(("updating text pointer cursor for shape %d\n", shape));
10178f44fb3bSmrg	recolor_cursor(screen,
10188f44fb3bSmrg		       screen->pointer_cursor,
10198f44fb3bSmrg		       T_COLOR(screen, MOUSE_FG),
10208f44fb3bSmrg		       T_COLOR(screen, MOUSE_BG));
10218f44fb3bSmrg    }
10228f44fb3bSmrg    if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
10238f44fb3bSmrg	screen->pointer_cursor = xw->work.pointer_cursors[which];
10248f44fb3bSmrg	TRACE(("defining text pointer cursor with shape %d\n", shape));
10258f44fb3bSmrg	XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
10268f44fb3bSmrg	if (XtIsRealized((Widget) xw)) {
10278f44fb3bSmrg	    /* briefly override pointerMode after changing the pointer */
10288f44fb3bSmrg	    if (screen->pointer_mode != pNever)
10298f44fb3bSmrg		screen->hide_pointer = True;
10308f44fb3bSmrg	    xtermShowPointer(xw, True);
10318f44fb3bSmrg	}
10328f44fb3bSmrg    }
10338f44fb3bSmrg}
10348f44fb3bSmrg
1035d522f475Smrg/* ARGSUSED */
1036d522f475Smrgvoid
1037d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
10389a64e1c5Smrg		 XEvent *event,
1039fa3f02f3Smrg		 String *params GCC_UNUSED,
1040d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
1041d522f475Smrg{
1042cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
1043cd3331d0Smrg    Input(term, &event->xkey, False);
1044d522f475Smrg}
1045d522f475Smrg
1046d522f475Smrg/* ARGSUSED */
1047d522f475Smrgvoid
1048d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
10499a64e1c5Smrg			 XEvent *event,
1050fa3f02f3Smrg			 String *params GCC_UNUSED,
1051d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
1052d522f475Smrg{
1053cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
1054cd3331d0Smrg    Input(term, &event->xkey, True);
1055d522f475Smrg}
1056d522f475Smrg
1057d522f475Smrg/* ARGSUSED */
1058d522f475Smrgvoid
1059d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
10609a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1061fa3f02f3Smrg		  String *params,
1062d522f475Smrg		  Cardinal *nparams)
1063d522f475Smrg{
1064d522f475Smrg
1065d522f475Smrg    if (*nparams != 1)
1066d522f475Smrg	return;
1067d522f475Smrg
1068d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
10690d92cbfdSchristos	const char *abcdef = "ABCDEF";
10700d92cbfdSchristos	const char *xxxxxx;
1071cd3331d0Smrg	Char c;
1072cd3331d0Smrg	UString p;
10730d92cbfdSchristos	unsigned value = 0;
10740d92cbfdSchristos
1075cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
10760d92cbfdSchristos	     '\0'; p++) {
10770d92cbfdSchristos	    value *= 16;
1078d522f475Smrg	    if (c >= '0' && c <= '9')
10790d92cbfdSchristos		value += (unsigned) (c - '0');
1080fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
10810d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
1082d522f475Smrg	    else
1083d522f475Smrg		break;
1084d522f475Smrg	}
10850d92cbfdSchristos	if (c == '\0') {
10860d92cbfdSchristos	    Char hexval[2];
10870d92cbfdSchristos	    hexval[0] = (Char) value;
10880d92cbfdSchristos	    hexval[1] = 0;
1089b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
10900d92cbfdSchristos	}
1091d522f475Smrg    } else {
1092cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
1093d522f475Smrg    }
1094d522f475Smrg}
1095d522f475Smrg
1096d522f475Smrg#if OPT_EXEC_XTERM
1097d522f475Smrg
1098d522f475Smrg#ifndef PROCFS_ROOT
1099d522f475Smrg#define PROCFS_ROOT "/proc"
1100d522f475Smrg#endif
1101d522f475Smrg
1102037a25ddSmrg/*
1103037a25ddSmrg * Determine the current working directory of the child so that we can
1104037a25ddSmrg * spawn a new terminal in the same directory.
1105037a25ddSmrg *
1106037a25ddSmrg * If we cannot get the CWD of the child, just use our own.
1107037a25ddSmrg */
1108037a25ddSmrgchar *
1109037a25ddSmrgProcGetCWD(pid_t pid)
1110037a25ddSmrg{
1111037a25ddSmrg    char *child_cwd = NULL;
1112037a25ddSmrg
1113037a25ddSmrg    if (pid) {
1114037a25ddSmrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
1115037a25ddSmrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
1116037a25ddSmrg	child_cwd = Readlink(child_cwd_link);
1117037a25ddSmrg    }
1118037a25ddSmrg    return child_cwd;
1119037a25ddSmrg}
1120037a25ddSmrg
1121d522f475Smrg/* ARGSUSED */
1122d522f475Smrgvoid
1123d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
11249a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1125fa3f02f3Smrg		    String *params,
1126d522f475Smrg		    Cardinal *nparams)
1127d522f475Smrg{
1128cd3331d0Smrg    TScreen *screen = TScreenOf(term);
1129d522f475Smrg    char *child_cwd = NULL;
1130d522f475Smrg    char *child_exe;
1131d522f475Smrg    pid_t pid;
1132d522f475Smrg
1133d522f475Smrg    /*
1134d522f475Smrg     * Try to find the actual program which is running in the child process.
1135d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
1136d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
1137d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
1138d522f475Smrg     */
1139d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
1140d522f475Smrg    if (!child_exe) {
1141cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
1142cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
1143d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
1144d522f475Smrg	} else {
11453367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
1146d522f475Smrg	}
1147d522f475Smrg	if (child_exe == 0)
1148d522f475Smrg	    return;
1149d522f475Smrg    }
1150d522f475Smrg
1151037a25ddSmrg    child_cwd = ProcGetCWD(screen->pid);
1152d522f475Smrg
1153d522f475Smrg    /* The reaper will take care of cleaning up the child */
1154d522f475Smrg    pid = fork();
1155d522f475Smrg    if (pid == -1) {
11563367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
1157d522f475Smrg    } else if (!pid) {
1158d522f475Smrg	/* We are the child */
1159d522f475Smrg	if (child_cwd) {
1160cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
1161d522f475Smrg	}
1162d522f475Smrg
1163d522f475Smrg	if (setuid(screen->uid) == -1
1164d522f475Smrg	    || setgid(screen->gid) == -1) {
11653367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
1166d522f475Smrg	} else {
11670d92cbfdSchristos	    unsigned myargc = *nparams + 1;
1168d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
1169d522f475Smrg
117094644356Smrg	    if (myargv != 0) {
117194644356Smrg		unsigned n = 0;
1172d522f475Smrg
117394644356Smrg		myargv[n++] = child_exe;
1174d522f475Smrg
117594644356Smrg		while (n < myargc) {
117694644356Smrg		    myargv[n++] = (char *) *params++;
117794644356Smrg		}
117894644356Smrg
117994644356Smrg		myargv[n] = 0;
118094644356Smrg		execv(child_exe, myargv);
118194644356Smrg	    }
1182d522f475Smrg
1183d522f475Smrg	    /* If we get here, we've failed */
11843367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
1185d522f475Smrg	}
1186d522f475Smrg	_exit(0);
1187d522f475Smrg    }
11883367019cSmrg
11893367019cSmrg    /* We are the parent; clean up */
1190d4fba8b9Smrg    free(child_cwd);
11913367019cSmrg    free(child_exe);
1192d522f475Smrg}
1193d522f475Smrg#endif /* OPT_EXEC_XTERM */
1194d522f475Smrg
1195d522f475Smrg/*
1196d522f475Smrg * Rather than sending characters to the host, put them directly into our
1197d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
1198d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
1199d522f475Smrg *
1200d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
1201d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
1202d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
1203d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
1204d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
1205d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
1206d522f475Smrg */
1207d522f475Smrg/* ARGSUSED */
1208d522f475Smrgvoid
1209d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
12109a64e1c5Smrg		XEvent *event GCC_UNUSED,
1211fa3f02f3Smrg		String *params,
1212d522f475Smrg		Cardinal *param_count)
1213d522f475Smrg{
1214d522f475Smrg    if (*param_count == 1) {
1215cd3331d0Smrg	const char *value = params[0];
1216b7c89284Ssnj	int need = (int) strlen(value);
1217cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
1218cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
1219d522f475Smrg
1220d522f475Smrg	if (have - used + need < BUF_SIZE) {
1221d522f475Smrg
1222cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
1223d522f475Smrg
1224d522f475Smrg	    TRACE(("Interpret %s\n", value));
1225d522f475Smrg	    VTbuffer->update++;
1226d522f475Smrg	}
1227d522f475Smrg    }
1228d522f475Smrg}
1229d522f475Smrg
1230d522f475Smrg/*ARGSUSED*/
1231d522f475Smrgvoid
1232d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
1233d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12349a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1235fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1236d522f475Smrg{
1237d522f475Smrg    /* NOP since we handled it above */
1238d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
1239cd3331d0Smrg    TRACE_FOCUS(w, event);
1240d522f475Smrg}
1241d522f475Smrg
1242d522f475Smrg/*ARGSUSED*/
1243d522f475Smrgvoid
1244d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
1245d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12469a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1247fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1248d522f475Smrg{
1249d522f475Smrg    /* NOP since we handled it above */
1250d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1251cd3331d0Smrg    TRACE_FOCUS(w, event);
1252d522f475Smrg}
1253d522f475Smrg
1254d522f475Smrg/*ARGSUSED*/
1255d522f475Smrgvoid
1256d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1257d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12589a64e1c5Smrg		  XEvent *ev,
1259fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1260d522f475Smrg{
1261d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1262d522f475Smrg    XtermWidget xw = term;
1263d522f475Smrg    TScreen *screen = TScreenOf(xw);
1264d522f475Smrg
12653367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1266d522f475Smrg	   visibleEventType(event->type),
12673367019cSmrg	   visibleNotifyMode(event->mode),
12683367019cSmrg	   visibleNotifyDetail(event->detail)));
1269cd3331d0Smrg    TRACE_FOCUS(xw, event);
1270d522f475Smrg
1271d522f475Smrg    if (screen->quiet_grab
1272d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1273c219fbebSmrg	/* EMPTY */ ;
1274d522f475Smrg    } else if (event->type == FocusIn) {
127594644356Smrg	if (event->detail != NotifyPointer) {
127694644356Smrg	    setXUrgency(xw, False);
127794644356Smrg	}
1278d522f475Smrg
1279d522f475Smrg	/*
1280d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1281d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1282d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1283d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1284d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1285d522f475Smrg	 * delivered to the obscured window.
1286d522f475Smrg	 */
1287d522f475Smrg	if (event->detail == NotifyNonlinear
1288d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
12893367019cSmrg	    unselectwindow(xw, INWINDOW);
1290d522f475Smrg	}
12913367019cSmrg	selectwindow(xw,
1292d522f475Smrg		     ((event->detail == NotifyPointer)
1293d522f475Smrg		      ? INWINDOW
1294d522f475Smrg		      : FOCUS));
1295d522f475Smrg	SendFocusButton(xw, event);
1296d522f475Smrg    } else {
1297d522f475Smrg#if OPT_FOCUS_EVENT
1298d522f475Smrg	if (event->type == FocusOut) {
1299d522f475Smrg	    SendFocusButton(xw, event);
1300d522f475Smrg	}
1301d522f475Smrg#endif
1302d522f475Smrg	/*
1303d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1304d522f475Smrg	 * ignore.
1305d522f475Smrg	 */
1306d522f475Smrg	if (event->mode != NotifyGrab) {
13073367019cSmrg	    unselectwindow(xw,
1308d522f475Smrg			   ((event->detail == NotifyPointer)
1309d522f475Smrg			    ? INWINDOW
1310d522f475Smrg			    : FOCUS));
1311d522f475Smrg	}
1312d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1313cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1314d522f475Smrg	    ReverseVideo(xw);
1315d522f475Smrg	    screen->grabbedKbd = False;
1316d522f475Smrg	    update_securekbd();
1317d522f475Smrg	}
1318d522f475Smrg    }
1319d522f475Smrg}
1320d522f475Smrg
1321d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1322d522f475Smrg
1323b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1324b7c89284Ssnjstatic Atom
1325b7c89284SsnjAtomBell(XtermWidget xw, int which)
1326b7c89284Ssnj{
1327b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1328b7c89284Ssnj    static struct {
1329b7c89284Ssnj	int value;
1330b7c89284Ssnj	const char *name;
1331b7c89284Ssnj    } table[] = {
1332b7c89284Ssnj	DATA(Info),
1333b7c89284Ssnj	    DATA(MarginBell),
1334b7c89284Ssnj	    DATA(MinorError),
1335b7c89284Ssnj	    DATA(TerminalBell)
1336b7c89284Ssnj    };
1337d4fba8b9Smrg#undef DATA
1338b7c89284Ssnj    Cardinal n;
1339b7c89284Ssnj    Atom result = None;
1340b7c89284Ssnj
1341b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1342b7c89284Ssnj	if (table[n].value == which) {
1343cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1344b7c89284Ssnj	    break;
1345b7c89284Ssnj	}
1346b7c89284Ssnj    }
1347b7c89284Ssnj    return result;
1348b7c89284Ssnj}
1349b7c89284Ssnj#endif
1350b7c89284Ssnj
1351d522f475Smrgvoid
1352b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1353d522f475Smrg{
1354b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1355b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1356b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1357cd3331d0Smrg#endif
1358cd3331d0Smrg
1359cd3331d0Smrg    switch (which) {
1360cd3331d0Smrg    case XkbBI_Info:
1361cd3331d0Smrg    case XkbBI_MinorError:
1362cd3331d0Smrg    case XkbBI_MajorError:
1363cd3331d0Smrg    case XkbBI_TerminalBell:
1364cd3331d0Smrg	switch (screen->warningVolume) {
1365cd3331d0Smrg	case bvOff:
1366cd3331d0Smrg	    percent = -100;
1367cd3331d0Smrg	    break;
1368cd3331d0Smrg	case bvLow:
1369cd3331d0Smrg	    break;
1370cd3331d0Smrg	case bvHigh:
1371cd3331d0Smrg	    percent = 100;
1372cd3331d0Smrg	    break;
1373cd3331d0Smrg	}
1374cd3331d0Smrg	break;
1375cd3331d0Smrg    case XkbBI_MarginBell:
1376cd3331d0Smrg	switch (screen->marginVolume) {
1377cd3331d0Smrg	case bvOff:
1378cd3331d0Smrg	    percent = -100;
1379cd3331d0Smrg	    break;
1380cd3331d0Smrg	case bvLow:
1381cd3331d0Smrg	    break;
1382cd3331d0Smrg	case bvHigh:
1383cd3331d0Smrg	    percent = 100;
1384cd3331d0Smrg	    break;
1385cd3331d0Smrg	}
1386cd3331d0Smrg	break;
1387cd3331d0Smrg    default:
1388cd3331d0Smrg	break;
1389cd3331d0Smrg    }
1390cd3331d0Smrg
1391cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1392b7c89284Ssnj    if (tony != None) {
1393c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1394b7c89284Ssnj    } else
1395b7c89284Ssnj#endif
1396b7c89284Ssnj	XBell(screen->display, percent);
1397b7c89284Ssnj}
1398b7c89284Ssnj
1399b7c89284Ssnjvoid
1400cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1401b7c89284Ssnj{
1402b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1403d522f475Smrg    struct timeval curtime;
1404d522f475Smrg
1405b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1406b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1407d522f475Smrg	return;
1408d522f475Smrg    }
1409d522f475Smrg
1410c219fbebSmrg    setXUrgency(xw, True);
1411d522f475Smrg
1412d522f475Smrg    /* has enough time gone by that we are allowed to ring
1413d522f475Smrg       the bell again? */
1414d522f475Smrg    if (screen->bellSuppressTime) {
1415037a25ddSmrg	long now_msecs;
1416037a25ddSmrg
1417d522f475Smrg	if (screen->bellInProgress) {
1418d4fba8b9Smrg	    do_xevents(xw);
1419d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1420d522f475Smrg		return;
1421d522f475Smrg	    }
1422d522f475Smrg	}
1423d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1424d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1425d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1426d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1427d522f475Smrg	    return;
1428d522f475Smrg	}
1429d522f475Smrg	lastBellTime = now_msecs;
1430d522f475Smrg    }
1431d522f475Smrg
1432d522f475Smrg    if (screen->visualbell) {
1433d522f475Smrg	VisualBell();
1434d522f475Smrg    } else {
1435b7c89284Ssnj	xtermBell(xw, which, percent);
1436d522f475Smrg    }
1437d522f475Smrg
1438d522f475Smrg    if (screen->poponbell)
1439c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1440d522f475Smrg
1441d522f475Smrg    if (screen->bellSuppressTime) {
1442d522f475Smrg	/* now we change a property and wait for the notify event to come
1443d522f475Smrg	   back.  If the server is suspending operations while the bell
1444d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1445d522f475Smrg	   know when the previous bell has finished */
1446d522f475Smrg	Widget w = CURRENT_EMU();
1447d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1448d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1449d522f475Smrg	screen->bellInProgress = True;
1450d522f475Smrg    }
1451d522f475Smrg}
1452d522f475Smrg
1453d522f475Smrgstatic void
1454fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1455d522f475Smrg{
14563367019cSmrg    int y = 0;
14573367019cSmrg    int x = 0;
14583367019cSmrg
14593367019cSmrg    if (screen->flash_line) {
14603367019cSmrg	y = CursorY(screen, screen->cur_row);
14613367019cSmrg	height = (unsigned) FontHeight(screen);
14623367019cSmrg    }
14633367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1464d522f475Smrg    XFlush(screen->display);
1465d522f475Smrg    Sleep(VB_DELAY);
14663367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1467d522f475Smrg}
1468d522f475Smrg
1469d522f475Smrgvoid
1470d522f475SmrgVisualBell(void)
1471d522f475Smrg{
1472d4fba8b9Smrg    XtermWidget xw = term;
1473d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1474d522f475Smrg
1475d522f475Smrg    if (VB_DELAY > 0) {
1476d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1477d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1478d522f475Smrg	XGCValues gcval;
1479d522f475Smrg	GC visualGC;
1480d522f475Smrg
1481d522f475Smrg	gcval.function = GXxor;
1482d522f475Smrg	gcval.foreground = xorPixel;
1483d4fba8b9Smrg	visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
1484d522f475Smrg#if OPT_TEK4014
1485d4fba8b9Smrg	if (TEK4014_ACTIVE(xw)) {
1486cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1487d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1488d522f475Smrg			TFullWidth(tekscr),
1489d522f475Smrg			TFullHeight(tekscr));
1490d522f475Smrg	} else
1491d522f475Smrg#endif
1492d522f475Smrg	{
1493d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1494d522f475Smrg			FullWidth(screen),
1495d522f475Smrg			FullHeight(screen));
1496d522f475Smrg	}
1497d4fba8b9Smrg	XtReleaseGC((Widget) xw, visualGC);
1498d522f475Smrg    }
1499d522f475Smrg}
1500d522f475Smrg
1501d522f475Smrg/* ARGSUSED */
1502d522f475Smrgvoid
1503d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1504d522f475Smrg			 XtPointer data GCC_UNUSED,
15059a64e1c5Smrg			 XEvent *ev,
1506fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1507d522f475Smrg{
1508d522f475Smrg    TScreen *screen = TScreenOf(term);
1509d522f475Smrg
1510d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1511d522f475Smrg	screen->bellInProgress = False;
1512d522f475Smrg    }
1513d522f475Smrg}
1514d522f475Smrg
15153367019cSmrgvoid
1516d4fba8b9SmrgxtermWarning(const char *fmt, ...)
15173367019cSmrg{
15183367019cSmrg    int save_err = errno;
15193367019cSmrg    va_list ap;
15203367019cSmrg
1521dfb07bc7Smrg    fflush(stdout);
1522d4fba8b9Smrg
1523d4fba8b9Smrg#if OPT_TRACE
1524d4fba8b9Smrg    va_start(ap, fmt);
1525d4fba8b9Smrg    Trace("xtermWarning: ");
1526d4fba8b9Smrg    TraceVA(fmt, ap);
1527d4fba8b9Smrg    va_end(ap);
1528d4fba8b9Smrg#endif
1529d4fba8b9Smrg
15303367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15313367019cSmrg    va_start(ap, fmt);
15323367019cSmrg    vfprintf(stderr, fmt, ap);
15333367019cSmrg    (void) fflush(stderr);
15343367019cSmrg
15353367019cSmrg    va_end(ap);
15363367019cSmrg    errno = save_err;
15373367019cSmrg}
15383367019cSmrg
15393367019cSmrgvoid
1540d4fba8b9SmrgxtermPerror(const char *fmt, ...)
15413367019cSmrg{
15423367019cSmrg    int save_err = errno;
1543d4fba8b9Smrg    const char *msg = strerror(errno);
15443367019cSmrg    va_list ap;
15453367019cSmrg
1546dfb07bc7Smrg    fflush(stdout);
1547d4fba8b9Smrg
1548d4fba8b9Smrg#if OPT_TRACE
1549d4fba8b9Smrg    va_start(ap, fmt);
1550d4fba8b9Smrg    Trace("xtermPerror: ");
1551d4fba8b9Smrg    TraceVA(fmt, ap);
1552d4fba8b9Smrg    va_end(ap);
1553d4fba8b9Smrg#endif
1554d4fba8b9Smrg
15553367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15563367019cSmrg    va_start(ap, fmt);
15573367019cSmrg    vfprintf(stderr, fmt, ap);
15583367019cSmrg    fprintf(stderr, ": %s\n", msg);
15593367019cSmrg    (void) fflush(stderr);
15603367019cSmrg
15613367019cSmrg    va_end(ap);
15623367019cSmrg    errno = save_err;
15633367019cSmrg}
15643367019cSmrg
1565d522f475SmrgWindow
1566c219fbebSmrgWMFrameWindow(XtermWidget xw)
1567d522f475Smrg{
1568d522f475Smrg    Window win_root, win_current, *children;
1569d522f475Smrg    Window win_parent = 0;
1570d522f475Smrg    unsigned int nchildren;
1571d522f475Smrg
1572c219fbebSmrg    win_current = XtWindow(xw);
1573d522f475Smrg
1574d522f475Smrg    /* find the parent which is child of root */
1575d522f475Smrg    do {
1576d522f475Smrg	if (win_parent)
1577d522f475Smrg	    win_current = win_parent;
1578c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1579d522f475Smrg		   win_current,
1580d522f475Smrg		   &win_root,
1581d522f475Smrg		   &win_parent,
1582d522f475Smrg		   &children,
1583d522f475Smrg		   &nchildren);
1584d522f475Smrg	XFree(children);
1585d522f475Smrg    } while (win_root != win_parent);
1586d522f475Smrg
1587d522f475Smrg    return win_current;
1588d522f475Smrg}
1589d522f475Smrg
1590d522f475Smrg#if OPT_DABBREV
1591d522f475Smrg/*
1592d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1593d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1594d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1595d522f475Smrg * expansions and ignores one of them if they are identical.
1596d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1597d522f475Smrg */
1598d522f475Smrg
1599d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1600d522f475Smrg
1601d522f475Smrgstatic int
1602fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1603d522f475Smrg{
1604b7c89284Ssnj    int result = -1;
1605b7c89284Ssnj    int firstLine = -(screen->savedlines);
1606d522f475Smrg
1607b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1608b7c89284Ssnj    while (cell->row >= firstLine) {
1609b7c89284Ssnj	if (--(cell->col) >= 0) {
1610b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1611b7c89284Ssnj	    break;
1612b7c89284Ssnj	}
1613b7c89284Ssnj	if (--(cell->row) < firstLine)
1614b7c89284Ssnj	    break;		/* ...there is no previous line */
1615b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1616b7c89284Ssnj	cell->col = MaxCols(screen);
1617b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1618b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1619d522f475Smrg	    break;
1620b7c89284Ssnj	}
1621d522f475Smrg    }
1622b7c89284Ssnj    return result;
1623d522f475Smrg}
1624d522f475Smrg
1625d522f475Smrgstatic char *
16269a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1627d522f475Smrg{
16289a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1629d522f475Smrg    char *abword;
1630d522f475Smrg    int c;
16319a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1632b7c89284Ssnj    char *result = 0;
1633d522f475Smrg
1634b7c89284Ssnj    abword = ab_end;
1635d522f475Smrg    *abword = '\0';		/* end of string marker */
1636d522f475Smrg
1637b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1638b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
16399a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1640b7c89284Ssnj	    *(--abword) = (char) c;
1641d522f475Smrg    }
1642d522f475Smrg
1643b7c89284Ssnj    if (c >= 0) {
1644b7c89284Ssnj	result = abword;
1645b7c89284Ssnj    } else if (abword != ab_end) {
1646b7c89284Ssnj	result = abword;
1647b7c89284Ssnj    }
1648b7c89284Ssnj
1649b7c89284Ssnj    if (result != 0) {
1650b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1651b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1652b7c89284Ssnj	    ;			/* skip preceding spaces */
1653b7c89284Ssnj	}
1654b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1655b7c89284Ssnj    }
1656b7c89284Ssnj    return result;
1657d522f475Smrg}
1658d522f475Smrg
1659d522f475Smrgstatic int
16609a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1661d522f475Smrg{
16629a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1663d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1664d522f475Smrg
1665b7c89284Ssnj    static CELL cell;
1666d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1667d522f475Smrg    static unsigned int expansions;
1668d522f475Smrg
1669d522f475Smrg    char *expansion;
1670d522f475Smrg    size_t hint_len;
1671b7c89284Ssnj    int result = 0;
1672b7c89284Ssnj    LineData *ld;
1673d522f475Smrg
1674d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1675d522f475Smrg	expansions = 0;
1676b7c89284Ssnj	cell.col = screen->cur_col;
1677b7c89284Ssnj	cell.row = screen->cur_row;
1678b7c89284Ssnj
1679d4fba8b9Smrg	free(dabbrev_hint);
1680b7c89284Ssnj
16819a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1682b7c89284Ssnj
1683d4fba8b9Smrg	    free(lastexpansion);
1684b7c89284Ssnj
1685b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1686b7c89284Ssnj
1687b7c89284Ssnj		/* make own copy */
1688b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1689b7c89284Ssnj		    screen->dabbrev_working = True;
1690b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1691b7c89284Ssnj		}
1692cd3331d0Smrg	    } else {
1693cd3331d0Smrg		return result;
1694b7c89284Ssnj	    }
1695cd3331d0Smrg	} else {
1696cd3331d0Smrg	    return result;
1697d522f475Smrg	}
1698b7c89284Ssnj	if (!screen->dabbrev_working) {
1699d4fba8b9Smrg	    free(lastexpansion);
1700d4fba8b9Smrg	    lastexpansion = 0;
1701b7c89284Ssnj	    return result;
1702b7c89284Ssnj	}
1703d522f475Smrg    }
1704d522f475Smrg
1705cd3331d0Smrg    if (dabbrev_hint == 0)
1706cd3331d0Smrg	return result;
1707cd3331d0Smrg
1708d522f475Smrg    hint_len = strlen(dabbrev_hint);
1709d522f475Smrg    for (;;) {
17109a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1711d522f475Smrg	    if (expansions >= 2) {
1712d522f475Smrg		expansions = 0;
1713b7c89284Ssnj		cell.col = screen->cur_col;
1714b7c89284Ssnj		cell.row = screen->cur_row;
1715d522f475Smrg		continue;
1716d522f475Smrg	    }
1717d522f475Smrg	    break;
1718d522f475Smrg	}
1719d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1720d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1721d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1722d522f475Smrg	    break;
1723d522f475Smrg    }
1724d522f475Smrg
1725b7c89284Ssnj    if (expansion != 0) {
1726037a25ddSmrg	Char *copybuffer;
1727037a25ddSmrg	size_t del_cnt = strlen(lastexpansion) - hint_len;
1728037a25ddSmrg	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1729b7c89284Ssnj
1730b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1731b7c89284Ssnj	    /* delete previous expansion */
1732b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1733b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1734b7c89284Ssnj		    expansion + hint_len,
1735b7c89284Ssnj		    strlen(expansion) - hint_len);
1736cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1737b7c89284Ssnj	    /* v_write() just reset our flag */
1738b7c89284Ssnj	    screen->dabbrev_working = True;
1739b7c89284Ssnj	    free(copybuffer);
1740b7c89284Ssnj
1741b7c89284Ssnj	    free(lastexpansion);
1742b7c89284Ssnj
1743b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1744b7c89284Ssnj		result = 1;
1745b7c89284Ssnj		expansions++;
1746b7c89284Ssnj	    }
1747b7c89284Ssnj	}
1748b7c89284Ssnj    }
1749b7c89284Ssnj
1750b7c89284Ssnj    return result;
1751d522f475Smrg}
1752d522f475Smrg
1753d522f475Smrg/*ARGSUSED*/
1754d522f475Smrgvoid
1755b7c89284SsnjHandleDabbrevExpand(Widget w,
17569a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1757fa3f02f3Smrg		    String *params GCC_UNUSED,
1758d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1759d522f475Smrg{
1760b7c89284Ssnj    XtermWidget xw;
1761b7c89284Ssnj
1762cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1763b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
17649a64e1c5Smrg	if (!dabbrev_expand(xw))
1765cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1766d522f475Smrg    }
1767d522f475Smrg}
1768d522f475Smrg#endif /* OPT_DABBREV */
1769d522f475Smrg
1770d4fba8b9Smrgvoid
1771d4fba8b9SmrgxtermDeiconify(XtermWidget xw)
1772d4fba8b9Smrg{
1773d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1774d4fba8b9Smrg    Display *dpy = screen->display;
1775d4fba8b9Smrg    Window target = VShellWindow(xw);
1776d4fba8b9Smrg    XEvent e;
1777d4fba8b9Smrg    Atom atom_state = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1778d4fba8b9Smrg
1779d4fba8b9Smrg    if (xtermIsIconified(xw)) {
1780d4fba8b9Smrg	TRACE(("...de-iconify window %#lx\n", target));
1781d4fba8b9Smrg	XMapWindow(dpy, target);
1782d4fba8b9Smrg
1783d4fba8b9Smrg	memset(&e, 0, sizeof(e));
1784d4fba8b9Smrg	e.xclient.type = ClientMessage;
1785d4fba8b9Smrg	e.xclient.message_type = atom_state;
1786d4fba8b9Smrg	e.xclient.display = dpy;
1787d4fba8b9Smrg	e.xclient.window = target;
1788d4fba8b9Smrg	e.xclient.format = 32;
1789d4fba8b9Smrg	e.xclient.data.l[0] = 1;
1790d4fba8b9Smrg	e.xclient.data.l[1] = CurrentTime;
1791d4fba8b9Smrg
1792d4fba8b9Smrg	XSendEvent(dpy, DefaultRootWindow(dpy), False,
1793d4fba8b9Smrg		   SubstructureRedirectMask | SubstructureNotifyMask, &e);
1794d4fba8b9Smrg	xevents(xw);
1795d4fba8b9Smrg    }
1796d4fba8b9Smrg}
1797d4fba8b9Smrg
1798d4fba8b9Smrgvoid
1799d4fba8b9SmrgxtermIconify(XtermWidget xw)
1800d4fba8b9Smrg{
1801d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1802d4fba8b9Smrg    Window target = VShellWindow(xw);
1803d4fba8b9Smrg
1804d4fba8b9Smrg    if (!xtermIsIconified(xw)) {
1805d4fba8b9Smrg	TRACE(("...iconify window %#lx\n", target));
1806d4fba8b9Smrg	XIconifyWindow(screen->display,
1807d4fba8b9Smrg		       target,
1808d4fba8b9Smrg		       DefaultScreen(screen->display));
1809d4fba8b9Smrg	xevents(xw);
1810d4fba8b9Smrg    }
1811d4fba8b9Smrg}
1812d4fba8b9Smrg
1813d4fba8b9SmrgBoolean
1814d4fba8b9SmrgxtermIsIconified(XtermWidget xw)
1815d4fba8b9Smrg{
1816d4fba8b9Smrg    XWindowAttributes win_attrs;
1817d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1818d4fba8b9Smrg    Window target = VShellWindow(xw);
1819d4fba8b9Smrg    Display *dpy = screen->display;
1820d4fba8b9Smrg    Boolean result = False;
1821d4fba8b9Smrg
1822d4fba8b9Smrg    if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
1823d4fba8b9Smrg	Atom actual_return_type;
1824d4fba8b9Smrg	int actual_format_return = 0;
1825d4fba8b9Smrg	unsigned long nitems_return = 0;
1826d4fba8b9Smrg	unsigned long bytes_after_return = 0;
1827d4fba8b9Smrg	unsigned char *prop_return = 0;
1828d4fba8b9Smrg	long long_length = 1024;
1829d4fba8b9Smrg	Atom requested_type = XA_ATOM;
1830d4fba8b9Smrg	Atom is_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
1831d4fba8b9Smrg	Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
1832d4fba8b9Smrg
1833d4fba8b9Smrg	/* this works with non-EWMH */
1834d4fba8b9Smrg	result = (win_attrs.map_state != IsViewable) ? True : False;
1835d4fba8b9Smrg
1836d4fba8b9Smrg	/* this is a convention used by some EWMH applications */
1837d4fba8b9Smrg	if (xtermGetWinProp(dpy,
1838d4fba8b9Smrg			    target,
1839d4fba8b9Smrg			    wm_state,
1840d4fba8b9Smrg			    0L,
1841d4fba8b9Smrg			    long_length,
1842d4fba8b9Smrg			    requested_type,
1843d4fba8b9Smrg			    &actual_return_type,
1844d4fba8b9Smrg			    &actual_format_return,
1845d4fba8b9Smrg			    &nitems_return,
1846d4fba8b9Smrg			    &bytes_after_return,
184750027b5bSmrg			    &prop_return)) {
184850027b5bSmrg	    if (prop_return != 0
184950027b5bSmrg		&& actual_return_type == requested_type
185050027b5bSmrg		&& actual_format_return == 32) {
185150027b5bSmrg		unsigned long n;
185250027b5bSmrg		for (n = 0; n < nitems_return; ++n) {
185350027b5bSmrg		    unsigned long check = (((unsigned long *)
185450027b5bSmrg					    (void *) prop_return)[n]);
185550027b5bSmrg		    if (check == is_hidden) {
185650027b5bSmrg			result = True;
185750027b5bSmrg			break;
185850027b5bSmrg		    }
1859d4fba8b9Smrg		}
186050027b5bSmrg		XFree(prop_return);
1861d4fba8b9Smrg	    }
1862d4fba8b9Smrg	}
1863d4fba8b9Smrg    }
1864d4fba8b9Smrg    TRACE(("...window %#lx is%s iconified\n",
1865d4fba8b9Smrg	   target,
1866d4fba8b9Smrg	   result ? "" : " not"));
1867d4fba8b9Smrg    return result;
1868d4fba8b9Smrg}
1869d4fba8b9Smrg
1870d522f475Smrg#if OPT_MAXIMIZE
1871d522f475Smrg/*ARGSUSED*/
1872d522f475Smrgvoid
1873b7c89284SsnjHandleDeIconify(Widget w,
18749a64e1c5Smrg		XEvent *event GCC_UNUSED,
1875fa3f02f3Smrg		String *params GCC_UNUSED,
1876d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1877d522f475Smrg{
1878b7c89284Ssnj    XtermWidget xw;
1879b7c89284Ssnj
1880b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1881d4fba8b9Smrg	xtermDeiconify(xw);
1882d522f475Smrg    }
1883d522f475Smrg}
1884d522f475Smrg
1885d522f475Smrg/*ARGSUSED*/
1886d522f475Smrgvoid
1887b7c89284SsnjHandleIconify(Widget w,
18889a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1889fa3f02f3Smrg	      String *params GCC_UNUSED,
1890d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1891d522f475Smrg{
1892b7c89284Ssnj    XtermWidget xw;
1893b7c89284Ssnj
1894b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1895d4fba8b9Smrg	xtermIconify(xw);
1896d522f475Smrg    }
1897d522f475Smrg}
1898d522f475Smrg
1899d522f475Smrgint
1900c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1901d522f475Smrg{
1902c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1903d522f475Smrg    XSizeHints hints;
1904d522f475Smrg    long supp = 0;
1905d522f475Smrg    Window root_win;
1906d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1907d522f475Smrg    int root_y = -1;
1908d522f475Smrg    unsigned root_border;
1909d522f475Smrg    unsigned root_depth;
19103367019cSmrg    int code;
1911d522f475Smrg
1912d522f475Smrg    if (XGetGeometry(screen->display,
1913c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1914d522f475Smrg		     &root_win,
1915d522f475Smrg		     &root_x,
1916d522f475Smrg		     &root_y,
1917d522f475Smrg		     width,
1918d522f475Smrg		     height,
1919d522f475Smrg		     &root_border,
1920d522f475Smrg		     &root_depth)) {
1921d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1922d522f475Smrg	       root_x,
1923d522f475Smrg	       root_y,
1924d522f475Smrg	       *width,
1925d522f475Smrg	       *height,
1926d522f475Smrg	       root_border));
1927d522f475Smrg
1928d522f475Smrg	*width -= (root_border * 2);
1929d522f475Smrg	*height -= (root_border * 2);
1930d522f475Smrg
1931d522f475Smrg	hints.flags = PMaxSize;
1932d522f475Smrg	if (XGetWMNormalHints(screen->display,
1933c219fbebSmrg			      VShellWindow(xw),
1934d522f475Smrg			      &hints,
1935d522f475Smrg			      &supp)
1936d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1937d522f475Smrg
1938d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1939d522f475Smrg		   hints.max_width,
1940d522f475Smrg		   hints.max_height));
1941d522f475Smrg
1942d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1943b7c89284Ssnj		*width = (unsigned) hints.max_width;
1944d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1945b7c89284Ssnj		*height = (unsigned) hints.max_height;
1946d522f475Smrg	}
19473367019cSmrg	code = 1;
19483367019cSmrg    } else {
19493367019cSmrg	*width = 0;
19503367019cSmrg	*height = 0;
19513367019cSmrg	code = 0;
1952d522f475Smrg    }
19533367019cSmrg    return code;
1954d522f475Smrg}
1955d522f475Smrg
1956d522f475Smrgvoid
1957c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1958d522f475Smrg{
1959c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1960d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1961d4fba8b9Smrg    unsigned root_width = 0, root_height = 0;
19623367019cSmrg    Boolean success = False;
1963d522f475Smrg
19643367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
19653367019cSmrg	   maximize,
19663367019cSmrg	   (maximize
19673367019cSmrg	    ? "maximize"
19683367019cSmrg	    : "restore")));
1969d522f475Smrg
19703367019cSmrg    /*
19713367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
19723367019cSmrg     * as well as the estimated root-window size.
19733367019cSmrg     */
19743367019cSmrg    if (maximize
19753367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
19763367019cSmrg	&& xtermGetWinAttrs(screen->display,
19773367019cSmrg			    WMFrameWindow(xw),
19783367019cSmrg			    &wm_attrs)
19793367019cSmrg	&& xtermGetWinAttrs(screen->display,
19803367019cSmrg			    VShellWindow(xw),
19813367019cSmrg			    &vshell_attrs)) {
19823367019cSmrg
19833367019cSmrg	if (screen->restore_data != True
19843367019cSmrg	    || screen->restore_width != root_width
19853367019cSmrg	    || screen->restore_height != root_height) {
19863367019cSmrg	    screen->restore_data = True;
1987d4fba8b9Smrg	    screen->restore_x = wm_attrs.x;
1988d4fba8b9Smrg	    screen->restore_y = wm_attrs.y;
19893367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
19903367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
19913367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1992d522f475Smrg		   screen->restore_x,
1993d522f475Smrg		   screen->restore_y,
1994d522f475Smrg		   screen->restore_width,
1995d522f475Smrg		   screen->restore_height));
19963367019cSmrg	}
1997d522f475Smrg
19983367019cSmrg	/* subtract wm decoration dimensions */
1999d4fba8b9Smrg	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
2000d4fba8b9Smrg	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
20013367019cSmrg	success = True;
20023367019cSmrg    } else if (screen->restore_data) {
20033367019cSmrg	success = True;
20043367019cSmrg	maximize = 0;
20053367019cSmrg    }
20063367019cSmrg
20073367019cSmrg    if (success) {
20083367019cSmrg	switch (maximize) {
20093367019cSmrg	case 3:
20103367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
20113367019cSmrg	    break;
20123367019cSmrg	case 2:
20133367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
20143367019cSmrg	    break;
20153367019cSmrg	case 1:
20163367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
2017d4fba8b9Smrg	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
2018d4fba8b9Smrg		   0,
2019d4fba8b9Smrg		   0,
2020d4fba8b9Smrg		   root_width,
2021d4fba8b9Smrg		   root_height));
20223367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
2023d4fba8b9Smrg			      0,	/* x */
2024d4fba8b9Smrg			      0,	/* y */
20253367019cSmrg			      root_width,
20263367019cSmrg			      root_height);
20273367019cSmrg	    break;
20283367019cSmrg
20293367019cSmrg	default:
20303367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
20313367019cSmrg	    if (screen->restore_data) {
20323367019cSmrg		screen->restore_data = False;
20333367019cSmrg
2034d4fba8b9Smrg		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
20353367019cSmrg		       screen->restore_x,
20363367019cSmrg		       screen->restore_y,
20373367019cSmrg		       screen->restore_width,
20383367019cSmrg		       screen->restore_height));
20393367019cSmrg
20403367019cSmrg		XMoveResizeWindow(screen->display,
20413367019cSmrg				  VShellWindow(xw),
20423367019cSmrg				  screen->restore_x,
20433367019cSmrg				  screen->restore_y,
20443367019cSmrg				  screen->restore_width,
20453367019cSmrg				  screen->restore_height);
20463367019cSmrg	    }
20473367019cSmrg	    break;
2048d522f475Smrg	}
2049d522f475Smrg    }
2050d522f475Smrg}
2051d522f475Smrg
2052d522f475Smrg/*ARGSUSED*/
2053d522f475Smrgvoid
2054b7c89284SsnjHandleMaximize(Widget w,
20559a64e1c5Smrg	       XEvent *event GCC_UNUSED,
2056fa3f02f3Smrg	       String *params GCC_UNUSED,
2057d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
2058d522f475Smrg{
2059b7c89284Ssnj    XtermWidget xw;
2060b7c89284Ssnj
2061b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2062b7c89284Ssnj	RequestMaximize(xw, 1);
2063d522f475Smrg    }
2064d522f475Smrg}
2065d522f475Smrg
2066d522f475Smrg/*ARGSUSED*/
2067d522f475Smrgvoid
2068b7c89284SsnjHandleRestoreSize(Widget w,
20699a64e1c5Smrg		  XEvent *event GCC_UNUSED,
2070fa3f02f3Smrg		  String *params GCC_UNUSED,
2071d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
2072d522f475Smrg{
2073b7c89284Ssnj    XtermWidget xw;
2074b7c89284Ssnj
2075b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2076b7c89284Ssnj	RequestMaximize(xw, 0);
2077d522f475Smrg    }
2078d522f475Smrg}
2079d522f475Smrg#endif /* OPT_MAXIMIZE */
2080d522f475Smrg
2081d522f475Smrgvoid
2082d522f475SmrgRedraw(void)
2083d522f475Smrg{
2084d4fba8b9Smrg    XtermWidget xw = term;
2085d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2086d522f475Smrg    XExposeEvent event;
2087d522f475Smrg
2088d522f475Smrg    TRACE(("Redraw\n"));
2089d522f475Smrg
2090d522f475Smrg    event.type = Expose;
2091d522f475Smrg    event.display = screen->display;
2092d522f475Smrg    event.x = 0;
2093d522f475Smrg    event.y = 0;
2094d522f475Smrg    event.count = 0;
2095d522f475Smrg
2096d522f475Smrg    if (VWindow(screen)) {
2097d522f475Smrg	event.window = VWindow(screen);
2098d4fba8b9Smrg	event.width = xw->core.width;
2099d4fba8b9Smrg	event.height = xw->core.height;
2100d4fba8b9Smrg	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
2101d4fba8b9Smrg						     (XEvent *) &event,
2102d4fba8b9Smrg						     NULL);
2103d522f475Smrg	if (ScrollbarWidth(screen)) {
2104d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
21059a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
2106d522f475Smrg	}
2107d522f475Smrg    }
2108d522f475Smrg#if OPT_TEK4014
2109d4fba8b9Smrg    if (TEK4014_SHOWN(xw)) {
2110cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
2111d522f475Smrg	event.window = TWindow(tekscr);
2112d522f475Smrg	event.width = tekWidget->core.width;
2113d522f475Smrg	event.height = tekWidget->core.height;
21149a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
2115d522f475Smrg    }
2116d522f475Smrg#endif
2117d522f475Smrg}
2118d522f475Smrg
2119d522f475Smrg#ifdef VMS
2120d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
2121d522f475Smrg#else
2122d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
2123d522f475Smrg#endif
2124d522f475Smrg
2125d522f475Smrgvoid
2126d522f475Smrgtimestamp_filename(char *dst, const char *src)
2127d522f475Smrg{
2128d522f475Smrg    time_t tstamp;
2129d522f475Smrg    struct tm *tstruct;
2130d522f475Smrg
2131d522f475Smrg    tstamp = time((time_t *) 0);
2132d522f475Smrg    tstruct = localtime(&tstamp);
2133d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
2134d522f475Smrg	    src,
21353367019cSmrg	    (int) tstruct->tm_year + 1900,
2136d522f475Smrg	    tstruct->tm_mon + 1,
2137d522f475Smrg	    tstruct->tm_mday,
2138d522f475Smrg	    tstruct->tm_hour,
2139d522f475Smrg	    tstruct->tm_min,
2140d522f475Smrg	    tstruct->tm_sec);
2141d522f475Smrg}
2142d522f475Smrg
2143d4fba8b9SmrgFILE *
2144d4fba8b9Smrgcreate_printfile(XtermWidget xw, const char *suffix)
2145d4fba8b9Smrg{
2146d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2147d4fba8b9Smrg    char fname[1024];
2148d4fba8b9Smrg    int fd;
2149d4fba8b9Smrg    FILE *fp;
2150d4fba8b9Smrg
2151d4fba8b9Smrg#ifdef VMS
2152d4fba8b9Smrg    sprintf(fname, "sys$scratch:xterm%s", suffix);
2153d4fba8b9Smrg#elif defined(HAVE_STRFTIME)
2154d4fba8b9Smrg    {
2155d4fba8b9Smrg	char format[1024];
2156d4fba8b9Smrg	time_t now;
2157d4fba8b9Smrg	struct tm *ltm;
2158d4fba8b9Smrg
2159d4fba8b9Smrg	now = time((time_t *) 0);
2160d4fba8b9Smrg	ltm = localtime(&now);
2161d4fba8b9Smrg
2162d4fba8b9Smrg	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2163d4fba8b9Smrg	if (strftime(fname, sizeof fname, format, ltm) == 0) {
2164d4fba8b9Smrg	    sprintf(fname, "xterm%s", suffix);
2165d4fba8b9Smrg	}
2166d4fba8b9Smrg    }
2167d4fba8b9Smrg#else
2168d4fba8b9Smrg    sprintf(fname, "xterm%s", suffix);
2169d4fba8b9Smrg#endif
2170d4fba8b9Smrg    fd = open_userfile(screen->uid, screen->gid, fname, False);
2171d4fba8b9Smrg    fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2172d4fba8b9Smrg    return fp;
2173d4fba8b9Smrg}
2174d4fba8b9Smrg
2175d522f475Smrgint
2176d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2177d522f475Smrg{
2178d522f475Smrg    int fd;
2179d522f475Smrg    struct stat sb;
2180d522f475Smrg
2181d522f475Smrg#ifdef VMS
2182d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
2183d522f475Smrg	int the_error = errno;
21843367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
21853367019cSmrg		     path,
21863367019cSmrg		     the_error,
21873367019cSmrg		     SysErrorMsg(the_error));
2188d522f475Smrg	return -1;
2189d522f475Smrg    }
2190d522f475Smrg    chown(path, uid, gid);
2191d522f475Smrg#else
2192d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
2193d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
2194d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2195d522f475Smrg	int the_error = errno;
21963367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
21973367019cSmrg		     path,
21983367019cSmrg		     the_error,
21993367019cSmrg		     SysErrorMsg(the_error));
2200d522f475Smrg	return -1;
2201d522f475Smrg    }
2202d522f475Smrg#endif
2203d522f475Smrg
2204d522f475Smrg    /*
2205d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
2206d522f475Smrg     * we do any damage, and that it is not world-writable.
2207d522f475Smrg     */
2208d522f475Smrg    if (fstat(fd, &sb) < 0
2209d522f475Smrg	|| sb.st_uid != uid
2210d522f475Smrg	|| (sb.st_mode & 022) != 0) {
22113367019cSmrg	xtermWarning("you do not own %s\n", path);
2212d522f475Smrg	close(fd);
2213d522f475Smrg	return -1;
2214d522f475Smrg    }
2215d522f475Smrg    return fd;
2216d522f475Smrg}
2217d522f475Smrg
2218d522f475Smrg#ifndef VMS
2219d522f475Smrg/*
2220d522f475Smrg * Create a file only if we could with the permissions of the real user id.
2221d522f475Smrg * We could emulate this with careful use of access() and following
2222d522f475Smrg * symbolic links, but that is messy and has race conditions.
2223d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2224d522f475Smrg * being available.
2225d522f475Smrg *
2226d522f475Smrg * Note: When called for user logging, we have ensured that the real and
2227d522f475Smrg * effective user ids are the same, so this remains as a convenience function
2228d522f475Smrg * for the debug logs.
2229d522f475Smrg *
2230d522f475Smrg * Returns
2231d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
2232d522f475Smrg *	-1 on error, e.g., cannot fork
2233d522f475Smrg *	 0 otherwise.
2234d522f475Smrg */
2235d522f475Smrgint
2236712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2237d522f475Smrg{
2238d522f475Smrg    int fd;
2239d522f475Smrg    pid_t pid;
2240d522f475Smrg    int retval = 0;
2241d522f475Smrg    int childstat = 0;
2242d522f475Smrg#ifndef HAVE_WAITPID
2243d522f475Smrg    int waited;
22443367019cSmrg    void (*chldfunc) (int);
2245d522f475Smrg
2246d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
2247d522f475Smrg#endif /* HAVE_WAITPID */
2248d522f475Smrg
2249d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2250d522f475Smrg	   (int) uid, (int) geteuid(),
2251d522f475Smrg	   (int) gid, (int) getegid(),
2252d522f475Smrg	   append,
2253d522f475Smrg	   pathname,
2254d522f475Smrg	   mode));
2255d522f475Smrg
2256d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
2257d522f475Smrg	fd = open(pathname,
2258d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2259d522f475Smrg		  mode);
2260d522f475Smrg	if (fd >= 0)
2261d522f475Smrg	    close(fd);
2262d522f475Smrg	return (fd >= 0);
2263d522f475Smrg    }
2264d522f475Smrg
2265d522f475Smrg    pid = fork();
2266d522f475Smrg    switch (pid) {
2267d522f475Smrg    case 0:			/* child */
2268d522f475Smrg	if (setgid(gid) == -1
2269d522f475Smrg	    || setuid(uid) == -1) {
2270d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
2271d522f475Smrg	    retval = 1;
2272d522f475Smrg	} else {
2273d522f475Smrg	    fd = open(pathname,
2274d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2275d522f475Smrg		      mode);
2276d522f475Smrg	    if (fd >= 0) {
2277d522f475Smrg		close(fd);
2278d522f475Smrg		retval = 0;
2279d522f475Smrg	    } else {
2280d522f475Smrg		retval = 1;
2281d522f475Smrg	    }
2282d522f475Smrg	}
2283d522f475Smrg	_exit(retval);
2284d522f475Smrg	/* NOTREACHED */
2285d522f475Smrg    case -1:			/* error */
2286d522f475Smrg	return retval;
2287d522f475Smrg    default:			/* parent */
2288d522f475Smrg#ifdef HAVE_WAITPID
2289d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
2290d522f475Smrg#ifdef EINTR
2291d522f475Smrg	    if (errno == EINTR)
2292d522f475Smrg		continue;
2293d522f475Smrg#endif /* EINTR */
2294d522f475Smrg#ifdef ERESTARTSYS
2295d522f475Smrg	    if (errno == ERESTARTSYS)
2296d522f475Smrg		continue;
2297d522f475Smrg#endif /* ERESTARTSYS */
2298d522f475Smrg	    break;
2299d522f475Smrg	}
2300d522f475Smrg#else /* HAVE_WAITPID */
2301d522f475Smrg	waited = wait(&childstat);
2302d522f475Smrg	signal(SIGCHLD, chldfunc);
2303d522f475Smrg	/*
2304d522f475Smrg	   Since we had the signal handler uninstalled for a while,
2305d522f475Smrg	   we might have missed the termination of our screen child.
2306d522f475Smrg	   If we can check for this possibility without hanging, do so.
2307d522f475Smrg	 */
2308d522f475Smrg	do
2309cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
23103367019cSmrg		NormalExit();
2311d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
2312d522f475Smrg#endif /* HAVE_WAITPID */
2313d522f475Smrg#ifndef WIFEXITED
2314d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
2315d522f475Smrg#endif
2316d522f475Smrg	if (WIFEXITED(childstat))
2317d522f475Smrg	    retval = 1;
2318d522f475Smrg	return retval;
2319d522f475Smrg    }
2320d522f475Smrg}
2321d522f475Smrg#endif /* !VMS */
2322d522f475Smrg
2323d522f475Smrgint
2324fa3f02f3SmrgxtermResetIds(TScreen *screen)
2325d522f475Smrg{
2326d522f475Smrg    int result = 0;
2327d522f475Smrg    if (setgid(screen->gid) == -1) {
23283367019cSmrg	xtermWarning("unable to reset group-id\n");
2329d522f475Smrg	result = -1;
2330d522f475Smrg    }
2331d522f475Smrg    if (setuid(screen->uid) == -1) {
23323367019cSmrg	xtermWarning("unable to reset user-id\n");
2333d522f475Smrg	result = -1;
2334d522f475Smrg    }
2335d522f475Smrg    return result;
2336d522f475Smrg}
2337d522f475Smrg
2338d522f475Smrg#ifdef ALLOWLOGGING
2339d522f475Smrg
2340d522f475Smrg/*
2341d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
2342d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
2343d522f475Smrg */
2344d522f475Smrg
2345d522f475Smrg#ifdef ALLOWLOGFILEEXEC
23463367019cSmrgstatic void
2347d4fba8b9Smrghandle_SIGPIPE(int sig GCC_UNUSED)
2348d522f475Smrg{
2349cd3331d0Smrg    XtermWidget xw = term;
2350cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2351d522f475Smrg
23523367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2353d522f475Smrg#ifdef SYSV
2354d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2355d522f475Smrg#endif /* SYSV */
2356d522f475Smrg    if (screen->logging)
2357cd3331d0Smrg	CloseLog(xw);
2358d522f475Smrg}
2359d4fba8b9Smrg
2360d4fba8b9Smrg/*
2361d4fba8b9Smrg * Open a command to pipe log data to it.
2362d4fba8b9Smrg * Warning, enabling this "feature" allows arbitrary programs
2363d4fba8b9Smrg * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2364d4fba8b9Smrg * done through escape sequences....  You have been warned.
2365d4fba8b9Smrg */
2366d4fba8b9Smrgstatic void
2367d4fba8b9SmrgStartLogExec(TScreen *screen)
2368d4fba8b9Smrg{
2369d4fba8b9Smrg    int pid;
2370d4fba8b9Smrg    int p[2];
2371d4fba8b9Smrg    static char *shell;
2372d4fba8b9Smrg    struct passwd pw;
2373d4fba8b9Smrg
2374d4fba8b9Smrg    if ((shell = x_getenv("SHELL")) == NULL) {
2375d4fba8b9Smrg
2376d4fba8b9Smrg	if (x_getpwuid(screen->uid, &pw)) {
2377d4fba8b9Smrg	    char *name = x_getlogin(screen->uid, &pw);
2378d4fba8b9Smrg	    if (*(pw.pw_shell)) {
2379d4fba8b9Smrg		shell = pw.pw_shell;
2380d4fba8b9Smrg	    }
2381d4fba8b9Smrg	    free(name);
2382d4fba8b9Smrg	}
2383d4fba8b9Smrg    }
2384d4fba8b9Smrg
2385d4fba8b9Smrg    if (shell == 0) {
2386d4fba8b9Smrg	static char dummy[] = "/bin/sh";
2387d4fba8b9Smrg	shell = dummy;
2388d4fba8b9Smrg    }
2389d4fba8b9Smrg
2390d4fba8b9Smrg    if (access(shell, X_OK) != 0) {
2391d4fba8b9Smrg	xtermPerror("Can't execute `%s'\n", shell);
2392d4fba8b9Smrg	return;
2393d4fba8b9Smrg    }
2394d4fba8b9Smrg
2395d4fba8b9Smrg    if (pipe(p) < 0) {
2396d4fba8b9Smrg	xtermPerror("Can't make a pipe connection\n");
2397d4fba8b9Smrg	return;
2398d4fba8b9Smrg    } else if ((pid = fork()) < 0) {
2399d4fba8b9Smrg	xtermPerror("Can't fork...\n");
2400d4fba8b9Smrg	return;
2401d4fba8b9Smrg    }
2402d4fba8b9Smrg    if (pid == 0) {		/* child */
2403d4fba8b9Smrg	/*
2404d4fba8b9Smrg	 * Close our output (we won't be talking back to the
2405d4fba8b9Smrg	 * parent), and redirect our child's output to the
2406d4fba8b9Smrg	 * original stderr.
2407d4fba8b9Smrg	 */
2408d4fba8b9Smrg	close(p[1]);
2409d4fba8b9Smrg	dup2(p[0], 0);
2410d4fba8b9Smrg	close(p[0]);
2411d4fba8b9Smrg	dup2(fileno(stderr), 1);
2412d4fba8b9Smrg	dup2(fileno(stderr), 2);
2413d4fba8b9Smrg
2414d4fba8b9Smrg	close(fileno(stderr));
2415d4fba8b9Smrg	close(ConnectionNumber(screen->display));
2416d4fba8b9Smrg	close(screen->respond);
2417d4fba8b9Smrg
2418d4fba8b9Smrg	signal(SIGHUP, SIG_DFL);
2419d4fba8b9Smrg	signal(SIGCHLD, SIG_DFL);
2420d4fba8b9Smrg
2421d4fba8b9Smrg	/* (this is redundant) */
2422d4fba8b9Smrg	if (xtermResetIds(screen) < 0)
2423d4fba8b9Smrg	    exit(ERROR_SETUID);
2424d4fba8b9Smrg
2425d4fba8b9Smrg	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2426d4fba8b9Smrg	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2427d4fba8b9Smrg	exit(ERROR_LOGEXEC);
2428d4fba8b9Smrg    }
2429d4fba8b9Smrg    close(p[0]);
2430d4fba8b9Smrg    screen->logfd = p[1];
2431d4fba8b9Smrg    signal(SIGPIPE, handle_SIGPIPE);
2432d4fba8b9Smrg}
2433d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2434d522f475Smrg
2435d4fba8b9Smrg/*
2436d4fba8b9Smrg * Generate a path for a logfile if no default path is given.
2437d4fba8b9Smrg */
2438d4fba8b9Smrgstatic char *
2439d4fba8b9SmrgGenerateLogPath(void)
2440d4fba8b9Smrg{
2441d4fba8b9Smrg    static char *log_default = NULL;
2442d4fba8b9Smrg
2443d4fba8b9Smrg    /* once opened we just reuse the same log name */
2444d4fba8b9Smrg    if (log_default)
2445d4fba8b9Smrg	return (log_default);
2446d4fba8b9Smrg
2447d4fba8b9Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2448d4fba8b9Smrg    {
2449d4fba8b9Smrg#define LEN_HOSTNAME 255
2450d4fba8b9Smrg	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
2451d4fba8b9Smrg	 * the total length of a domain name (i.e., label octets and label
2452d4fba8b9Smrg	 * length octets) is restricted to 255 octets or less.''
2453d4fba8b9Smrg	 */
2454d4fba8b9Smrg#define LEN_GETPID 9
2455d4fba8b9Smrg	/*
2456d4fba8b9Smrg	 * This is arbitrary...
2457d4fba8b9Smrg	 */
2458d4fba8b9Smrg	const char form[] = "Xterm.log.%s%s.%lu";
2459d4fba8b9Smrg	char where[LEN_HOSTNAME + 1];
2460d4fba8b9Smrg	char when[LEN_TIMESTAMP];
2461d4fba8b9Smrg	time_t now = time((time_t *) 0);
2462d4fba8b9Smrg	struct tm *ltm = (struct tm *) localtime(&now);
2463d4fba8b9Smrg
2464d4fba8b9Smrg	if ((gethostname(where, sizeof(where)) == 0) &&
2465d4fba8b9Smrg	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2466d4fba8b9Smrg	    ((log_default = (char *) malloc((sizeof(form)
2467d4fba8b9Smrg					     + strlen(where)
2468d4fba8b9Smrg					     + strlen(when)
2469d4fba8b9Smrg					     + LEN_GETPID))) != NULL)) {
2470d4fba8b9Smrg	    (void) sprintf(log_default,
2471d4fba8b9Smrg			   form,
2472d4fba8b9Smrg			   where, when,
2473d4fba8b9Smrg			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
2474d4fba8b9Smrg	}
2475d4fba8b9Smrg    }
2476d4fba8b9Smrg#else
2477d4fba8b9Smrg    static const char log_def_name[] = "XtermLog.XXXXXX";
2478d4fba8b9Smrg    if ((log_default = x_strdup(log_def_name)) != NULL) {
2479d4fba8b9Smrg	mktemp(log_default);
2480d4fba8b9Smrg    }
2481d4fba8b9Smrg#endif
2482d4fba8b9Smrg
2483d4fba8b9Smrg    return (log_default);
2484d4fba8b9Smrg}
2485d4fba8b9Smrg
2486d522f475Smrgvoid
2487cd3331d0SmrgStartLog(XtermWidget xw)
2488d522f475Smrg{
2489cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2490d522f475Smrg
2491d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2492d522f475Smrg	return;
2493d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
2494d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
2495d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2496d522f475Smrg			 0640);
2497d522f475Smrg    if (screen->logfd < 0)
2498d522f475Smrg	return;			/* open failed */
2499d522f475Smrg#else /*VMS */
25003367019cSmrg
2501d4fba8b9Smrg    /* if we weren't supplied with a logfile path, generate one */
2502d4fba8b9Smrg    if (IsEmpty(screen->logfile))
2503d4fba8b9Smrg	screen->logfile = GenerateLogPath();
25043367019cSmrg
2505d4fba8b9Smrg    /* give up if we were unable to allocate the filename */
2506d4fba8b9Smrg    if (!screen->logfile)
2507d4fba8b9Smrg	return;
2508d522f475Smrg
2509d4fba8b9Smrg    if (*screen->logfile == '|') {	/* exec command */
2510d4fba8b9Smrg#ifdef ALLOWLOGFILEEXEC
2511d4fba8b9Smrg	StartLogExec(screen);
2512d522f475Smrg#else
2513cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2514cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2515d522f475Smrg	return;
2516d522f475Smrg#endif
2517d4fba8b9Smrg    } else if (strcmp(screen->logfile, "-") == 0) {
2518d4fba8b9Smrg	screen->logfd = STDOUT_FILENO;
2519d522f475Smrg    } else {
2520d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2521d522f475Smrg					   screen->gid,
2522d522f475Smrg					   screen->logfile,
2523d4fba8b9Smrg					   True)) < 0)
2524d522f475Smrg	    return;
2525d522f475Smrg    }
2526d522f475Smrg#endif /*VMS */
2527d522f475Smrg    screen->logstart = VTbuffer->next;
2528d522f475Smrg    screen->logging = True;
2529d522f475Smrg    update_logging();
2530d522f475Smrg}
2531d522f475Smrg
2532d522f475Smrgvoid
2533cd3331d0SmrgCloseLog(XtermWidget xw)
2534d522f475Smrg{
2535cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2536cd3331d0Smrg
2537d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2538d522f475Smrg	return;
2539cd3331d0Smrg    FlushLog(xw);
2540d522f475Smrg    close(screen->logfd);
2541d522f475Smrg    screen->logging = False;
2542d522f475Smrg    update_logging();
2543d522f475Smrg}
2544d522f475Smrg
2545d522f475Smrgvoid
2546cd3331d0SmrgFlushLog(XtermWidget xw)
2547d522f475Smrg{
2548cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2549cd3331d0Smrg
2550d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2551d522f475Smrg	Char *cp;
2552d522f475Smrg	int i;
2553d522f475Smrg
2554d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2555d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2556d522f475Smrg	if (!tt_new_output)
2557d522f475Smrg	    return;
2558d522f475Smrg	tt_new_output = False;
2559d522f475Smrg#endif /* VMS */
2560d522f475Smrg	cp = VTbuffer->next;
2561d522f475Smrg	if (screen->logstart != 0
2562cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2563cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2564d522f475Smrg	}
2565d522f475Smrg	screen->logstart = VTbuffer->next;
2566d522f475Smrg    }
2567d522f475Smrg}
2568d522f475Smrg
2569d522f475Smrg#endif /* ALLOWLOGGING */
2570d522f475Smrg
2571d522f475Smrg/***====================================================================***/
2572d522f475Smrg
2573d4fba8b9Smrgstatic unsigned
2574d4fba8b9SmrgmaskToShift(unsigned long mask)
2575d4fba8b9Smrg{
2576d4fba8b9Smrg    unsigned result = 0;
2577d4fba8b9Smrg    if (mask != 0) {
2578d4fba8b9Smrg	while ((mask & 1) == 0) {
2579d4fba8b9Smrg	    mask >>= 1;
2580d4fba8b9Smrg	    ++result;
2581d4fba8b9Smrg	}
2582d4fba8b9Smrg    }
2583d4fba8b9Smrg    return result;
2584d4fba8b9Smrg}
2585d4fba8b9Smrg
2586d4fba8b9Smrgstatic unsigned
2587d4fba8b9SmrgmaskToWidth(unsigned long mask)
2588d4fba8b9Smrg{
2589d4fba8b9Smrg    unsigned result = 0;
2590d4fba8b9Smrg    while (mask != 0) {
2591d4fba8b9Smrg	if ((mask & 1) != 0)
2592d4fba8b9Smrg	    ++result;
2593d4fba8b9Smrg	mask >>= 1;
2594d4fba8b9Smrg    }
2595d4fba8b9Smrg    return result;
2596d4fba8b9Smrg}
2597d4fba8b9Smrg
2598c48a5815SmrgXVisualInfo *
2599fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2600fa3f02f3Smrg{
2601fa3f02f3Smrg#define MYFMT "getVisualInfo \
2602fa3f02f3Smrgdepth %d, \
2603fa3f02f3Smrgtype %d (%s), \
2604fa3f02f3Smrgsize %d \
2605fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2606fa3f02f3Smrg#define MYARG \
2607fa3f02f3Smrg       vi->depth,\
2608fa3f02f3Smrg       vi->class,\
2609fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2610fa3f02f3Smrg       vi->colormap_size,\
2611fa3f02f3Smrg       vi->red_mask,\
2612fa3f02f3Smrg       vi->green_mask,\
2613fa3f02f3Smrg       vi->blue_mask
2614d522f475Smrg
2615fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2616fa3f02f3Smrg    Display *dpy = screen->display;
2617fa3f02f3Smrg    XVisualInfo myTemplate;
2618fa3f02f3Smrg
2619fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2620fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2621fa3f02f3Smrg								XDefaultScreen(dpy)));
2622fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2623fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2624fa3f02f3Smrg
2625fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2626fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2627d4fba8b9Smrg	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2628d4fba8b9Smrg	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2629d4fba8b9Smrg	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2630d4fba8b9Smrg	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2631d4fba8b9Smrg	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2632d4fba8b9Smrg	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2633d4fba8b9Smrg
2634d4fba8b9Smrg	    xw->has_rgb = ((vi->red_mask != 0) &&
2635d4fba8b9Smrg			   (vi->green_mask != 0) &&
2636d4fba8b9Smrg			   (vi->blue_mask != 0) &&
2637d4fba8b9Smrg			   ((vi->red_mask & vi->green_mask) == 0) &&
2638d4fba8b9Smrg			   ((vi->green_mask & vi->blue_mask) == 0) &&
2639c48a5815Smrg			   ((vi->blue_mask & vi->red_mask) == 0) &&
2640c48a5815Smrg			   (vi->class == TrueColor
2641c48a5815Smrg			    || vi->class == DirectColor));
2642d4fba8b9Smrg
2643fa3f02f3Smrg	    if (resource.reportColors) {
2644fa3f02f3Smrg		printf(MYFMT, MYARG);
2645fa3f02f3Smrg	    }
2646fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2647d4fba8b9Smrg	    TRACE(("...shifts %u/%u/%u\n",
2648d4fba8b9Smrg		   xw->rgb_shifts[0],
2649d4fba8b9Smrg		   xw->rgb_shifts[1],
2650d4fba8b9Smrg		   xw->rgb_shifts[2]));
2651c48a5815Smrg	    TRACE(("...widths %u/%u/%u\n",
2652c48a5815Smrg		   xw->rgb_widths[0],
2653c48a5815Smrg		   xw->rgb_widths[1],
2654c48a5815Smrg		   xw->rgb_widths[2]));
2655fa3f02f3Smrg	}
2656fa3f02f3Smrg    }
2657c48a5815Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
2658fa3f02f3Smrg#undef MYFMT
2659fa3f02f3Smrg#undef MYARG
2660fa3f02f3Smrg}
26613367019cSmrg
26629a64e1c5Smrg#if OPT_ISO_COLORS
2663d4fba8b9Smrgstatic Bool
2664d4fba8b9SmrgReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
26659a64e1c5Smrg{
2666d4fba8b9Smrg    Bool result = False;
2667d4fba8b9Smrg
26689a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
26699a64e1c5Smrg	XColor color;
26709a64e1c5Smrg	char buffer[80];
26719a64e1c5Smrg
26729a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
26739a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2674c48a5815Smrg	(void) QueryOneColor(xw, &color);
2675d4fba8b9Smrg	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2676d4fba8b9Smrg		opcode,
2677d4fba8b9Smrg		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
26789a64e1c5Smrg		color.red,
26799a64e1c5Smrg		color.green,
26809a64e1c5Smrg		color.blue);
26819a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
26829a64e1c5Smrg	unparseputs(xw, buffer);
26839a64e1c5Smrg	unparseputc1(xw, final);
2684d4fba8b9Smrg	result = True;
26859a64e1c5Smrg    }
2686d4fba8b9Smrg    return result;
26879a64e1c5Smrg}
26889a64e1c5Smrg
2689fa3f02f3Smrgstatic void
2690fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2691fa3f02f3Smrg{
2692fa3f02f3Smrg    if (getVisualInfo(xw)) {
2693fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2694fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2695fa3f02f3Smrg    } else {
2696fa3f02f3Smrg	*typep = 0;
2697fa3f02f3Smrg	*sizep = 0;
2698fa3f02f3Smrg    }
26993367019cSmrg}
27003367019cSmrg
27013367019cSmrg#define MAX_COLORTABLE 4096
27023367019cSmrg
27033367019cSmrg/*
27043367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
27053367019cSmrg */
27063367019cSmrgstatic Boolean
27073367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
27083367019cSmrg{
27093367019cSmrg    Colormap cmap = xw->core.colormap;
27103367019cSmrg    TScreen *screen = TScreenOf(xw);
2711fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
27123367019cSmrg
2713fa3f02f3Smrg    if (!result
27143367019cSmrg	&& length != 0
27153367019cSmrg	&& length < MAX_COLORTABLE) {
27163367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2717037a25ddSmrg
27183367019cSmrg	if (screen->cmap_data != 0) {
2719037a25ddSmrg	    unsigned i;
2720d4fba8b9Smrg	    unsigned shift;
2721d4fba8b9Smrg
2722d4fba8b9Smrg	    if (getVisualInfo(xw))
2723d4fba8b9Smrg		shift = xw->rgb_shifts[2];
2724d4fba8b9Smrg	    else
2725d4fba8b9Smrg		shift = 0;
2726037a25ddSmrg
27273367019cSmrg	    screen->cmap_size = length;
27283367019cSmrg
27293367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
2730d4fba8b9Smrg		screen->cmap_data[i].pixel = (unsigned long) i << shift;
27313367019cSmrg	    }
27323367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
27333367019cSmrg					     cmap,
27343367019cSmrg					     screen->cmap_data,
27353367019cSmrg					     (int) screen->cmap_size) != 0);
27363367019cSmrg	}
27373367019cSmrg    }
2738d522f475Smrg    return result;
2739d522f475Smrg}
2740d522f475Smrg
2741c48a5815Smrg/***====================================================================***/
2742c48a5815Smrg
2743c48a5815Smrg/*
2744c48a5815Smrg * Call this function with def->{red,green,blue} initialized, to obtain a pixel
2745c48a5815Smrg * value.
2746c48a5815Smrg */
2747c48a5815SmrgBoolean
2748c48a5815SmrgAllocOneColor(XtermWidget xw, XColor *def)
2749c48a5815Smrg{
2750c48a5815Smrg    TScreen *screen = TScreenOf(xw);
2751c48a5815Smrg    Boolean result = True;
2752c48a5815Smrg
2753c48a5815Smrg#define MaskIt(name,nn) \
2754c48a5815Smrg	((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
2755c48a5815Smrg	             << xw->rgb_shifts[nn]) \
2756c48a5815Smrg	 & xw->visInfo->name ##_mask)
2757c48a5815Smrg
275850027b5bSmrg    if (getVisualInfo(xw) != NULL && xw->has_rgb) {
2759c48a5815Smrg	def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
2760c48a5815Smrg    } else {
2761c48a5815Smrg	Display *dpy = screen->display;
2762c48a5815Smrg	if (!XAllocColor(dpy, xw->core.colormap, def)) {
2763c48a5815Smrg	    /*
2764c48a5815Smrg	     * Decide between foreground and background by a grayscale
2765c48a5815Smrg	     * approximation.
2766c48a5815Smrg	     */
2767c48a5815Smrg	    int bright = def->red * 3 + def->green * 10 + def->blue;
2768c48a5815Smrg	    int levels = 14 * 0x8000;
2769c48a5815Smrg	    def->pixel = ((bright >= levels)
2770c48a5815Smrg			  ? xw->dft_background
2771c48a5815Smrg			  : xw->dft_foreground);
2772c48a5815Smrg	    result = False;
2773c48a5815Smrg	}
2774c48a5815Smrg    }
2775c48a5815Smrg    return result;
2776c48a5815Smrg}
2777c48a5815Smrg
2778c48a5815Smrg/***====================================================================***/
2779c48a5815Smrg
2780c48a5815Smrg/*
2781c48a5815Smrg * Call this function with def->pixel set to the color that we want to convert
2782c48a5815Smrg * to separate red/green/blue.
2783c48a5815Smrg */
2784c48a5815SmrgBoolean
2785c48a5815SmrgQueryOneColor(XtermWidget xw, XColor *def)
2786c48a5815Smrg{
2787c48a5815Smrg    Boolean result = True;
2788c48a5815Smrg
2789c48a5815Smrg#define UnMaskIt(name,nn) \
2790c48a5815Smrg	((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
2791c48a5815Smrg#define UnMaskIt2(name,nn) \
2792c48a5815Smrg	(unsigned short)((((UnMaskIt(name,nn) << 8) \
2793c48a5815Smrg			   |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
2794c48a5815Smrg
279550027b5bSmrg    if (getVisualInfo(xw) != NULL && xw->has_rgb) {
2796c48a5815Smrg	/* *INDENT-EQLS* */
2797c48a5815Smrg	def->red   = UnMaskIt2(red, 0);
2798c48a5815Smrg	def->green = UnMaskIt2(green, 1);
2799c48a5815Smrg	def->blue  = UnMaskIt2(blue, 2);
2800c48a5815Smrg    } else if (!XQueryColor(TScreenOf(xw)->display, xw->core.colormap, def)) {
2801c48a5815Smrg	result     = False;
2802c48a5815Smrg    }
2803c48a5815Smrg    return result;
2804c48a5815Smrg}
2805c48a5815Smrg
2806c48a5815Smrg/***====================================================================***/
2807c48a5815Smrg
2808d522f475Smrg/*
2809d522f475Smrg * Find closest color for "def" in "cmap".
2810d522f475Smrg * Set "def" to the resulting color.
2811d522f475Smrg *
2812d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2813d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2814d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2815d522f475Smrg *
2816d522f475Smrg * Return False if not able to find or allocate a color.
2817d522f475Smrg */
2818d522f475Smrgstatic Boolean
2819c48a5815SmrgallocateClosestRGB(XtermWidget xw, XColor *def)
2820d522f475Smrg{
28213367019cSmrg    TScreen *screen = TScreenOf(xw);
2822d522f475Smrg    Boolean result = False;
28233367019cSmrg    unsigned cmap_type;
2824d522f475Smrg    unsigned cmap_size;
2825d522f475Smrg
2826fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2827d522f475Smrg
28283367019cSmrg    if ((cmap_type & 1) != 0) {
28293367019cSmrg
28303367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2831037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2832d522f475Smrg
2833d522f475Smrg	    if (tried != 0) {
2834037a25ddSmrg		unsigned attempts;
2835d522f475Smrg
2836d522f475Smrg		/*
2837d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2838d522f475Smrg		 * approximation to the requested color.
2839d522f475Smrg		 */
2840d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2841d522f475Smrg		    Boolean first = True;
2842037a25ddSmrg		    double bestRGB = 0.0;
2843037a25ddSmrg		    unsigned bestInx = 0;
2844037a25ddSmrg		    unsigned i;
2845d522f475Smrg
2846d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2847d522f475Smrg			if (!tried[bestInx]) {
2848037a25ddSmrg			    double diff, thisRGB = 0.0;
2849037a25ddSmrg
2850d522f475Smrg			    /*
2851d522f475Smrg			     * Look for the best match based on luminance.
2852d522f475Smrg			     * Measure this by the least-squares difference of
2853d522f475Smrg			     * the weighted R/G/B components from the color map
2854d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2855d522f475Smrg			     * component of the YIQ color space model for
2856d522f475Smrg			     * weights that correspond to the luminance.
2857d522f475Smrg			     */
2858d522f475Smrg#define AddColorWeight(weight, color) \
28593367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2860037a25ddSmrg			    thisRGB += diff * diff
2861d522f475Smrg
2862d522f475Smrg			    AddColorWeight(0.30, red);
2863d522f475Smrg			    AddColorWeight(0.61, green);
2864d522f475Smrg			    AddColorWeight(0.11, blue);
2865d522f475Smrg
2866d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2867d522f475Smrg				first = False;
2868d522f475Smrg				bestInx = i;
2869d522f475Smrg				bestRGB = thisRGB;
2870d522f475Smrg			    }
2871d522f475Smrg			}
2872d522f475Smrg		    }
2873c48a5815Smrg		    if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
28743367019cSmrg			*def = screen->cmap_data[bestInx];
28753367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
28763367019cSmrg			       def->green, def->blue));
2877d522f475Smrg			result = True;
2878d522f475Smrg			break;
2879d522f475Smrg		    }
2880d522f475Smrg		    /*
2881d522f475Smrg		     * It failed - either the color map entry was readonly, or
2882d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2883d522f475Smrg		     * so we will ignore it
2884d522f475Smrg		     */
2885d522f475Smrg		    tried[bestInx] = True;
2886d522f475Smrg		}
2887d522f475Smrg		free(tried);
2888d522f475Smrg	    }
2889d522f475Smrg	}
2890d522f475Smrg    }
2891d522f475Smrg    return result;
2892d522f475Smrg}
2893d522f475Smrg
28943367019cSmrg#ifndef ULONG_MAX
28953367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
28963367019cSmrg#endif
28973367019cSmrg
2898d522f475Smrg/*
2899d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2900d522f475Smrg * to 256.
2901d522f475Smrg *
2902d522f475Smrg * Returns
2903d522f475Smrg *	-1 on error
2904d522f475Smrg *	0 on no change
2905d522f475Smrg *	1 if a new color was allocated.
2906d522f475Smrg */
2907d522f475Smrgstatic int
2908d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2909d522f475Smrg		  ColorRes * res,
2910cd3331d0Smrg		  const char *spec)
2911d522f475Smrg{
2912d522f475Smrg    int result;
2913d522f475Smrg    XColor def;
2914d522f475Smrg
29153367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2916c48a5815Smrg	if (res->mode == True &&
2917c48a5815Smrg	    EQL_COLOR_RES(res, def.pixel)) {
2918d522f475Smrg	    result = 0;
2919d522f475Smrg	} else {
2920d522f475Smrg	    result = 1;
2921d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
29223367019cSmrg	    res->red = def.red;
29233367019cSmrg	    res->green = def.green;
29243367019cSmrg	    res->blue = def.blue;
29253367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
29263367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
29273367019cSmrg		   def.red,
29283367019cSmrg		   def.green,
29293367019cSmrg		   def.blue,
29303367019cSmrg		   def.pixel));
2931d522f475Smrg	    if (!res->mode)
2932d522f475Smrg		result = 0;
2933d522f475Smrg	    res->mode = True;
2934d522f475Smrg	}
2935d522f475Smrg    } else {
2936d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2937d522f475Smrg	result = -1;
2938d522f475Smrg    }
2939d522f475Smrg    return (result);
2940d522f475Smrg}
2941d522f475Smrg
2942d522f475SmrgPixel
2943cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2944d522f475Smrg{
2945d522f475Smrg    Pixel result = 0;
2946d522f475Smrg
2947d522f475Smrg    if (res->mode) {
2948d522f475Smrg	result = res->value;
2949d522f475Smrg    } else {
2950d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2951cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2952d522f475Smrg
2953cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2954cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2955d522f475Smrg
2956cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2957cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2958d522f475Smrg		res->mode = -True;
29593367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
29603367019cSmrg			     NonNull(res->resource));
2961d522f475Smrg	    }
2962d522f475Smrg	    result = res->value;
2963d522f475Smrg	} else {
2964d522f475Smrg	    result = 0;
2965d522f475Smrg	}
2966d522f475Smrg    }
2967d522f475Smrg    return result;
2968d522f475Smrg}
2969d522f475Smrg
2970cd3331d0Smrgstatic int
2971cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2972cd3331d0Smrg{
2973cd3331d0Smrg    int code;
2974cd3331d0Smrg
2975cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2976cd3331d0Smrg	code = -1;
2977cd3331d0Smrg    } else {
2978cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2979cd3331d0Smrg
2980cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2981cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2982cd3331d0Smrg    }
2983cd3331d0Smrg    return code;
2984cd3331d0Smrg}
2985cd3331d0Smrg
2986cd3331d0Smrg/*
2987cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2988cd3331d0Smrg * values from the given buffer.
2989cd3331d0Smrg *
2990cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2991cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2992cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2993cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2994cd3331d0Smrg * 'first' set to the beginning of those indices.
2995cd3331d0Smrg *
2996cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2997cd3331d0Smrg */
2998d522f475Smrgstatic Bool
2999d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
3000d4fba8b9Smrg		       int opcode,
3001d522f475Smrg		       char *buf,
3002cd3331d0Smrg		       int first,
3003d522f475Smrg		       int final)
3004d522f475Smrg{
3005d522f475Smrg    int repaint = False;
3006d522f475Smrg    int code;
3007cd3331d0Smrg    int last = (MAXCOLORS - first);
3008d4fba8b9Smrg    int queried = 0;
3009d522f475Smrg
3010d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
3011d522f475Smrg
3012d522f475Smrg    while (buf && *buf) {
3013037a25ddSmrg	int color;
3014037a25ddSmrg	char *name = strchr(buf, ';');
3015037a25ddSmrg
3016d522f475Smrg	if (name == NULL)
3017d522f475Smrg	    break;
3018d522f475Smrg	*name = '\0';
3019d522f475Smrg	name++;
3020d522f475Smrg	color = atoi(buf);
3021cd3331d0Smrg	if (color < 0 || color >= last)
3022cd3331d0Smrg	    break;		/* quit on any error */
3023d522f475Smrg	buf = strchr(name, ';');
3024d522f475Smrg	if (buf) {
3025d522f475Smrg	    *buf = '\0';
3026d522f475Smrg	    buf++;
3027d522f475Smrg	}
3028cd3331d0Smrg	if (!strcmp(name, "?")) {
3029d4fba8b9Smrg	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3030d4fba8b9Smrg		++queried;
3031cd3331d0Smrg	} else {
3032cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
3033d522f475Smrg	    if (code < 0) {
3034d522f475Smrg		/* stop on any error */
3035d522f475Smrg		break;
3036d522f475Smrg	    } else if (code > 0) {
3037d522f475Smrg		repaint = True;
3038d522f475Smrg	    }
3039d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
3040d522f475Smrg	     * change style (dynamic colors).
3041d522f475Smrg	     */
3042d522f475Smrg	}
3043d522f475Smrg    }
3044d4fba8b9Smrg    if (queried)
3045d4fba8b9Smrg	unparse_end(xw);
3046d522f475Smrg
3047d522f475Smrg    return (repaint);
3048d522f475Smrg}
3049cd3331d0Smrg
3050cd3331d0Smrgstatic Bool
3051cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
3052cd3331d0Smrg{
3053cd3331d0Smrg    Bool repaint = False;
3054cd3331d0Smrg    int last = MAXCOLORS - start;
3055cd3331d0Smrg
3056cd3331d0Smrg    if (color >= 0 && color < last) {
3057cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3058cd3331d0Smrg
3059cd3331d0Smrg	if (res->mode) {
3060cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
3061cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3062cd3331d0Smrg		repaint = True;
3063cd3331d0Smrg	    }
3064cd3331d0Smrg	}
3065cd3331d0Smrg    }
3066cd3331d0Smrg    return repaint;
3067cd3331d0Smrg}
3068cd3331d0Smrg
3069cd3331d0Smrgint
3070cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3071cd3331d0Smrg{
3072cd3331d0Smrg    int repaint = 0;
3073cd3331d0Smrg    int color;
3074cd3331d0Smrg
3075cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3076cd3331d0Smrg    if (*buf != '\0') {
3077cd3331d0Smrg	/* reset specific colors */
3078cd3331d0Smrg	while (!IsEmpty(buf)) {
3079cd3331d0Smrg	    char *next;
3080cd3331d0Smrg
3081037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
3082037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
3083cd3331d0Smrg		break;		/* no number at all */
3084cd3331d0Smrg	    if (next != 0) {
3085cd3331d0Smrg		if (strchr(";", *next) == 0)
3086cd3331d0Smrg		    break;	/* unexpected delimiter */
3087cd3331d0Smrg		++next;
3088cd3331d0Smrg	    }
3089cd3331d0Smrg
3090cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3091cd3331d0Smrg		++repaint;
3092cd3331d0Smrg	    }
3093cd3331d0Smrg	    buf = next;
3094cd3331d0Smrg	}
3095cd3331d0Smrg    } else {
3096cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3097cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
3098cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3099cd3331d0Smrg		++repaint;
3100cd3331d0Smrg	    }
3101cd3331d0Smrg	}
3102cd3331d0Smrg    }
3103cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3104cd3331d0Smrg    return repaint;
3105cd3331d0Smrg}
3106d522f475Smrg#else
3107c48a5815Smrg#define allocateClosestRGB(xw, def) 0
3108d522f475Smrg#endif /* OPT_ISO_COLORS */
3109d522f475Smrg
3110fa3f02f3SmrgBoolean
31119a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
3112fa3f02f3Smrg{
3113c48a5815Smrg    (void) xw;
3114c48a5815Smrg    (void) def;
3115c48a5815Smrg    return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
3116fa3f02f3Smrg}
3117fa3f02f3Smrg
31183367019cSmrgstatic Boolean
31199a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
31203367019cSmrg{
31213367019cSmrg    Boolean result = False;
31223367019cSmrg    TScreen *screen = TScreenOf(xw);
31233367019cSmrg    Colormap cmap = xw->core.colormap;
31248f44fb3bSmrg    size_t have = strlen(spec);
31253367019cSmrg
31268f44fb3bSmrg    if (have == 0 || have > MAX_U_STRING) {
31278f44fb3bSmrg	if (resource.reportColors) {
3128c48a5815Smrg	    printf("color  (ignored, length %lu)\n", (unsigned long) have);
31298f44fb3bSmrg	}
31308f44fb3bSmrg    } else if (XParseColor(screen->display, cmap, spec, def)) {
3131fa3f02f3Smrg	XColor save_def = *def;
3132fa3f02f3Smrg	if (resource.reportColors) {
3133fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3134fa3f02f3Smrg		   def->red, def->green, def->blue,
3135fa3f02f3Smrg		   spec);
3136fa3f02f3Smrg	}
3137fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
3138fa3f02f3Smrg	    if (resource.reportColors) {
3139fa3f02f3Smrg		if (def->red != save_def.red ||
3140fa3f02f3Smrg		    def->green != save_def.green ||
3141fa3f02f3Smrg		    def->blue != save_def.blue) {
3142fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3143fa3f02f3Smrg			   def->red, def->green, def->blue,
3144fa3f02f3Smrg			   spec);
3145fa3f02f3Smrg		}
3146fa3f02f3Smrg	    }
3147fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3148fa3f02f3Smrg		   def->red, def->green, def->blue));
3149fa3f02f3Smrg	    result = True;
3150fa3f02f3Smrg	}
31513367019cSmrg    }
31523367019cSmrg    return result;
31533367019cSmrg}
31543367019cSmrg
31553367019cSmrg/*
31563367019cSmrg * This provides an approximation (the closest color from xterm's palette)
31573367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
31583367019cSmrg * because of the context in which it is used.
31593367019cSmrg */
31603367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
31613367019cSmrgint
31623367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
31633367019cSmrg{
31643367019cSmrg    int result = -1;
3165c48a5815Smrg#if OPT_ISO_COLORS
31663367019cSmrg    int n;
31673367019cSmrg    int best_index = -1;
31683367019cSmrg    unsigned long best_value = 0;
31693367019cSmrg    unsigned long this_value;
31703367019cSmrg    long diff_red, diff_green, diff_blue;
31713367019cSmrg
31723367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
31733367019cSmrg
31743367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
31753367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
31763367019cSmrg
31773367019cSmrg	/* ensure that we have a value for each of the colors */
31783367019cSmrg	if (!res->mode) {
31793367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
31803367019cSmrg	}
31813367019cSmrg
31823367019cSmrg	/* find the closest match */
31833367019cSmrg	if (res->mode == True) {
31843367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
31853367019cSmrg		    res->value, res->red, res->green, res->blue));
31863367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
31873367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
31883367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
31893367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
31903367019cSmrg					  + (diff_green * diff_green)
31913367019cSmrg					  + (diff_blue * diff_blue));
31923367019cSmrg	    if (best_index < 0 || this_value < best_value) {
31933367019cSmrg		best_index = n;
31943367019cSmrg		best_value = this_value;
31953367019cSmrg	    }
31963367019cSmrg	}
31973367019cSmrg    }
31983367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
31993367019cSmrg    result = best_index;
3200c48a5815Smrg
32013367019cSmrg#else
32023367019cSmrg    (void) xw;
32033367019cSmrg    (void) find_red;
32043367019cSmrg    (void) find_green;
32053367019cSmrg    (void) find_blue;
32063367019cSmrg#endif
32073367019cSmrg    return result;
32083367019cSmrg}
32093367019cSmrg
3210d4fba8b9Smrg#if OPT_DIRECT_COLOR
3211d4fba8b9Smrgint
3212d4fba8b9SmrggetDirectColor(XtermWidget xw, int red, int green, int blue)
3213d4fba8b9Smrg{
3214c48a5815Smrg    Pixel result = 0;
3215c48a5815Smrg
3216c48a5815Smrg#define getRGB(name,shift) \
3217c48a5815Smrg    do { \
3218c48a5815Smrg	Pixel value = (Pixel) name & 0xff; \
3219c48a5815Smrg	if (xw->rgb_widths[shift] < 8) { \
3220c48a5815Smrg	    value >>= (int) (8 - xw->rgb_widths[shift]); \
3221c48a5815Smrg	} \
3222c48a5815Smrg	value <<= xw->rgb_shifts[shift]; \
3223c48a5815Smrg	value &= xw->visInfo->name ##_mask; \
3224c48a5815Smrg	result |= value; \
3225c48a5815Smrg    } while (0)
3226c48a5815Smrg
3227c48a5815Smrg    getRGB(red, 0);
3228c48a5815Smrg    getRGB(green, 1);
3229c48a5815Smrg    getRGB(blue, 2);
3230c48a5815Smrg
3231c48a5815Smrg#undef getRGB
3232c48a5815Smrg
3233d4fba8b9Smrg    return (int) result;
3234d4fba8b9Smrg}
3235d4fba8b9Smrg
3236d4fba8b9Smrgstatic void
3237d4fba8b9SmrgformatDirectColor(char *target, XtermWidget xw, unsigned value)
3238d4fba8b9Smrg{
3239c48a5815Smrg    Pixel result[3];
3240c48a5815Smrg
3241c48a5815Smrg#define getRGB(name, shift) \
3242c48a5815Smrg    do { \
3243c48a5815Smrg	result[shift] = value & xw->visInfo->name ## _mask; \
3244c48a5815Smrg	result[shift] >>= xw->rgb_shifts[shift]; \
3245c48a5815Smrg	if (xw->rgb_widths[shift] < 8) \
3246c48a5815Smrg	    result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
3247c48a5815Smrg    } while(0)
3248c48a5815Smrg
3249c48a5815Smrg    getRGB(red, 0);
3250c48a5815Smrg    getRGB(green, 1);
3251c48a5815Smrg    getRGB(blue, 2);
3252c48a5815Smrg
3253c48a5815Smrg#undef getRGB
3254c48a5815Smrg
3255c48a5815Smrg    sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
3256d4fba8b9Smrg}
3257d4fba8b9Smrg#endif /* OPT_DIRECT_COLOR */
3258d4fba8b9Smrg
3259d4fba8b9Smrg#define fg2SGR(n) \
3260d4fba8b9Smrg		(n) >= 8 ? 9 : 3, \
3261d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3262d4fba8b9Smrg#define bg2SGR(n) \
3263d4fba8b9Smrg		(n) >= 8 ? 10 : 4, \
3264d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3265d4fba8b9Smrg
3266d4fba8b9Smrg#define EndOf(s) (s) + strlen(s)
3267d4fba8b9Smrg
3268d4fba8b9Smrgchar *
3269d4fba8b9SmrgxtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3270d4fba8b9Smrg{
3271d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
3272d4fba8b9Smrg    char *msg = target;
3273d4fba8b9Smrg
3274d4fba8b9Smrg    strcpy(target, "0");
3275d4fba8b9Smrg    if (attr & BOLD)
3276d4fba8b9Smrg	strcat(msg, ";1");
3277d4fba8b9Smrg    if (attr & UNDERLINE)
3278d4fba8b9Smrg	strcat(msg, ";4");
3279d4fba8b9Smrg    if (attr & BLINK)
3280d4fba8b9Smrg	strcat(msg, ";5");
3281d4fba8b9Smrg    if (attr & INVERSE)
3282d4fba8b9Smrg	strcat(msg, ";7");
3283d4fba8b9Smrg    if (attr & INVISIBLE)
3284d4fba8b9Smrg	strcat(msg, ";8");
3285d4fba8b9Smrg#if OPT_WIDE_ATTRS
3286d4fba8b9Smrg    if (attr & ATR_FAINT)
3287d4fba8b9Smrg	strcat(msg, ";2");
3288d4fba8b9Smrg    if (attr & ATR_ITALIC)
3289d4fba8b9Smrg	strcat(msg, ";3");
3290d4fba8b9Smrg    if (attr & ATR_STRIKEOUT)
3291d4fba8b9Smrg	strcat(msg, ";9");
3292d4fba8b9Smrg    if (attr & ATR_DBL_UNDER)
3293d4fba8b9Smrg	strcat(msg, ";21");
3294d4fba8b9Smrg#endif
3295d4fba8b9Smrg#if OPT_256_COLORS || OPT_88_COLORS
3296d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3297d4fba8b9Smrg	if (attr & FG_COLOR) {
3298d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3299d4fba8b9Smrg		strcat(msg, ";38:2::");
3300d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3301d4fba8b9Smrg	    }) if (fg >= 16) {
3302d4fba8b9Smrg		sprintf(EndOf(msg), ";38:5:%d", fg);
3303d4fba8b9Smrg	    } else {
3304d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3305d4fba8b9Smrg	    }
3306d4fba8b9Smrg	}
3307d4fba8b9Smrg	if (attr & BG_COLOR) {
3308d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3309d4fba8b9Smrg		strcat(msg, ";48:2::");
3310d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3311d4fba8b9Smrg	    }) if (bg >= 16) {
3312d4fba8b9Smrg		sprintf(EndOf(msg), ";48:5:%d", bg);
3313d4fba8b9Smrg	    } else {
3314d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3315d4fba8b9Smrg	    }
3316d4fba8b9Smrg	}
3317d4fba8b9Smrg    });
3318d4fba8b9Smrg#elif OPT_ISO_COLORS
3319d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3320d4fba8b9Smrg	if (attr & FG_COLOR) {
3321d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3322d4fba8b9Smrg	}
3323d4fba8b9Smrg	if (attr & BG_COLOR) {
3324d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3325d4fba8b9Smrg	}
3326d4fba8b9Smrg    });
3327d4fba8b9Smrg#else
3328d4fba8b9Smrg    (void) screen;
3329d4fba8b9Smrg    (void) fg;
3330d4fba8b9Smrg    (void) bg;
3331d4fba8b9Smrg#endif
3332d4fba8b9Smrg    return target;
3333d4fba8b9Smrg}
3334d4fba8b9Smrg
3335d522f475Smrg#if OPT_PASTE64
3336d522f475Smrgstatic void
3337fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3338d522f475Smrg{
3339d522f475Smrg#define PDATA(a,b) { a, #b }
3340d522f475Smrg    static struct {
3341d522f475Smrg	char given;
3342cd3331d0Smrg	String result;
3343d522f475Smrg    } table[] = {
3344d522f475Smrg	PDATA('s', SELECT),
3345d522f475Smrg	    PDATA('p', PRIMARY),
3346d4fba8b9Smrg	    PDATA('q', SECONDARY),
3347d522f475Smrg	    PDATA('c', CLIPBOARD),
3348d522f475Smrg	    PDATA('0', CUT_BUFFER0),
3349d522f475Smrg	    PDATA('1', CUT_BUFFER1),
3350d522f475Smrg	    PDATA('2', CUT_BUFFER2),
3351d522f475Smrg	    PDATA('3', CUT_BUFFER3),
3352d522f475Smrg	    PDATA('4', CUT_BUFFER4),
3353d522f475Smrg	    PDATA('5', CUT_BUFFER5),
3354d522f475Smrg	    PDATA('6', CUT_BUFFER6),
3355d522f475Smrg	    PDATA('7', CUT_BUFFER7),
3356d522f475Smrg    };
3357d522f475Smrg
3358cd3331d0Smrg    const char *base = buf;
3359d522f475Smrg    Cardinal j, n = 0;
3360d522f475Smrg
3361d522f475Smrg    TRACE(("Manipulate selection data\n"));
3362d522f475Smrg
3363d522f475Smrg    while (*buf != ';' && *buf != '\0') {
3364d522f475Smrg	++buf;
3365d522f475Smrg    }
3366d522f475Smrg
3367d522f475Smrg    if (*buf == ';') {
3368037a25ddSmrg	char *used;
3369037a25ddSmrg
3370d522f475Smrg	*buf++ = '\0';
3371d522f475Smrg
3372d522f475Smrg	if (*base == '\0')
3373d522f475Smrg	    base = "s0";
3374d522f475Smrg
33753367019cSmrg	if ((used = x_strdup(base)) != 0) {
3376037a25ddSmrg	    String *select_args;
3377037a25ddSmrg
33783367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
33793367019cSmrg		while (*base != '\0') {
33803367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
33813367019cSmrg			if (*base == table[j].given) {
33823367019cSmrg			    used[n] = *base;
33833367019cSmrg			    select_args[n++] = table[j].result;
33843367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
33853367019cSmrg			    break;
33863367019cSmrg			}
33873367019cSmrg		    }
33883367019cSmrg		    ++base;
33893367019cSmrg		}
33903367019cSmrg		used[n] = 0;
33913367019cSmrg
33923367019cSmrg		if (!strcmp(buf, "?")) {
33933367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
33943367019cSmrg			TRACE(("Getting selection\n"));
33953367019cSmrg			unparseputc1(xw, ANSI_OSC);
33963367019cSmrg			unparseputs(xw, "52");
33973367019cSmrg			unparseputc(xw, ';');
33983367019cSmrg
33993367019cSmrg			unparseputs(xw, used);
34003367019cSmrg			unparseputc(xw, ';');
34013367019cSmrg
34023367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
34033367019cSmrg			screen->base64_paste = n;
34043367019cSmrg			screen->base64_final = final;
34053367019cSmrg
3406dfb07bc7Smrg			screen->selection_time =
3407dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3408dfb07bc7Smrg
34093367019cSmrg			/* terminator will be written in this call */
34103367019cSmrg			xtermGetSelection((Widget) xw,
3411dfb07bc7Smrg					  screen->selection_time,
34123367019cSmrg					  select_args, n,
34133367019cSmrg					  NULL);
341494644356Smrg			/*
341594644356Smrg			 * select_args is used via SelectionReceived, cannot
341694644356Smrg			 * free it here.
341794644356Smrg			 */
341894644356Smrg		    } else {
341994644356Smrg			free(select_args);
34203367019cSmrg		    }
34213367019cSmrg		} else {
34223367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
3423d4fba8b9Smrg			char *old = buf;
3424d4fba8b9Smrg
3425d4fba8b9Smrg			TRACE(("Setting selection(%s) with %s\n", used, buf));
3426dfb07bc7Smrg			screen->selection_time =
3427dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3428d4fba8b9Smrg
3429d4fba8b9Smrg			for (j = 0; j < n; ++j) {
3430d4fba8b9Smrg			    buf = old;
3431d4fba8b9Smrg			    ClearSelectionBuffer(screen, select_args[j]);
3432d4fba8b9Smrg			    while (*buf != '\0') {
3433d4fba8b9Smrg				AppendToSelectionBuffer(screen,
3434d4fba8b9Smrg							CharOf(*buf++),
3435d4fba8b9Smrg							select_args[j]);
3436d4fba8b9Smrg			    }
3437d4fba8b9Smrg			}
34383367019cSmrg			CompleteSelection(xw, select_args, n);
34393367019cSmrg		    }
344094644356Smrg		    free(select_args);
34413367019cSmrg		}
3442cd3331d0Smrg	    }
34433367019cSmrg	    free(used);
3444d522f475Smrg	}
3445d522f475Smrg    }
3446d522f475Smrg}
3447d522f475Smrg#endif /* OPT_PASTE64 */
3448d522f475Smrg
3449d522f475Smrg/***====================================================================***/
3450d522f475Smrg
3451d4fba8b9Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3452d4fba8b9Smrg			 || (xw->screen.utf8_title) \
3453d4fba8b9Smrg			 || (xw->screen.c1_printable))
3454cd3331d0Smrg
3455d522f475Smrgstatic Bool
3456fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3457d522f475Smrg{
3458cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3459d522f475Smrg    Bool result = False;
3460d522f475Smrg    Char *cp = *bufp;
3461d522f475Smrg    Char *next = cp;
3462d522f475Smrg
3463d522f475Smrg    (void) screen;
3464d522f475Smrg    (void) last;
3465d522f475Smrg
3466d522f475Smrg#if OPT_WIDE_CHARS
3467cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3468d522f475Smrg	PtyData data;
3469d522f475Smrg
34709a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3471d522f475Smrg	    if (data.utf_data != UCS_REPL
3472d522f475Smrg		&& (data.utf_data >= 128 ||
3473d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3474d522f475Smrg		next += (data.utf_size - 1);
3475d522f475Smrg		result = True;
3476d522f475Smrg	    } else {
3477d522f475Smrg		result = False;
3478d522f475Smrg	    }
3479d522f475Smrg	} else {
3480d522f475Smrg	    result = False;
3481d522f475Smrg	}
3482d522f475Smrg    } else
3483d522f475Smrg#endif
3484d522f475Smrg#if OPT_C1_PRINT
3485d522f475Smrg	if (screen->c1_printable
3486d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3487d522f475Smrg	result = True;
3488d522f475Smrg    } else
3489d522f475Smrg#endif
3490d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3491d522f475Smrg	result = True;
3492d522f475Smrg    }
3493d522f475Smrg    *bufp = next;
3494d522f475Smrg    return result;
3495d522f475Smrg}
3496d522f475Smrg
3497d522f475Smrg/***====================================================================***/
3498d522f475Smrg
3499d522f475Smrg/*
3500d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3501cd3331d0Smrg * array indices.  Compare with TermColors.
3502d522f475Smrg */
3503d522f475Smrgtypedef enum {
3504d522f475Smrg    OSC_TEXT_FG = 10
3505d522f475Smrg    ,OSC_TEXT_BG
3506d522f475Smrg    ,OSC_TEXT_CURSOR
3507d522f475Smrg    ,OSC_MOUSE_FG
3508d522f475Smrg    ,OSC_MOUSE_BG
3509d522f475Smrg#if OPT_TEK4014
3510d522f475Smrg    ,OSC_TEK_FG = 15
3511d522f475Smrg    ,OSC_TEK_BG
3512d522f475Smrg#endif
3513d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3514d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3515d522f475Smrg#endif
3516d522f475Smrg#if OPT_TEK4014
3517d522f475Smrg    ,OSC_TEK_CURSOR = 18
3518d522f475Smrg#endif
3519d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3520d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3521d522f475Smrg#endif
3522d522f475Smrg    ,OSC_NCOLORS
3523d522f475Smrg} OscTextColors;
3524d522f475Smrg
3525cd3331d0Smrg/*
3526cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3527cd3331d0Smrg */
3528cd3331d0Smrg#define OSC_RESET 100
3529cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3530cd3331d0Smrg
3531d522f475Smrgstatic Bool
3532d522f475SmrgGetOldColors(XtermWidget xw)
3533d522f475Smrg{
35349a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3535037a25ddSmrg	int i;
3536037a25ddSmrg
35379a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
35389a64e1c5Smrg	if (xw->work.oldColors == NULL) {
35393367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3540d522f475Smrg	    return (False);
3541d522f475Smrg	}
35429a64e1c5Smrg	xw->work.oldColors->which = 0;
3543d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
35449a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
35459a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3546d522f475Smrg	}
35479a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3548d522f475Smrg    }
3549d522f475Smrg    return (True);
3550d522f475Smrg}
3551d522f475Smrg
3552d522f475Smrgstatic int
3553d4fba8b9SmrgoppositeColor(XtermWidget xw, int n)
3554d522f475Smrg{
3555d4fba8b9Smrg    Boolean reversed = (xw->misc.re_verse);
3556d4fba8b9Smrg
3557d522f475Smrg    switch (n) {
3558d522f475Smrg    case TEXT_FG:
3559d4fba8b9Smrg	n = reversed ? TEXT_FG : TEXT_BG;
3560d522f475Smrg	break;
3561d522f475Smrg    case TEXT_BG:
3562d4fba8b9Smrg	n = reversed ? TEXT_BG : TEXT_FG;
3563d522f475Smrg	break;
3564d522f475Smrg    case MOUSE_FG:
3565d522f475Smrg	n = MOUSE_BG;
3566d522f475Smrg	break;
3567d522f475Smrg    case MOUSE_BG:
3568d522f475Smrg	n = MOUSE_FG;
3569d522f475Smrg	break;
3570d522f475Smrg#if OPT_TEK4014
3571d522f475Smrg    case TEK_FG:
3572d4fba8b9Smrg	n = reversed ? TEK_FG : TEK_BG;
3573d522f475Smrg	break;
3574d522f475Smrg    case TEK_BG:
3575d4fba8b9Smrg	n = reversed ? TEK_BG : TEK_FG;
3576d522f475Smrg	break;
3577d522f475Smrg#endif
3578d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3579d522f475Smrg    case HIGHLIGHT_FG:
3580d522f475Smrg	n = HIGHLIGHT_BG;
3581d522f475Smrg	break;
3582d522f475Smrg    case HIGHLIGHT_BG:
3583d522f475Smrg	n = HIGHLIGHT_FG;
3584d522f475Smrg	break;
3585d522f475Smrg#endif
3586d522f475Smrg    default:
3587d522f475Smrg	break;
3588d522f475Smrg    }
3589d522f475Smrg    return n;
3590d522f475Smrg}
3591d522f475Smrg
3592d4fba8b9Smrgstatic Bool
3593d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3594d522f475Smrg{
3595d4fba8b9Smrg    Bool result = False;
3596d4fba8b9Smrg
3597cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3598cd3331d0Smrg	XColor color;
3599cd3331d0Smrg	char buffer[80];
3600d522f475Smrg
3601cd3331d0Smrg	/*
3602cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3603cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3604cd3331d0Smrg	 * reporting the opposite color which would be used.
3605cd3331d0Smrg	 */
3606d4fba8b9Smrg	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3607cd3331d0Smrg
3608cd3331d0Smrg	GetOldColors(xw);
36099a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3610c48a5815Smrg	(void) QueryOneColor(xw, &color);
3611cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3612cd3331d0Smrg		color.red,
3613cd3331d0Smrg		color.green,
3614cd3331d0Smrg		color.blue);
3615712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
36169a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3617cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3618cd3331d0Smrg	unparseputs(xw, buffer);
3619cd3331d0Smrg	unparseputc1(xw, final);
3620d4fba8b9Smrg	result = True;
3621cd3331d0Smrg    }
3622d4fba8b9Smrg    return result;
3623d522f475Smrg}
3624d522f475Smrg
3625d522f475Smrgstatic Bool
3626d4fba8b9SmrgUpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3627d522f475Smrg{
3628d522f475Smrg    int i;
3629d522f475Smrg
3630d522f475Smrg    /* if we were going to free old colors, this would be the place to
3631d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3632d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3633d522f475Smrg     * we could save some overhead this way.   The only case in which this
3634d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3635d522f475Smrg     * which case they can restart xterm
3636d522f475Smrg     */
3637d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3638d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
36399a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
36409a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
36419a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3642d522f475Smrg	    }
3643d522f475Smrg	    if (pNew->names[i]) {
36449a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3645d522f475Smrg	    }
36469a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3647d522f475Smrg	}
3648d522f475Smrg    }
3649d522f475Smrg    return (True);
3650d522f475Smrg}
3651d522f475Smrg
3652d522f475Smrg/*
3653d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3654d522f475Smrg * xterm is compiled.
3655d522f475Smrg */
3656d522f475Smrgstatic int
3657d522f475SmrgOscToColorIndex(OscTextColors mode)
3658d522f475Smrg{
3659d522f475Smrg    int result = 0;
3660d522f475Smrg
3661d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3662d522f475Smrg    switch (mode) {
3663d522f475Smrg	CASE(TEXT_FG);
3664d522f475Smrg	CASE(TEXT_BG);
3665d522f475Smrg	CASE(TEXT_CURSOR);
3666d522f475Smrg	CASE(MOUSE_FG);
3667d522f475Smrg	CASE(MOUSE_BG);
3668d522f475Smrg#if OPT_TEK4014
3669d522f475Smrg	CASE(TEK_FG);
3670d522f475Smrg	CASE(TEK_BG);
3671d522f475Smrg#endif
3672d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3673d522f475Smrg	CASE(HIGHLIGHT_BG);
3674d522f475Smrg	CASE(HIGHLIGHT_FG);
3675d522f475Smrg#endif
3676d522f475Smrg#if OPT_TEK4014
3677d522f475Smrg	CASE(TEK_CURSOR);
3678d522f475Smrg#endif
3679d522f475Smrg    case OSC_NCOLORS:
3680d522f475Smrg	break;
3681d522f475Smrg    }
3682d522f475Smrg    return result;
3683d522f475Smrg}
3684d522f475Smrg
3685d522f475Smrgstatic Bool
3686d522f475SmrgChangeColorsRequest(XtermWidget xw,
3687d522f475Smrg		    int start,
3688d522f475Smrg		    char *names,
3689d522f475Smrg		    int final)
3690d522f475Smrg{
3691d522f475Smrg    Bool result = False;
3692d522f475Smrg    ScrnColors newColors;
3693d522f475Smrg
3694d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3695d522f475Smrg
3696d522f475Smrg    if (GetOldColors(xw)) {
3697037a25ddSmrg	int i;
3698d4fba8b9Smrg	int queried = 0;
3699037a25ddSmrg
3700d522f475Smrg	newColors.which = 0;
3701d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3702d522f475Smrg	    newColors.names[i] = NULL;
3703d522f475Smrg	}
3704d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3705037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3706d522f475Smrg	    if (xw->misc.re_verse)
3707d4fba8b9Smrg		ndx = oppositeColor(xw, ndx);
3708d522f475Smrg
3709cd3331d0Smrg	    if (IsEmpty(names)) {
3710d522f475Smrg		newColors.names[ndx] = NULL;
3711d522f475Smrg	    } else {
3712037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3713037a25ddSmrg
3714d522f475Smrg		names = strchr(names, ';');
3715d522f475Smrg		if (names != NULL) {
3716d522f475Smrg		    *names++ = '\0';
3717d522f475Smrg		}
3718fa3f02f3Smrg		if (thisName != 0) {
3719fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3720d4fba8b9Smrg			if (ReportColorRequest(xw, ndx, final))
3721d4fba8b9Smrg			    ++queried;
37229a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
37239a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3724fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3725fa3f02f3Smrg		    }
3726d522f475Smrg		}
3727d522f475Smrg	    }
3728d522f475Smrg	}
3729d522f475Smrg
3730d522f475Smrg	if (newColors.which != 0) {
3731d522f475Smrg	    ChangeColors(xw, &newColors);
3732d522f475Smrg	    UpdateOldColors(xw, &newColors);
3733d4fba8b9Smrg	} else if (queried) {
3734d4fba8b9Smrg	    unparse_end(xw);
3735d522f475Smrg	}
3736d522f475Smrg	result = True;
3737d522f475Smrg    }
3738d522f475Smrg    return result;
3739d522f475Smrg}
3740d522f475Smrg
3741cd3331d0Smrgstatic Bool
3742cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3743cd3331d0Smrg		   int code)
3744cd3331d0Smrg{
3745cd3331d0Smrg    Bool result = False;
3746cd3331d0Smrg
3747dfb07bc7Smrg    (void) xw;
3748dfb07bc7Smrg    (void) code;
3749dfb07bc7Smrg
3750cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3751cd3331d0Smrg    if (GetOldColors(xw)) {
3752037a25ddSmrg	ScrnColors newColors;
3753037a25ddSmrg	const char *thisName;
3754037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3755037a25ddSmrg
3756cd3331d0Smrg	if (xw->misc.re_verse)
3757d4fba8b9Smrg	    ndx = oppositeColor(xw, ndx);
3758cd3331d0Smrg
3759cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3760cd3331d0Smrg
3761cd3331d0Smrg	newColors.which = 0;
3762cd3331d0Smrg	newColors.names[ndx] = NULL;
3763cd3331d0Smrg
3764cd3331d0Smrg	if (thisName != 0
37659a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
37669a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3767cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3768cd3331d0Smrg
3769cd3331d0Smrg	    if (newColors.which != 0) {
3770cd3331d0Smrg		ChangeColors(xw, &newColors);
3771cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3772cd3331d0Smrg	    }
3773cd3331d0Smrg	}
3774cd3331d0Smrg	result = True;
3775cd3331d0Smrg    }
3776cd3331d0Smrg    return result;
3777cd3331d0Smrg}
3778cd3331d0Smrg
3779cd3331d0Smrg#if OPT_SHIFT_FONTS
3780cd3331d0Smrg/*
3781cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3782cd3331d0Smrg *
3783cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3784cd3331d0Smrg * the corresponding menu font entry.
3785cd3331d0Smrg */
3786cd3331d0Smrgstatic int
3787fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3788cd3331d0Smrg{
3789cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3790cd3331d0Smrg    int num = screen->menu_font_number;
3791cd3331d0Smrg    int rel = 0;
3792cd3331d0Smrg
3793cd3331d0Smrg    if (*++source == '+') {
3794cd3331d0Smrg	rel = 1;
3795cd3331d0Smrg	source++;
3796cd3331d0Smrg    } else if (*source == '-') {
3797cd3331d0Smrg	rel = -1;
3798cd3331d0Smrg	source++;
3799cd3331d0Smrg    }
3800cd3331d0Smrg
3801cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3802cd3331d0Smrg	int val = atoi(source);
3803cd3331d0Smrg	if (rel > 0)
3804cd3331d0Smrg	    rel = val;
3805cd3331d0Smrg	else if (rel < 0)
3806cd3331d0Smrg	    rel = -val;
3807cd3331d0Smrg	else
3808cd3331d0Smrg	    num = val;
3809cd3331d0Smrg    }
3810cd3331d0Smrg
3811cd3331d0Smrg    if (rel != 0) {
3812cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3813cd3331d0Smrg				     screen->menu_font_number, rel);
3814cd3331d0Smrg
3815cd3331d0Smrg    }
3816cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3817cd3331d0Smrg    *target = source;
3818cd3331d0Smrg    return num;
3819cd3331d0Smrg}
3820cd3331d0Smrg
3821cd3331d0Smrgstatic void
3822cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3823cd3331d0Smrg{
3824cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3825cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3826cd3331d0Smrg	Bool success = True;
3827cd3331d0Smrg	int num;
3828cb4a1343Smrg	String base = buf + 1;
3829cd3331d0Smrg	const char *name = 0;
3830cd3331d0Smrg
3831cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3832cd3331d0Smrg	if (num < 0
3833cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3834cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3835cd3331d0Smrg	    success = False;
3836cd3331d0Smrg	} else {
3837cd3331d0Smrg#if OPT_RENDERFONT
3838cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3839cd3331d0Smrg		name = getFaceName(xw, False);
3840cd3331d0Smrg	    } else
3841cd3331d0Smrg#endif
3842cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3843cd3331d0Smrg		success = False;
3844cd3331d0Smrg	    }
3845cd3331d0Smrg	}
3846cd3331d0Smrg
3847cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3848cd3331d0Smrg	unparseputs(xw, "50");
3849cd3331d0Smrg
3850cd3331d0Smrg	if (success) {
3851cd3331d0Smrg	    unparseputc(xw, ';');
3852cd3331d0Smrg	    if (buf >= base) {
3853cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3854cd3331d0Smrg		if (*buf != '\0') {
3855037a25ddSmrg		    char temp[10];
3856037a25ddSmrg
3857cd3331d0Smrg		    unparseputc(xw, '#');
3858cd3331d0Smrg		    sprintf(temp, "%d", num);
3859cd3331d0Smrg		    unparseputs(xw, temp);
3860cd3331d0Smrg		    if (*name != '\0')
3861cd3331d0Smrg			unparseputc(xw, ' ');
3862cd3331d0Smrg		}
3863cd3331d0Smrg	    }
3864cd3331d0Smrg	    unparseputs(xw, name);
3865cd3331d0Smrg	}
3866cd3331d0Smrg
3867cd3331d0Smrg	unparseputc1(xw, final);
3868cd3331d0Smrg	unparse_end(xw);
3869cd3331d0Smrg    }
3870cd3331d0Smrg}
3871cd3331d0Smrg
3872cd3331d0Smrgstatic void
3873cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3874cd3331d0Smrg{
3875cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3876cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3877cd3331d0Smrg	Bool success = True;
3878cd3331d0Smrg	int num;
3879cd3331d0Smrg	VTFontNames fonts;
3880cd3331d0Smrg	char *name;
3881cd3331d0Smrg
3882cd3331d0Smrg	/*
3883cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3884cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3885cd3331d0Smrg	 *
3886cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3887cd3331d0Smrg	 * to load the font entry.
3888cd3331d0Smrg	 */
3889cd3331d0Smrg	if (*buf == '#') {
3890cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3891cd3331d0Smrg
3892cd3331d0Smrg	    if (num < 0
3893cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3894cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3895cd3331d0Smrg		success = False;
3896cd3331d0Smrg	    } else {
3897cd3331d0Smrg		/*
3898cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3899cd3331d0Smrg		 * for a font specification within the control.
3900cd3331d0Smrg		 */
3901cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3902cd3331d0Smrg		    ++buf;
3903cd3331d0Smrg		}
3904cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3905cd3331d0Smrg		    ++buf;
3906cd3331d0Smrg		}
3907cd3331d0Smrg#if OPT_RENDERFONT
3908cd3331d0Smrg		if (UsingRenderFont(xw)) {
3909c219fbebSmrg		    /* EMPTY */
3910c219fbebSmrg		    /* there is only one font entry to load */
3911c219fbebSmrg		    ;
3912cd3331d0Smrg		} else
3913cd3331d0Smrg#endif
3914cd3331d0Smrg		{
3915cd3331d0Smrg		    /*
3916cd3331d0Smrg		     * Normally there is no font specified in the control.
3917cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3918cd3331d0Smrg		     */
3919cd3331d0Smrg		    if (*buf == '\0') {
3920cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3921cd3331d0Smrg			    success = False;
3922cd3331d0Smrg			}
3923cd3331d0Smrg		    }
3924cd3331d0Smrg		}
3925cd3331d0Smrg	    }
3926cd3331d0Smrg	} else {
3927cd3331d0Smrg	    num = screen->menu_font_number;
3928cd3331d0Smrg	}
3929cd3331d0Smrg	name = x_strtrim(buf);
393094644356Smrg	if (screen->EscapeFontName()) {
393194644356Smrg	    FREE_STRING(screen->EscapeFontName());
393294644356Smrg	    screen->EscapeFontName() = 0;
393394644356Smrg	}
3934cd3331d0Smrg	if (success && !IsEmpty(name)) {
3935cd3331d0Smrg#if OPT_RENDERFONT
3936cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3937cd3331d0Smrg		setFaceName(xw, name);
3938cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3939cd3331d0Smrg	    } else
3940cd3331d0Smrg#endif
3941cd3331d0Smrg	    {
3942cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3943cd3331d0Smrg		fonts.f_n = name;
3944cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
394594644356Smrg		if (num == screen->menu_font_number &&
394694644356Smrg		    num != fontMenu_fontescape) {
394794644356Smrg		    screen->EscapeFontName() = x_strdup(name);
394894644356Smrg		}
3949cd3331d0Smrg	    }
3950cd3331d0Smrg	} else {
3951cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3952cd3331d0Smrg	}
395394644356Smrg	update_font_escape();
3954cd3331d0Smrg	free(name);
3955cd3331d0Smrg    }
3956cd3331d0Smrg}
3957cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3958cd3331d0Smrg
3959d522f475Smrg/***====================================================================***/
3960d522f475Smrg
3961d522f475Smrgvoid
3962fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3963d522f475Smrg{
3964cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3965d522f475Smrg    int mode;
3966d522f475Smrg    Char *cp;
3967d522f475Smrg    int state = 0;
3968d522f475Smrg    char *buf = 0;
396950027b5bSmrg    char temp[20];
3970cd3331d0Smrg#if OPT_ISO_COLORS
3971cd3331d0Smrg    int ansi_colors = 0;
3972cd3331d0Smrg#endif
3973cd3331d0Smrg    Bool need_data = True;
3974fa3f02f3Smrg    Bool optional_data = False;
3975d522f475Smrg
3976d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3977d522f475Smrg
3978712a7ff4Smrg    (void) screen;
3979712a7ff4Smrg
3980d522f475Smrg    /*
3981d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3982d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3983d522f475Smrg     * with the same final character as the application sends to make this
3984d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3985d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3986d522f475Smrg     */
3987d522f475Smrg    mode = 0;
3988d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3989d522f475Smrg	switch (state) {
3990d522f475Smrg	case 0:
3991d522f475Smrg	    if (isdigit(*cp)) {
3992d522f475Smrg		mode = 10 * mode + (*cp - '0');
3993d522f475Smrg		if (mode > 65535) {
3994d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3995d522f475Smrg		    return;
3996d522f475Smrg		}
3997d522f475Smrg		break;
3998d4fba8b9Smrg	    } else {
3999d4fba8b9Smrg		switch (*cp) {
4000d4fba8b9Smrg		case 'I':
4001d4fba8b9Smrg		    xtermLoadIcon(xw, (char *) ++cp);
4002d4fba8b9Smrg		    return;
4003d4fba8b9Smrg		case 'l':
4004d4fba8b9Smrg		    ChangeTitle(xw, (char *) ++cp);
4005d4fba8b9Smrg		    return;
4006d4fba8b9Smrg		case 'L':
4007d4fba8b9Smrg		    ChangeIconName(xw, (char *) ++cp);
4008d4fba8b9Smrg		    return;
4009d4fba8b9Smrg		}
4010d522f475Smrg	    }
4011d522f475Smrg	    /* FALLTHRU */
4012d522f475Smrg	case 1:
4013d522f475Smrg	    if (*cp != ';') {
4014cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
4015cd3331d0Smrg		       (int) (cp - oscbuf)));
4016d522f475Smrg		return;
4017d522f475Smrg	    }
4018d522f475Smrg	    state = 2;
4019d522f475Smrg	    break;
4020d522f475Smrg	case 2:
4021d522f475Smrg	    buf = (char *) cp;
4022d522f475Smrg	    state = 3;
4023d522f475Smrg	    /* FALLTHRU */
4024d522f475Smrg	default:
4025cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4026d522f475Smrg		switch (mode) {
4027d522f475Smrg		case 0:
4028d522f475Smrg		case 1:
4029d522f475Smrg		case 2:
4030d522f475Smrg		    break;
4031d522f475Smrg		default:
4032d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
4033d522f475Smrg			   CharOf(*cp),
4034cd3331d0Smrg			   (int) (cp - oscbuf)));
4035d522f475Smrg		    return;
4036d522f475Smrg		}
4037d522f475Smrg	    }
4038d522f475Smrg	}
4039d522f475Smrg    }
4040cd3331d0Smrg
40413367019cSmrg    /*
40423367019cSmrg     * Check if the palette changed and there are no more immediate changes
40433367019cSmrg     * that could be deferred to the next repaint.
40443367019cSmrg     */
4045dfb07bc7Smrg    if (xw->work.palette_changed) {
40463367019cSmrg	switch (mode) {
4047d4fba8b9Smrg	case 03:		/* change X property */
40483367019cSmrg	case 30:		/* Konsole (unused) */
40493367019cSmrg	case 31:		/* Konsole (unused) */
40503367019cSmrg	case 50:		/* font operations */
40513367019cSmrg	case 51:		/* Emacs (unused) */
40523367019cSmrg#if OPT_PASTE64
40533367019cSmrg	case 52:		/* selection data */
40543367019cSmrg#endif
40553367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
4056dfb07bc7Smrg	    xw->work.palette_changed = False;
40573367019cSmrg	    xtermRepaint(xw);
40583367019cSmrg	    break;
4059d4fba8b9Smrg	default:
4060d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
4061d4fba8b9Smrg	    break;
40623367019cSmrg	}
40633367019cSmrg    }
40643367019cSmrg
4065cd3331d0Smrg    /*
4066cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
4067cd3331d0Smrg     * a special case.
4068cd3331d0Smrg     */
4069cd3331d0Smrg    switch (mode) {
407094644356Smrg    case 50:
4071cd3331d0Smrg#if OPT_ISO_COLORS
4072cd3331d0Smrg    case OSC_Reset(4):
4073cd3331d0Smrg    case OSC_Reset(5):
4074fa3f02f3Smrg	need_data = False;
4075fa3f02f3Smrg	optional_data = True;
4076fa3f02f3Smrg	break;
4077cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4078cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4079cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4080cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4081cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4082cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4083cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4084cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4085cd3331d0Smrg#endif
4086cd3331d0Smrg#if OPT_TEK4014
4087cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4088cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4089cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4090cd3331d0Smrg#endif
4091cd3331d0Smrg	need_data = False;
4092cd3331d0Smrg	break;
4093cd3331d0Smrg#endif
4094cd3331d0Smrg    default:
4095cd3331d0Smrg	break;
4096cd3331d0Smrg    }
4097cd3331d0Smrg
4098cd3331d0Smrg    /*
4099cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
4100cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
4101cd3331d0Smrg     */
4102cd3331d0Smrg    if (IsEmpty(buf)) {
4103cd3331d0Smrg	if (need_data) {
410450027b5bSmrg	    switch (mode) {
410550027b5bSmrg	    case 0:
410650027b5bSmrg	    case 1:
410750027b5bSmrg	    case 2:
410850027b5bSmrg		buf = strcpy(temp, "xterm");
410950027b5bSmrg		break;
411050027b5bSmrg	    default:
411150027b5bSmrg		TRACE(("do_osc found no data\n"));
411250027b5bSmrg		return;
411350027b5bSmrg	    }
411450027b5bSmrg	} else {
411550027b5bSmrg	    temp[0] = '\0';
411650027b5bSmrg	    buf = temp;
4117cd3331d0Smrg	}
4118fa3f02f3Smrg    } else if (!need_data && !optional_data) {
4119fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
4120d522f475Smrg	return;
41210d92cbfdSchristos    }
4122d522f475Smrg
4123d522f475Smrg    switch (mode) {
4124d522f475Smrg    case 0:			/* new icon name and title */
4125b7c89284Ssnj	ChangeIconName(xw, buf);
4126b7c89284Ssnj	ChangeTitle(xw, buf);
4127d522f475Smrg	break;
4128d522f475Smrg
4129d522f475Smrg    case 1:			/* new icon name only */
4130b7c89284Ssnj	ChangeIconName(xw, buf);
4131d522f475Smrg	break;
4132d522f475Smrg
4133d522f475Smrg    case 2:			/* new title only */
4134b7c89284Ssnj	ChangeTitle(xw, buf);
4135d522f475Smrg	break;
4136d522f475Smrg
413722d8e007Schristos#ifdef notdef
4138d522f475Smrg    case 3:			/* change X property */
4139cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
41400d92cbfdSchristos	    ChangeXprop(buf);
4141d522f475Smrg	break;
414222d8e007Schristos#endif
4143d522f475Smrg#if OPT_ISO_COLORS
4144cd3331d0Smrg    case 5:
4145cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4146cd3331d0Smrg	/* FALLTHRU */
4147d522f475Smrg    case 4:
4148d4fba8b9Smrg	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4149dfb07bc7Smrg	    xw->work.palette_changed = True;
4150cd3331d0Smrg	break;
415194644356Smrg    case 6:
415294644356Smrg	/* FALLTHRU */
415394644356Smrg    case OSC_Reset(6):
415494644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
415594644356Smrg	while (*buf != '\0') {
415694644356Smrg	    long which = 0;
415794644356Smrg	    long value = 0;
415894644356Smrg	    char *next;
415994644356Smrg	    if (*buf == ';') {
416094644356Smrg		++buf;
416194644356Smrg	    } else {
416294644356Smrg		which = strtol(buf, &next, 10);
4163037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
416494644356Smrg		    break;
416594644356Smrg		buf = next;
416694644356Smrg		if (*buf == ';')
416794644356Smrg		    ++buf;
416894644356Smrg	    }
416994644356Smrg	    if (*buf == ';') {
417094644356Smrg		++buf;
417194644356Smrg	    } else {
417294644356Smrg		value = strtol(buf, &next, 10);
4173dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
417494644356Smrg		    break;
417594644356Smrg		buf = next;
417694644356Smrg		if (*buf == ';')
417794644356Smrg		    ++buf;
417894644356Smrg	    }
417994644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
418094644356Smrg	    switch (which) {
418194644356Smrg	    case 0:
418294644356Smrg		screen->colorBDMode = (value != 0);
418394644356Smrg		break;
418494644356Smrg	    case 1:
418594644356Smrg		screen->colorULMode = (value != 0);
418694644356Smrg		break;
418794644356Smrg	    case 2:
418894644356Smrg		screen->colorBLMode = (value != 0);
418994644356Smrg		break;
419094644356Smrg	    case 3:
419194644356Smrg		screen->colorRVMode = (value != 0);
419294644356Smrg		break;
419394644356Smrg#if OPT_WIDE_ATTRS
419494644356Smrg	    case 4:
419594644356Smrg		screen->colorITMode = (value != 0);
419694644356Smrg		break;
419794644356Smrg#endif
419894644356Smrg	    default:
419994644356Smrg		TRACE(("...unknown colorXXMode\n"));
420094644356Smrg		break;
420194644356Smrg	    }
420294644356Smrg	}
420394644356Smrg	break;
4204cd3331d0Smrg    case OSC_Reset(5):
4205cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4206cd3331d0Smrg	/* FALLTHRU */
4207cd3331d0Smrg    case OSC_Reset(4):
4208cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4209dfb07bc7Smrg	    xw->work.palette_changed = True;
4210d522f475Smrg	break;
4211d522f475Smrg#endif
4212d522f475Smrg    case OSC_TEXT_FG:
4213d522f475Smrg    case OSC_TEXT_BG:
4214d522f475Smrg    case OSC_TEXT_CURSOR:
4215d522f475Smrg    case OSC_MOUSE_FG:
4216d522f475Smrg    case OSC_MOUSE_BG:
4217d522f475Smrg#if OPT_HIGHLIGHT_COLOR
4218d522f475Smrg    case OSC_HIGHLIGHT_BG:
4219cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
4220d522f475Smrg#endif
4221d522f475Smrg#if OPT_TEK4014
4222d522f475Smrg    case OSC_TEK_FG:
4223d522f475Smrg    case OSC_TEK_BG:
4224d522f475Smrg    case OSC_TEK_CURSOR:
4225d522f475Smrg#endif
4226cd3331d0Smrg	if (xw->misc.dynamicColors) {
4227d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
4228cd3331d0Smrg	}
4229cd3331d0Smrg	break;
4230cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4231cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4232cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4233cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4234cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4235cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4236cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4237cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4238cd3331d0Smrg#endif
4239cd3331d0Smrg#if OPT_TEK4014
4240cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4241cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4242cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4243cd3331d0Smrg#endif
4244cd3331d0Smrg	if (xw->misc.dynamicColors) {
4245cd3331d0Smrg	    ResetColorsRequest(xw, mode);
4246cd3331d0Smrg	}
4247d522f475Smrg	break;
4248d522f475Smrg
42498f44fb3bSmrg    case 22:
42508f44fb3bSmrg	xtermSetupPointer(xw, buf);
42518f44fb3bSmrg	break;
42528f44fb3bSmrg
4253d522f475Smrg    case 30:
4254d522f475Smrg    case 31:
4255d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
4256d522f475Smrg	break;
4257d522f475Smrg
4258d522f475Smrg#ifdef ALLOWLOGGING
4259d522f475Smrg    case 46:			/* new log file */
4260d522f475Smrg#ifdef ALLOWLOGFILECHANGES
4261d522f475Smrg	/*
4262d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
4263d522f475Smrg	 * arbitrary files accessible to the person running xterm.
4264d522f475Smrg	 */
4265037a25ddSmrg	if (strcmp(buf, "?")) {
4266037a25ddSmrg	    char *bp;
4267dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
4268d4fba8b9Smrg		free(screen->logfile);
4269037a25ddSmrg		screen->logfile = bp;
4270037a25ddSmrg		break;
4271037a25ddSmrg	    }
4272d522f475Smrg	}
4273d522f475Smrg#endif
4274cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4275cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4276d522f475Smrg	break;
4277d522f475Smrg#endif /* ALLOWLOGGING */
4278d522f475Smrg
4279d522f475Smrg    case 50:
4280d522f475Smrg#if OPT_SHIFT_FONTS
4281cd3331d0Smrg	if (*buf == '?') {
4282cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
4283cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
4284cd3331d0Smrg	    ChangeFontRequest(xw, buf);
4285d522f475Smrg	}
4286d522f475Smrg#endif /* OPT_SHIFT_FONTS */
4287d522f475Smrg	break;
4288d522f475Smrg    case 51:
4289d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
4290d522f475Smrg	break;
4291d522f475Smrg
4292d522f475Smrg#if OPT_PASTE64
4293d522f475Smrg    case 52:
4294cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
4295d522f475Smrg	break;
4296d522f475Smrg#endif
4297d522f475Smrg	/*
4298d522f475Smrg	 * One could write code to send back the display and host names,
4299d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
4300d522f475Smrg	 */
4301cd3331d0Smrg    default:
4302cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
4303cd3331d0Smrg	break;
4304d522f475Smrg    }
4305d522f475Smrg    unparse_end(xw);
4306d522f475Smrg}
4307d522f475Smrg
4308d522f475Smrg/*
4309d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
4310d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
4311d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4312d522f475Smrg * "real" terminals accept commas in the string definitions).
4313d522f475Smrg */
4314d522f475Smrgstatic int
4315cd3331d0Smrgudk_value(const char **cp)
4316d522f475Smrg{
4317cd3331d0Smrg    int result = -1;
4318d522f475Smrg
4319d522f475Smrg    for (;;) {
4320037a25ddSmrg	int c;
4321037a25ddSmrg
4322d522f475Smrg	if ((c = **cp) != '\0')
4323d522f475Smrg	    *cp = *cp + 1;
4324d522f475Smrg	if (c == ';' || c == '\0')
4325cd3331d0Smrg	    break;
4326cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
4327cd3331d0Smrg	    break;
4328d522f475Smrg    }
4329cd3331d0Smrg
4330cd3331d0Smrg    return result;
4331d522f475Smrg}
4332d522f475Smrg
4333d522f475Smrgvoid
43349a64e1c5Smrgreset_decudk(XtermWidget xw)
4335d522f475Smrg{
4336d522f475Smrg    int n;
4337d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
4338d4fba8b9Smrg	FreeAndNull(xw->work.user_keys[n].str);
4339d4fba8b9Smrg	xw->work.user_keys[n].len = 0;
4340d522f475Smrg    }
4341d522f475Smrg}
4342d522f475Smrg
4343d522f475Smrg/*
4344d522f475Smrg * Parse the data for DECUDK (user-defined keys).
4345d522f475Smrg */
4346d522f475Smrgstatic void
43479a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
4348d522f475Smrg{
4349d522f475Smrg    while (*cp) {
4350cd3331d0Smrg	const char *base = cp;
4351d4fba8b9Smrg	char *str = malloc(strlen(cp) + 3);
4352d522f475Smrg	unsigned key = 0;
4353d522f475Smrg	int len = 0;
4354d522f475Smrg
435594644356Smrg	if (str == NULL)
435694644356Smrg	    break;
435794644356Smrg
4358d522f475Smrg	while (isdigit(CharOf(*cp)))
43590d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
4360037a25ddSmrg
4361d522f475Smrg	if (*cp == '/') {
4362037a25ddSmrg	    int lo, hi;
4363037a25ddSmrg
4364d522f475Smrg	    cp++;
4365d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
4366d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
43670d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
4368d522f475Smrg	    }
4369d522f475Smrg	}
4370d522f475Smrg	if (len > 0 && key < MAX_UDK) {
43713367019cSmrg	    str[len] = '\0';
4372d4fba8b9Smrg	    free(xw->work.user_keys[key].str);
43739a64e1c5Smrg	    xw->work.user_keys[key].str = str;
43749a64e1c5Smrg	    xw->work.user_keys[key].len = len;
4375d4fba8b9Smrg	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4376d522f475Smrg	} else {
4377d522f475Smrg	    free(str);
4378d522f475Smrg	}
4379d522f475Smrg	if (*cp == ';')
4380d522f475Smrg	    cp++;
4381d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
4382d522f475Smrg	    break;
4383d522f475Smrg    }
4384d522f475Smrg}
4385d522f475Smrg
4386fa3f02f3Smrg/*
4387fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
4388fa3f02f3Smrg * interspersing with control characters, but have the string already.
4389fa3f02f3Smrg */
4390fa3f02f3Smrgstatic void
4391fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
4392fa3f02f3Smrg{
4393fa3f02f3Smrg    const char *cp = *string;
4394fa3f02f3Smrg    ParmType nparam = 0;
4395fa3f02f3Smrg    int last_empty = 1;
4396fa3f02f3Smrg
4397fa3f02f3Smrg    memset(params, 0, sizeof(*params));
4398fa3f02f3Smrg    while (*cp != '\0') {
4399fa3f02f3Smrg	Char ch = CharOf(*cp++);
4400fa3f02f3Smrg
4401fa3f02f3Smrg	if (isdigit(ch)) {
4402fa3f02f3Smrg	    last_empty = 0;
4403fa3f02f3Smrg	    if (nparam < NPARAM) {
4404fa3f02f3Smrg		params->a_param[nparam] =
4405fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
4406fa3f02f3Smrg				+ (ch - '0'));
4407fa3f02f3Smrg	    }
4408fa3f02f3Smrg	} else if (ch == ';') {
4409fa3f02f3Smrg	    last_empty = 1;
4410fa3f02f3Smrg	    nparam++;
4411fa3f02f3Smrg	} else if (ch < 32) {
4412fa3f02f3Smrg	    /* EMPTY */ ;
4413fa3f02f3Smrg	} else {
4414fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
4415fa3f02f3Smrg	    params->a_final = ch;
4416fa3f02f3Smrg	    break;
4417fa3f02f3Smrg	}
4418fa3f02f3Smrg    }
4419fa3f02f3Smrg
4420fa3f02f3Smrg    *string = cp;
4421fa3f02f3Smrg    if (!last_empty)
4422fa3f02f3Smrg	nparam++;
4423fa3f02f3Smrg    if (nparam > NPARAM)
4424fa3f02f3Smrg	params->a_nparam = NPARAM;
4425fa3f02f3Smrg    else
4426fa3f02f3Smrg	params->a_nparam = nparam;
4427fa3f02f3Smrg}
4428fa3f02f3Smrg
4429d522f475Smrg#if OPT_TRACE
4430d522f475Smrg#define SOFT_WIDE 10
4431d522f475Smrg#define SOFT_HIGH 20
4432d522f475Smrg
4433d522f475Smrgstatic void
4434fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
4435d522f475Smrg{
4436d522f475Smrg    char DscsName[8];
4437d522f475Smrg    int len;
4438d522f475Smrg    int Pfn = params->a_param[0];
4439d522f475Smrg    int Pcn = params->a_param[1];
4440d522f475Smrg    int Pe = params->a_param[2];
4441d522f475Smrg    int Pcmw = params->a_param[3];
4442d522f475Smrg    int Pw = params->a_param[4];
4443d522f475Smrg    int Pt = params->a_param[5];
4444d522f475Smrg    int Pcmh = params->a_param[6];
4445d522f475Smrg    int Pcss = params->a_param[7];
4446d522f475Smrg
4447d522f475Smrg    int start_char = Pcn + 0x20;
4448d522f475Smrg    int char_wide = ((Pcmw == 0)
4449d522f475Smrg		     ? (Pcss ? 6 : 10)
4450d522f475Smrg		     : (Pcmw > 4
4451d522f475Smrg			? Pcmw
4452d522f475Smrg			: (Pcmw + 3)));
4453d522f475Smrg    int char_high = ((Pcmh == 0)
44543367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
4455d522f475Smrg			? 10
4456d522f475Smrg			: 20)
4457d522f475Smrg		     : Pcmh);
4458d522f475Smrg    Char ch;
4459d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
4460d522f475Smrg    Bool first = True;
4461d522f475Smrg    Bool prior = False;
4462d522f475Smrg    int row = 0, col = 0;
4463d522f475Smrg
4464d522f475Smrg    TRACE(("Parsing DECDLD\n"));
4465d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
4466d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
4467d522f475Smrg    TRACE(("  erase control %d\n", Pe));
4468d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
4469d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
4470d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
4471d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
4472d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
4473d522f475Smrg
4474d522f475Smrg    if (Pfn > 1
4475d522f475Smrg	|| Pcn > 95
4476d522f475Smrg	|| Pe > 2
4477d522f475Smrg	|| Pcmw > 10
4478d522f475Smrg	|| Pcmw == 1
4479d522f475Smrg	|| Pt > 2
4480d522f475Smrg	|| Pcmh > 20
4481d522f475Smrg	|| Pcss > 1
4482d522f475Smrg	|| char_wide > SOFT_WIDE
4483d522f475Smrg	|| char_high > SOFT_HIGH) {
4484d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4485d522f475Smrg	return;
4486d522f475Smrg    }
4487d522f475Smrg
4488d522f475Smrg    len = 0;
4489d522f475Smrg    while (*string != '\0') {
4490d522f475Smrg	ch = CharOf(*string++);
4491d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4492d522f475Smrg	    if (len < 2)
4493b7c89284Ssnj		DscsName[len++] = (char) ch;
4494d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4495b7c89284Ssnj	    DscsName[len++] = (char) ch;
4496d522f475Smrg	    break;
4497d522f475Smrg	}
4498d522f475Smrg    }
4499d522f475Smrg    DscsName[len] = 0;
4500d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4501d522f475Smrg
4502d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4503d522f475Smrg    while (*string != '\0') {
4504d522f475Smrg	if (first) {
4505d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4506d522f475Smrg	    if (prior) {
4507d522f475Smrg		for (row = 0; row < char_high; ++row) {
4508d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4509d522f475Smrg		}
4510d522f475Smrg	    }
4511d522f475Smrg	    prior = False;
4512d522f475Smrg	    first = False;
4513d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4514d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4515d522f475Smrg		    bits[row][col] = '.';
4516d522f475Smrg		}
4517d522f475Smrg	    }
4518d522f475Smrg	    row = col = 0;
4519d522f475Smrg	}
4520d522f475Smrg	ch = CharOf(*string++);
4521d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4522d522f475Smrg	    int n;
4523d522f475Smrg
4524b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4525d522f475Smrg	    for (n = 0; n < 6; ++n) {
4526b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4527d522f475Smrg	    }
4528d522f475Smrg	    col += 1;
4529d522f475Smrg	    prior = True;
4530d522f475Smrg	} else if (ch == '/') {
4531d522f475Smrg	    row += 6;
4532d522f475Smrg	    col = 0;
4533d522f475Smrg	} else if (ch == ';') {
4534d522f475Smrg	    first = True;
4535d522f475Smrg	    ++start_char;
4536d522f475Smrg	}
4537d522f475Smrg    }
4538d522f475Smrg}
4539d522f475Smrg#else
4540d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4541d522f475Smrg#endif
4542d522f475Smrg
4543d4fba8b9Smrg#if OPT_DEC_RECTOPS
4544d4fba8b9Smrgstatic const char *
4545d4fba8b9Smrgskip_params(const char *cp)
4546d4fba8b9Smrg{
4547d4fba8b9Smrg    while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4548d4fba8b9Smrg	++cp;
4549d4fba8b9Smrg    return cp;
4550d4fba8b9Smrg}
4551d4fba8b9Smrg
4552d4fba8b9Smrgstatic int
4553d4fba8b9Smrgparse_int_param(const char **cp)
4554d4fba8b9Smrg{
4555d4fba8b9Smrg    int result = 0;
4556d4fba8b9Smrg    const char *s = *cp;
4557d4fba8b9Smrg    while (*s != '\0') {
4558d4fba8b9Smrg	if (*s == ';') {
4559d4fba8b9Smrg	    ++s;
4560d4fba8b9Smrg	    break;
4561d4fba8b9Smrg	} else if (*s >= '0' && *s <= '9') {
4562d4fba8b9Smrg	    result = (result * 10) + (*s++ - '0');
4563d4fba8b9Smrg	} else {
4564d4fba8b9Smrg	    s += strlen(s);
4565d4fba8b9Smrg	}
4566d4fba8b9Smrg    }
4567d4fba8b9Smrg    TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
4568d4fba8b9Smrg    *cp = s;
4569d4fba8b9Smrg    return result;
4570d4fba8b9Smrg}
4571d4fba8b9Smrg
4572d4fba8b9Smrgstatic int
4573d4fba8b9Smrgparse_chr_param(const char **cp)
4574d4fba8b9Smrg{
4575d4fba8b9Smrg    int result = 0;
4576d4fba8b9Smrg    const char *s = *cp;
4577d4fba8b9Smrg    if (*s != '\0') {
4578d4fba8b9Smrg	if ((result = CharOf(*s++)) != 0) {
4579d4fba8b9Smrg	    if (*s == ';') {
4580d4fba8b9Smrg		++s;
4581d4fba8b9Smrg	    } else if (*s != '\0') {
4582d4fba8b9Smrg		result = 0;
4583d4fba8b9Smrg	    }
4584d4fba8b9Smrg	}
4585d4fba8b9Smrg    }
4586d4fba8b9Smrg    TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
4587d4fba8b9Smrg    *cp = s;
4588d4fba8b9Smrg    return result;
4589d4fba8b9Smrg}
4590d4fba8b9Smrg
4591d4fba8b9Smrgstatic void
4592d4fba8b9Smrgrestore_DECCIR(XtermWidget xw, const char *cp)
4593d4fba8b9Smrg{
4594d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
4595d4fba8b9Smrg    int value;
4596d4fba8b9Smrg
4597d4fba8b9Smrg    /* row */
4598d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4599d4fba8b9Smrg	return;
4600d4fba8b9Smrg    screen->cur_row = (value - 1);
4601d4fba8b9Smrg
4602d4fba8b9Smrg    /* column */
4603d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4604d4fba8b9Smrg	return;
4605d4fba8b9Smrg    screen->cur_col = (value - 1);
4606d4fba8b9Smrg
4607d4fba8b9Smrg    /* page */
4608d4fba8b9Smrg    if (parse_int_param(&cp) != 1)
4609d4fba8b9Smrg	return;
4610d4fba8b9Smrg
4611d4fba8b9Smrg    /* rendition */
4612d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4613d4fba8b9Smrg	return;
4614d4fba8b9Smrg    UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4615d4fba8b9Smrg    xw->flags |= (value & 8) ? INVERSE : 0;
4616d4fba8b9Smrg    xw->flags |= (value & 4) ? BLINK : 0;
4617d4fba8b9Smrg    xw->flags |= (value & 2) ? UNDERLINE : 0;
4618d4fba8b9Smrg    xw->flags |= (value & 1) ? BOLD : 0;
4619d4fba8b9Smrg
4620d4fba8b9Smrg    /* attributes */
4621d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4622d4fba8b9Smrg	return;
4623d4fba8b9Smrg    screen->protected_mode &= ~DEC_PROTECT;
4624d4fba8b9Smrg    screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4625d4fba8b9Smrg
4626d4fba8b9Smrg    /* flags */
4627d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4628d4fba8b9Smrg	return;
4629d4fba8b9Smrg    screen->do_wrap = (value & 8) ? True : False;
4630d4fba8b9Smrg    screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4631d4fba8b9Smrg    UIntClr(xw->flags, ORIGIN);
4632d4fba8b9Smrg    xw->flags |= (value & 1) ? ORIGIN : 0;
4633d4fba8b9Smrg
4634d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4635d4fba8b9Smrg	return;
4636d4fba8b9Smrg    screen->curgl = (Char) value;
4637d4fba8b9Smrg
4638d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4639d4fba8b9Smrg	return;
4640d4fba8b9Smrg    screen->curgr = (Char) value;
4641d4fba8b9Smrg
4642d4fba8b9Smrg    /* character-set size */
4643d4fba8b9Smrg    if (parse_chr_param(&cp) != 0x4f)	/* works for xterm */
4644d4fba8b9Smrg	return;
4645d4fba8b9Smrg
4646d4fba8b9Smrg    /* SCS designators */
4647d4fba8b9Smrg    for (value = 0; value < NUM_GSETS; ++value) {
4648d4fba8b9Smrg	if (*cp == '%') {
4649d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '%', *++cp);
4650d4fba8b9Smrg	} else if (*cp != '\0') {
4651d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4652d4fba8b9Smrg	} else {
4653d4fba8b9Smrg	    return;
4654d4fba8b9Smrg	}
4655d4fba8b9Smrg	cp++;
4656d4fba8b9Smrg    }
4657d4fba8b9Smrg
4658d4fba8b9Smrg    TRACE(("...done DECCIR\n"));
4659d4fba8b9Smrg}
4660d4fba8b9Smrg
4661d4fba8b9Smrgstatic void
4662d4fba8b9Smrgrestore_DECTABSR(XtermWidget xw, const char *cp)
4663d4fba8b9Smrg{
4664d4fba8b9Smrg    int stop = 0;
4665d4fba8b9Smrg    Bool fail = False;
4666d4fba8b9Smrg
4667d4fba8b9Smrg    TabZonk(xw->tabs);
4668d4fba8b9Smrg    while (*cp != '\0' && !fail) {
4669d4fba8b9Smrg	if ((*cp) >= '0' && (*cp) <= '9') {
4670d4fba8b9Smrg	    stop = (stop * 10) + ((*cp) - '0');
4671d4fba8b9Smrg	} else if (*cp == '/') {
4672d4fba8b9Smrg	    --stop;
4673d4fba8b9Smrg	    if (OkTAB(stop)) {
4674d4fba8b9Smrg		TabSet(xw->tabs, stop);
4675d4fba8b9Smrg		stop = 0;
4676d4fba8b9Smrg	    } else {
4677d4fba8b9Smrg		fail = True;
4678d4fba8b9Smrg	    }
4679d4fba8b9Smrg	} else {
4680d4fba8b9Smrg	    fail = True;
4681d4fba8b9Smrg	}
4682d4fba8b9Smrg	++cp;
4683d4fba8b9Smrg    }
4684d4fba8b9Smrg    --stop;
4685d4fba8b9Smrg    if (OkTAB(stop))
4686d4fba8b9Smrg	TabSet(xw->tabs, stop);
4687d4fba8b9Smrg
4688d4fba8b9Smrg    TRACE(("...done DECTABSR\n"));
4689d4fba8b9Smrg}
4690d4fba8b9Smrg#endif
4691d4fba8b9Smrg
4692d522f475Smrgvoid
4693fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4694d522f475Smrg{
4695cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4696d522f475Smrg    char reply[BUFSIZ];
4697cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4698d522f475Smrg    Bool okay;
4699d522f475Smrg    ANSI params;
4700d4fba8b9Smrg#if OPT_DEC_RECTOPS
4701d4fba8b9Smrg    char psarg = '0';
4702d4fba8b9Smrg#endif
4703d522f475Smrg
4704cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4705d522f475Smrg
4706d522f475Smrg    if (dcslen != strlen(cp))
4707d522f475Smrg	/* shouldn't have nulls in the string */
4708d522f475Smrg	return;
4709d522f475Smrg
4710d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4711d522f475Smrg    case '$':			/* DECRQSS */
4712d522f475Smrg	okay = True;
4713d522f475Smrg
4714d522f475Smrg	cp++;
4715d4fba8b9Smrg	if (*cp == 'q') {
4716d4fba8b9Smrg	    *reply = '\0';
4717d4fba8b9Smrg	    cp++;
4718d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4719d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCA\n"));
4720d522f475Smrg		sprintf(reply, "%d%s",
4721d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4722d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4723d522f475Smrg			cp);
4724d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
47253367019cSmrg		if (screen->vtXX_level < 2) {
47263367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
47273367019cSmrg		    break;
47283367019cSmrg		}
4729d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCL\n"));
4730d522f475Smrg		sprintf(reply, "%d%s%s",
4731d522f475Smrg			(screen->vtXX_level ?
4732d522f475Smrg			 screen->vtXX_level : 1) + 60,
4733d522f475Smrg			(screen->vtXX_level >= 2)
4734d522f475Smrg			? (screen->control_eight_bits
4735d522f475Smrg			   ? ";0" : ";1")
4736d522f475Smrg			: "",
4737d522f475Smrg			cp);
4738d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4739d4fba8b9Smrg		TRACE(("DECRQSS -> DECSTBM\n"));
4740d522f475Smrg		sprintf(reply, "%d;%dr",
4741d522f475Smrg			screen->top_marg + 1,
4742d522f475Smrg			screen->bot_marg + 1);
47433367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
47443367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
4745d4fba8b9Smrg		    TRACE(("DECRQSS -> DECSLRM\n"));
47463367019cSmrg		    sprintf(reply, "%d;%ds",
47473367019cSmrg			    screen->lft_marg + 1,
47483367019cSmrg			    screen->rgt_marg + 1);
4749037a25ddSmrg		} else {
4750037a25ddSmrg		    okay = False;
47513367019cSmrg		}
4752d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4753d4fba8b9Smrg		TRACE(("DECRQSS -> SGR\n"));
4754d4fba8b9Smrg		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4755d522f475Smrg		strcat(reply, "m");
4756712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
47573367019cSmrg		int code = STEADY_BLOCK;
47583367019cSmrg		if (isCursorUnderline(screen))
47593367019cSmrg		    code = STEADY_UNDERLINE;
47603367019cSmrg		else if (isCursorBar(screen))
47613367019cSmrg		    code = STEADY_BAR;
47623367019cSmrg#if OPT_BLINK_CURS
476394644356Smrg		if (screen->cursor_blink_esc != 0)
47643367019cSmrg		    code -= 1;
47653367019cSmrg#endif
4766d4fba8b9Smrg		TRACE(("reply DECSCUSR\n"));
47673367019cSmrg		sprintf(reply, "%d%s", code, cp);
4768d4fba8b9Smrg	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4769d4fba8b9Smrg		sprintf(reply, "%d%s",
4770d4fba8b9Smrg			((screen->max_row > 24) ? screen->max_row : 24),
4771d4fba8b9Smrg			cp);
4772d4fba8b9Smrg		TRACE(("reply DECSLPP\n"));
4773d4fba8b9Smrg	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4774d4fba8b9Smrg		TRACE(("reply DECSCPP\n"));
4775d4fba8b9Smrg		sprintf(reply, "%d%s",
4776d4fba8b9Smrg			((xw->flags & IN132COLUMNS) ? 132 : 80),
4777d4fba8b9Smrg			cp);
477850027b5bSmrg	    } else
477950027b5bSmrg#if OPT_STATUS_LINE
478050027b5bSmrg	    if (!strcmp(cp, "$}")) {	/* DECSASD */
478150027b5bSmrg		TRACE(("reply DECSASD\n"));
478250027b5bSmrg		sprintf(reply, "%d%s",
478350027b5bSmrg			screen->status_active,
478450027b5bSmrg			cp);
478550027b5bSmrg	    } else if (!strcmp(cp, "$~")) {	/* DECSSDT */
478650027b5bSmrg		TRACE(("reply DECSASD\n"));
478750027b5bSmrg		sprintf(reply, "%d%s",
478850027b5bSmrg			screen->status_type,
478950027b5bSmrg			cp);
479050027b5bSmrg	    } else
479150027b5bSmrg#endif
479250027b5bSmrg	    if (!strcmp(cp, "*|")) {	/* DECSNLS */
4793d4fba8b9Smrg		TRACE(("reply DECSNLS\n"));
4794d4fba8b9Smrg		sprintf(reply, "%d%s",
4795d4fba8b9Smrg			screen->max_row + 1,
4796d4fba8b9Smrg			cp);
47970d92cbfdSchristos	    } else {
4798d4fba8b9Smrg		okay = False;
479922d8e007Schristos	    }
4800d4fba8b9Smrg
4801d4fba8b9Smrg	    unparseputc1(xw, ANSI_DCS);
4802d4fba8b9Smrg	    unparseputc(xw, okay ? '1' : '0');
4803d4fba8b9Smrg	    unparseputc(xw, '$');
4804d4fba8b9Smrg	    unparseputc(xw, 'r');
4805d4fba8b9Smrg	    cp = reply;
4806d4fba8b9Smrg	    unparseputs(xw, cp);
4807d4fba8b9Smrg	    unparseputc1(xw, ANSI_ST);
4808d522f475Smrg	} else {
4809d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4810d522f475Smrg	}
4811d522f475Smrg	break;
4812d522f475Smrg    case '+':
4813d522f475Smrg	cp++;
4814cd3331d0Smrg	switch (*cp) {
4815d4fba8b9Smrg#if OPT_TCAP_QUERY
4816cd3331d0Smrg	case 'p':
4817cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4818cd3331d0Smrg		set_termcap(xw, cp + 1);
4819cd3331d0Smrg	    }
4820cd3331d0Smrg	    break;
4821cd3331d0Smrg	case 'q':
4822cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4823cd3331d0Smrg		Bool fkey;
4824cd3331d0Smrg		unsigned state;
4825cd3331d0Smrg		int code;
4826cd3331d0Smrg		const char *tmp;
4827cd3331d0Smrg		const char *parsed = ++cp;
4828d522f475Smrg
4829cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4830d522f475Smrg
4831cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4832b7c89284Ssnj
4833cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4834d522f475Smrg
4835cd3331d0Smrg		unparseputc(xw, '+');
4836cd3331d0Smrg		unparseputc(xw, 'r');
4837d522f475Smrg
4838cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4839cd3331d0Smrg		    if (cp == parsed)
4840cd3331d0Smrg			break;	/* no data found, error */
4841d522f475Smrg
4842cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4843cd3331d0Smrg			unparseputc(xw, *tmp);
4844d522f475Smrg
4845cd3331d0Smrg		    if (code >= 0) {
4846cd3331d0Smrg			unparseputc(xw, '=');
4847cd3331d0Smrg			screen->tc_query_code = code;
4848cd3331d0Smrg			screen->tc_query_fkey = fkey;
4849d522f475Smrg#if OPT_ISO_COLORS
4850cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4851cd3331d0Smrg			 * number of colors) */
4852cd3331d0Smrg			if (code == XK_COLORS) {
4853d4fba8b9Smrg			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
4854d4fba8b9Smrg			} else
4855d4fba8b9Smrg#if OPT_DIRECT_COLOR
4856d4fba8b9Smrg			if (code == XK_RGB) {
4857d4fba8b9Smrg			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
4858d4fba8b9Smrg				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
4859d4fba8b9Smrg				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
4860d4fba8b9Smrg				    unparseputn(xw, xw->rgb_widths[0]);
4861d4fba8b9Smrg				} else {
4862d4fba8b9Smrg				    char temp[1024];
4863d4fba8b9Smrg				    sprintf(temp, "%d/%d/%d",
4864d4fba8b9Smrg					    xw->rgb_widths[0],
4865d4fba8b9Smrg					    xw->rgb_widths[1],
4866d4fba8b9Smrg					    xw->rgb_widths[2]);
4867d4fba8b9Smrg				    unparseputs(xw, temp);
4868d4fba8b9Smrg				}
4869d4fba8b9Smrg			    } else {
4870d4fba8b9Smrg				unparseputs(xw, "-1");
4871d4fba8b9Smrg			    }
4872cd3331d0Smrg			} else
4873d4fba8b9Smrg#endif
4874cd3331d0Smrg#endif
4875cd3331d0Smrg			if (code == XK_TCAPNAME) {
4876c219fbebSmrg			    unparseputs(xw, resource.term_name);
4877cd3331d0Smrg			} else {
4878cd3331d0Smrg			    XKeyEvent event;
4879d4fba8b9Smrg			    memset(&event, 0, sizeof(event));
4880cd3331d0Smrg			    event.state = state;
4881cd3331d0Smrg			    Input(xw, &event, False);
4882cd3331d0Smrg			}
4883cd3331d0Smrg			screen->tc_query_code = -1;
4884cd3331d0Smrg		    } else {
4885cd3331d0Smrg			break;	/* no match found, error */
4886d522f475Smrg		    }
4887d522f475Smrg
4888d522f475Smrg		    cp = parsed;
4889cd3331d0Smrg		    if (*parsed == ';') {
4890cd3331d0Smrg			unparseputc(xw, *parsed++);
4891cd3331d0Smrg			cp = parsed;
4892cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4893cd3331d0Smrg		    }
4894d522f475Smrg		}
4895cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4896d522f475Smrg	    }
4897cd3331d0Smrg	    break;
4898d4fba8b9Smrg#endif
4899d4fba8b9Smrg#if OPT_XRES_QUERY
4900d4fba8b9Smrg	case 'Q':
4901d4fba8b9Smrg	    ++cp;
4902d4fba8b9Smrg	    if (AllowXResOps(xw)) {
4903d4fba8b9Smrg		Boolean first = True;
4904d4fba8b9Smrg		while (*cp != '\0') {
4905d4fba8b9Smrg		    const char *parsed = 0;
4906d4fba8b9Smrg		    const char *tmp;
4907d4fba8b9Smrg		    char *name = x_decode_hex(cp, &parsed);
4908d4fba8b9Smrg		    char *value;
4909d4fba8b9Smrg		    char *result;
4910d4fba8b9Smrg		    if (cp == parsed || name == NULL) {
4911d4fba8b9Smrg			free(name);
4912d4fba8b9Smrg			break;	/* no data found, error */
4913d4fba8b9Smrg		    }
4914d4fba8b9Smrg		    TRACE(("query-feature '%s'\n", name));
4915d4fba8b9Smrg		    if ((value = vt100ResourceToString(xw, name)) != 0) {
4916d4fba8b9Smrg			okay = True;	/* valid */
4917d4fba8b9Smrg		    } else {
4918d4fba8b9Smrg			okay = False;	/* invalid */
4919d4fba8b9Smrg		    }
4920d4fba8b9Smrg		    if (first) {
4921d4fba8b9Smrg			unparseputc1(xw, ANSI_DCS);
4922d4fba8b9Smrg			unparseputc(xw, okay ? '1' : '0');
4923d4fba8b9Smrg			unparseputc(xw, '+');
4924d4fba8b9Smrg			unparseputc(xw, 'R');
4925d4fba8b9Smrg			first = False;
4926d4fba8b9Smrg		    }
4927d4fba8b9Smrg
4928d4fba8b9Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4929d4fba8b9Smrg			unparseputc(xw, *tmp);
4930d4fba8b9Smrg
4931d4fba8b9Smrg		    if (value != 0) {
4932d4fba8b9Smrg			unparseputc1(xw, '=');
4933d4fba8b9Smrg			result = x_encode_hex(value);
4934d4fba8b9Smrg			unparseputs(xw, result);
4935d4fba8b9Smrg		    } else {
4936d4fba8b9Smrg			result = NULL;
4937d4fba8b9Smrg		    }
4938d4fba8b9Smrg
4939d4fba8b9Smrg		    free(name);
4940d4fba8b9Smrg		    free(value);
4941d4fba8b9Smrg		    free(result);
4942d4fba8b9Smrg
4943d4fba8b9Smrg		    cp = parsed;
4944d4fba8b9Smrg		    if (*parsed == ';') {
4945d4fba8b9Smrg			unparseputc(xw, *parsed++);
4946d4fba8b9Smrg			cp = parsed;
4947d4fba8b9Smrg		    }
4948d4fba8b9Smrg		}
4949d4fba8b9Smrg		if (!first)
4950d4fba8b9Smrg		    unparseputc1(xw, ANSI_ST);
4951d4fba8b9Smrg	    }
4952d4fba8b9Smrg	    break;
4953d4fba8b9Smrg#endif
4954d522f475Smrg	}
4955d522f475Smrg	break;
4956d4fba8b9Smrg#if OPT_DEC_RECTOPS
4957d4fba8b9Smrg    case '1':
4958d4fba8b9Smrg	/* FALLTHRU */
4959d4fba8b9Smrg    case '2':
4960d4fba8b9Smrg	if (*skip_params(cp) == '$') {
4961d4fba8b9Smrg	    psarg = *cp++;
4962d4fba8b9Smrg	    if ((*cp++ == '$')
4963d4fba8b9Smrg		&& (*cp++ == 't')
4964d4fba8b9Smrg		&& (screen->vtXX_level >= 3)) {
4965d4fba8b9Smrg		switch (psarg) {
4966d4fba8b9Smrg		case '1':
4967d4fba8b9Smrg		    TRACE(("DECRSPS (DECCIR)\n"));
4968d4fba8b9Smrg		    restore_DECCIR(xw, cp);
4969d4fba8b9Smrg		    break;
4970d4fba8b9Smrg		case '2':
4971d4fba8b9Smrg		    TRACE(("DECRSPS (DECTABSR)\n"));
4972d4fba8b9Smrg		    restore_DECTABSR(xw, cp);
4973d4fba8b9Smrg		    break;
4974d4fba8b9Smrg		}
4975d4fba8b9Smrg	    }
4976d4fba8b9Smrg	    break;
4977d4fba8b9Smrg	}
4978d522f475Smrg#endif
4979d4fba8b9Smrg	/* FALLTHRU */
4980d522f475Smrg    default:
4981d4fba8b9Smrg	if (optRegisGraphics(screen) ||
4982d4fba8b9Smrg	    optSixelGraphics(screen) ||
4983fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
49840d92cbfdSchristos	    parse_ansi_params(&params, &cp);
49850d92cbfdSchristos	    switch (params.a_final) {
4986d4fba8b9Smrg	    case 'p':		/* ReGIS */
49879a64e1c5Smrg#if OPT_REGIS_GRAPHICS
4988d4fba8b9Smrg		if (optRegisGraphics(screen)) {
4989fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4990fa3f02f3Smrg		}
49919a64e1c5Smrg#else
49929a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
49939a64e1c5Smrg#endif
4994fa3f02f3Smrg		break;
4995d4fba8b9Smrg	    case 'q':		/* sixel */
49969a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
4997d4fba8b9Smrg		if (optSixelGraphics(screen)) {
4998037a25ddSmrg		    (void) parse_sixel(xw, &params, cp);
4999fa3f02f3Smrg		}
50009a64e1c5Smrg#else
50019a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
5002fa3f02f3Smrg#endif
50039a64e1c5Smrg		break;
50040d92cbfdSchristos	    case '|':		/* DECUDK */
50059a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
50069a64e1c5Smrg		    if (params.a_param[0] == 0)
50079a64e1c5Smrg			reset_decudk(xw);
50089a64e1c5Smrg		    parse_decudk(xw, cp);
50099a64e1c5Smrg		}
50100d92cbfdSchristos		break;
501194644356Smrg	    case L_CURL:	/* DECDLD */
50129a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
50139a64e1c5Smrg		    parse_decdld(&params, cp);
50149a64e1c5Smrg		}
50150d92cbfdSchristos		break;
50160d92cbfdSchristos	    }
5017d522f475Smrg	}
5018d522f475Smrg	break;
5019d522f475Smrg    }
5020d522f475Smrg    unparse_end(xw);
5021d522f475Smrg}
5022d522f475Smrg
5023cb4a1343Smrg#if OPT_DEC_RECTOPS
5024cb4a1343Smrgenum {
5025cb4a1343Smrg    mdUnknown = 0,
5026cb4a1343Smrg    mdMaybeSet = 1,
5027cb4a1343Smrg    mdMaybeReset = 2,
5028cb4a1343Smrg    mdAlwaysSet = 3,
5029cb4a1343Smrg    mdAlwaysReset = 4
5030cb4a1343Smrg};
5031cb4a1343Smrg
5032cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
50333367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
5034cb4a1343Smrg
5035cb4a1343Smrg/*
5036cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
5037cb4a1343Smrg * 0 - not recognized
5038cb4a1343Smrg * 1 - set
5039cb4a1343Smrg * 2 - reset
5040cb4a1343Smrg * 3 - permanently set
5041cb4a1343Smrg * 4 - permanently reset
5042cb4a1343Smrg * Only one mode can be reported at a time.
5043cb4a1343Smrg */
5044cb4a1343Smrgvoid
5045d4fba8b9Smrgdo_ansi_rqm(XtermWidget xw, int nparams, int *params)
5046cb4a1343Smrg{
5047cb4a1343Smrg    ANSI reply;
5048cb4a1343Smrg    int count = 0;
5049cb4a1343Smrg
5050d4fba8b9Smrg    TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5051cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5052037a25ddSmrg
5053cb4a1343Smrg    if (nparams >= 1) {
5054d4fba8b9Smrg	int result = mdUnknown;
5055037a25ddSmrg
5056d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5057cb4a1343Smrg	switch (params[0]) {
5058cb4a1343Smrg	case 1:		/* GATM */
5059cb4a1343Smrg	    result = mdAlwaysReset;
5060cb4a1343Smrg	    break;
5061cb4a1343Smrg	case 2:
5062cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
5063cb4a1343Smrg	    break;
5064cb4a1343Smrg	case 3:		/* CRM */
5065cb4a1343Smrg	    result = mdMaybeReset;
5066cb4a1343Smrg	    break;
5067cb4a1343Smrg	case 4:
5068cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
5069cb4a1343Smrg	    break;
5070cb4a1343Smrg	case 5:		/* SRTM */
5071cb4a1343Smrg	case 7:		/* VEM */
5072cb4a1343Smrg	case 10:		/* HEM */
5073cb4a1343Smrg	case 11:		/* PUM */
5074cb4a1343Smrg	    result = mdAlwaysReset;
5075cb4a1343Smrg	    break;
5076cb4a1343Smrg	case 12:
5077cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
5078cb4a1343Smrg	    break;
5079cb4a1343Smrg	case 13:		/* FEAM */
5080cb4a1343Smrg	case 14:		/* FETM */
5081cb4a1343Smrg	case 15:		/* MATM */
5082cb4a1343Smrg	case 16:		/* TTM */
5083cb4a1343Smrg	case 17:		/* SATM */
5084cb4a1343Smrg	case 18:		/* TSM */
5085cb4a1343Smrg	case 19:		/* EBM */
5086cb4a1343Smrg	    result = mdAlwaysReset;
5087cb4a1343Smrg	    break;
5088cb4a1343Smrg	case 20:
5089cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
5090cb4a1343Smrg	    break;
5091cb4a1343Smrg	}
5092cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5093cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5094cb4a1343Smrg    }
5095cb4a1343Smrg    reply.a_type = ANSI_CSI;
5096cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5097cb4a1343Smrg    reply.a_inters = '$';
5098cb4a1343Smrg    reply.a_final = 'y';
5099cb4a1343Smrg    unparseseq(xw, &reply);
5100cb4a1343Smrg}
5101cb4a1343Smrg
5102cb4a1343Smrgvoid
5103d4fba8b9Smrgdo_dec_rqm(XtermWidget xw, int nparams, int *params)
5104cb4a1343Smrg{
5105cb4a1343Smrg    ANSI reply;
5106cb4a1343Smrg    int count = 0;
5107cb4a1343Smrg
5108d4fba8b9Smrg    TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5109cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5110037a25ddSmrg
5111cb4a1343Smrg    if (nparams >= 1) {
5112cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
5113d4fba8b9Smrg	int result = mdUnknown;
5114cb4a1343Smrg
5115d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5116d4fba8b9Smrg	switch ((DECSET_codes) params[0]) {
5117fa3f02f3Smrg	case srm_DECCKM:
5118cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5119cb4a1343Smrg	    break;
5120fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
5121cb4a1343Smrg#if OPT_VT52_MODE
51223367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
5123cb4a1343Smrg#else
5124cb4a1343Smrg	    result = mdMaybeSet;
5125cb4a1343Smrg#endif
5126cb4a1343Smrg	    break;
5127fa3f02f3Smrg	case srm_DECCOLM:
5128cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
5129cb4a1343Smrg	    break;
5130fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
5131cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5132cb4a1343Smrg	    break;
5133fa3f02f3Smrg	case srm_DECSCNM:
5134cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5135cb4a1343Smrg	    break;
5136fa3f02f3Smrg	case srm_DECOM:
5137cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
5138cb4a1343Smrg	    break;
5139fa3f02f3Smrg	case srm_DECAWM:
5140cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
5141cb4a1343Smrg	    break;
5142fa3f02f3Smrg	case srm_DECARM:
5143cb4a1343Smrg	    result = mdAlwaysReset;
5144cb4a1343Smrg	    break;
5145fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
5146cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5147cb4a1343Smrg	    break;
5148cb4a1343Smrg#if OPT_TOOLBAR
5149fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
5150cb4a1343Smrg	    result = MdBool(resource.toolBar);
5151cb4a1343Smrg	    break;
5152cb4a1343Smrg#endif
5153cb4a1343Smrg#if OPT_BLINK_CURS
5154d4fba8b9Smrg	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5155d4fba8b9Smrg	    result = MdBool(screen->cursor_blink_esc);
5156d4fba8b9Smrg	    break;
5157d4fba8b9Smrg	case srm_CURSOR_BLINK_OPS:
5158d4fba8b9Smrg	    switch (screen->cursor_blink) {
5159d4fba8b9Smrg	    case cbTrue:
5160d4fba8b9Smrg		result = mdMaybeSet;
5161d4fba8b9Smrg		break;
5162d4fba8b9Smrg	    case cbFalse:
5163d4fba8b9Smrg		result = mdMaybeReset;
5164d4fba8b9Smrg		break;
5165d4fba8b9Smrg	    case cbAlways:
5166d4fba8b9Smrg		result = mdAlwaysSet;
5167d4fba8b9Smrg		break;
5168d4fba8b9Smrg	    case cbLAST:
5169d4fba8b9Smrg		/* FALLTHRU */
5170d4fba8b9Smrg	    case cbNever:
5171d4fba8b9Smrg		result = mdAlwaysReset;
5172d4fba8b9Smrg		break;
5173d4fba8b9Smrg	    }
5174d4fba8b9Smrg	    break;
5175d4fba8b9Smrg	case srm_XOR_CURSOR_BLINKS:
5176d4fba8b9Smrg	    result = (screen->cursor_blink_xor
5177d4fba8b9Smrg		      ? mdAlwaysSet
5178d4fba8b9Smrg		      : mdAlwaysReset);
5179cb4a1343Smrg	    break;
5180cb4a1343Smrg#endif
5181fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
5182712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
5183cb4a1343Smrg	    break;
5184fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
5185712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
5186cb4a1343Smrg	    break;
5187fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5188cb4a1343Smrg	    result = MdBool(screen->cursor_set);
5189cb4a1343Smrg	    break;
5190fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
5191cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5192cb4a1343Smrg	    break;
5193cb4a1343Smrg#if OPT_SHIFT_FONTS
5194fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
5195cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
5196cb4a1343Smrg	    break;
5197cb4a1343Smrg#endif
5198cb4a1343Smrg#if OPT_TEK4014
5199fa3f02f3Smrg	case srm_DECTEK:
5200cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
5201cb4a1343Smrg	    break;
5202cb4a1343Smrg#endif
5203fa3f02f3Smrg	case srm_132COLS:
5204cb4a1343Smrg	    result = MdBool(screen->c132);
5205cb4a1343Smrg	    break;
5206fa3f02f3Smrg	case srm_CURSES_HACK:
5207cb4a1343Smrg	    result = MdBool(screen->curses);
5208cb4a1343Smrg	    break;
5209fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
5210d4fba8b9Smrg	    if (screen->vtXX_level >= 2) {
5211d4fba8b9Smrg		result = MdFlag(xw->flags, NATIONAL);
5212d4fba8b9Smrg	    } else {
5213d4fba8b9Smrg		result = 0;
5214d4fba8b9Smrg	    }
5215cb4a1343Smrg	    break;
5216fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
5217cb4a1343Smrg	    result = MdBool(screen->marginbell);
5218cb4a1343Smrg	    break;
5219d4fba8b9Smrg#if OPT_PRINT_GRAPHICS
5220d4fba8b9Smrg	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5221d4fba8b9Smrg	    result = MdBool(screen->graphics_expanded_print_mode);
5222d4fba8b9Smrg	    break;
5223d4fba8b9Smrg#endif
5224fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
5225d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5226d4fba8b9Smrg		result = MdFlag(xw->flags, REVERSEWRAP);
5227cb4a1343Smrg	    break;
5228d4fba8b9Smrg#if defined(ALLOWLOGGING)
5229fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
5230d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5231d4fba8b9Smrg#if defined(ALLOWLOGFILEONOFF)
5232d4fba8b9Smrg		result = MdBool(screen->logging);
5233d4fba8b9Smrg#else
5234d4fba8b9Smrg		result = ((MdBool(screen->logging) == mdMaybeSet)
5235d4fba8b9Smrg			  ? mdAlwaysSet
5236d4fba8b9Smrg			  : mdAlwaysReset);
5237d4fba8b9Smrg#endif
5238d4fba8b9Smrg	    break;
5239d4fba8b9Smrg#elif OPT_PRINT_GRAPHICS
5240d4fba8b9Smrg	case srm_DECGPBM:	/* Graphics Print Background Mode */
5241d4fba8b9Smrg	    result = MdBool(screen->graphics_print_background_mode);
5242cb4a1343Smrg	    break;
5243cb4a1343Smrg#endif
5244fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5245cb4a1343Smrg	    /* FALLTHRU */
5246fa3f02f3Smrg	case srm_OPT_ALTBUF:
5247cb4a1343Smrg	    result = MdBool(screen->whichBuf);
5248cb4a1343Smrg	    break;
5249d4fba8b9Smrg	case srm_ALTBUF:
5250d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5251d4fba8b9Smrg		result = MdBool(screen->whichBuf);
5252d4fba8b9Smrg	    break;
5253fa3f02f3Smrg	case srm_DECNKM:
5254cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5255cb4a1343Smrg	    break;
5256fa3f02f3Smrg	case srm_DECBKM:
5257cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5258cb4a1343Smrg	    break;
5259fa3f02f3Smrg	case srm_DECLRMM:
5260d4fba8b9Smrg	    if (screen->vtXX_level >= 4) {	/* VT420 */
5261d4fba8b9Smrg		result = MdFlag(xw->flags, LEFT_RIGHT);
5262d4fba8b9Smrg	    } else {
5263d4fba8b9Smrg		result = 0;
5264d4fba8b9Smrg	    }
52653367019cSmrg	    break;
5266fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
5267fa3f02f3Smrg	case srm_DECSDM:
5268fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5269fa3f02f3Smrg	    break;
5270fa3f02f3Smrg#endif
5271fa3f02f3Smrg	case srm_DECNCSM:
5272d4fba8b9Smrg	    if (screen->vtXX_level >= 5) {	/* VT510 */
5273d4fba8b9Smrg		result = MdFlag(xw->flags, NOCLEAR_COLM);
5274d4fba8b9Smrg	    } else {
5275d4fba8b9Smrg		result = 0;
5276d4fba8b9Smrg	    }
52773367019cSmrg	    break;
5278d4fba8b9Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5279cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5280cb4a1343Smrg	    break;
5281fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5282cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5283cb4a1343Smrg	    break;
5284fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
5285cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5286cb4a1343Smrg	    break;
5287fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
5288cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5289cb4a1343Smrg	    break;
5290cb4a1343Smrg#if OPT_FOCUS_EVENT
5291fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
5292cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
5293cb4a1343Smrg	    break;
5294cb4a1343Smrg#endif
5295fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
52963367019cSmrg	    /* FALLTHRU */
5297fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
52983367019cSmrg	    /* FALLTHRU */
5299fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
5300d4fba8b9Smrg	    /* FALLTHRU */
5301d4fba8b9Smrg	case srm_PIXEL_POSITION_MOUSE:
53023367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
53033367019cSmrg	    break;
5304fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
53053367019cSmrg	    result = MdBool(screen->alternateScroll);
5306cb4a1343Smrg	    break;
5307fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
5308cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
5309cb4a1343Smrg	    break;
5310fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5311cb4a1343Smrg	    result = MdBool(screen->scrollkey);
5312cb4a1343Smrg	    break;
5313fa3f02f3Smrg	case srm_EIGHT_BIT_META:
53143367019cSmrg	    result = MdBool(screen->eight_bit_meta);
5315cb4a1343Smrg	    break;
5316cb4a1343Smrg#if OPT_NUM_LOCK
5317fa3f02f3Smrg	case srm_REAL_NUMLOCK:
5318cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
5319cb4a1343Smrg	    break;
5320fa3f02f3Smrg	case srm_META_SENDS_ESC:
5321cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
5322cb4a1343Smrg	    break;
5323cb4a1343Smrg#endif
5324fa3f02f3Smrg	case srm_DELETE_IS_DEL:
5325d4fba8b9Smrg	    result = MdBool(xtermDeleteIsDEL(xw));
5326cb4a1343Smrg	    break;
5327cb4a1343Smrg#if OPT_NUM_LOCK
5328fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
5329cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
5330cb4a1343Smrg	    break;
5331cb4a1343Smrg#endif
5332fa3f02f3Smrg	case srm_KEEP_SELECTION:
5333cb4a1343Smrg	    result = MdBool(screen->keepSelection);
5334cb4a1343Smrg	    break;
5335fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
5336cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
5337cb4a1343Smrg	    break;
5338fa3f02f3Smrg	case srm_BELL_IS_URGENT:
5339cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
5340cb4a1343Smrg	    break;
5341fa3f02f3Smrg	case srm_POP_ON_BELL:
5342cb4a1343Smrg	    result = MdBool(screen->poponbell);
5343cb4a1343Smrg	    break;
5344d4fba8b9Smrg	case srm_KEEP_CLIPBOARD:
5345d4fba8b9Smrg	    result = MdBool(screen->keepClipboard);
5346d4fba8b9Smrg	    break;
5347d4fba8b9Smrg	case srm_ALLOW_ALTBUF:
5348d4fba8b9Smrg	    result = MdBool(xw->misc.titeInhibit);
5349d4fba8b9Smrg	    break;
5350d4fba8b9Smrg	case srm_SAVE_CURSOR:
5351cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
5352cb4a1343Smrg	    break;
5353cb4a1343Smrg#if OPT_TCAP_FKEYS
5354fa3f02f3Smrg	case srm_TCAP_FKEYS:
5355cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5356cb4a1343Smrg	    break;
5357cb4a1343Smrg#endif
5358cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
5359fa3f02f3Smrg	case srm_SUN_FKEYS:
5360cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5361cb4a1343Smrg	    break;
5362cb4a1343Smrg#endif
5363cb4a1343Smrg#if OPT_HP_FUNC_KEYS
5364fa3f02f3Smrg	case srm_HP_FKEYS:
5365cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5366cb4a1343Smrg	    break;
5367cb4a1343Smrg#endif
5368cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
5369fa3f02f3Smrg	case srm_SCO_FKEYS:
5370cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5371cb4a1343Smrg	    break;
5372cb4a1343Smrg#endif
5373fa3f02f3Smrg	case srm_LEGACY_FKEYS:
5374cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5375cb4a1343Smrg	    break;
5376cb4a1343Smrg#if OPT_SUNPC_KBD
5377fa3f02f3Smrg	case srm_VT220_FKEYS:
5378cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5379cb4a1343Smrg	    break;
5380cb4a1343Smrg#endif
5381d4fba8b9Smrg#if OPT_PASTE64 || OPT_READLINE
5382d4fba8b9Smrg	case srm_PASTE_IN_BRACKET:
5383d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5384d4fba8b9Smrg	    break;
5385d4fba8b9Smrg#endif
5386cb4a1343Smrg#if OPT_READLINE
5387fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
5388d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5389cb4a1343Smrg	    break;
5390fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
5391d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5392cb4a1343Smrg	    break;
5393fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
5394d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5395cb4a1343Smrg	    break;
5396fa3f02f3Smrg	case srm_PASTE_QUOTE:
5397d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5398cb4a1343Smrg	    break;
5399fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
5400d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5401cb4a1343Smrg	    break;
5402cb4a1343Smrg#endif /* OPT_READLINE */
5403d4fba8b9Smrg#if OPT_GRAPHICS
54049a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
54059a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
54069a64e1c5Smrg	    break;
54079a64e1c5Smrg#endif
54089a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
54099a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
54109a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
54119a64e1c5Smrg	    break;
54129a64e1c5Smrg#endif
54139a64e1c5Smrg	default:
54149a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
54159a64e1c5Smrg		   params[0]));
5416cb4a1343Smrg	}
5417cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5418cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5419d4fba8b9Smrg	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5420cb4a1343Smrg    }
5421cb4a1343Smrg    reply.a_type = ANSI_CSI;
5422cb4a1343Smrg    reply.a_pintro = '?';
5423cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5424cb4a1343Smrg    reply.a_inters = '$';
5425cb4a1343Smrg    reply.a_final = 'y';
5426cb4a1343Smrg    unparseseq(xw, &reply);
5427cb4a1343Smrg}
5428cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
5429cb4a1343Smrg
5430d522f475Smrgchar *
54319a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
5432d522f475Smrg{
5433d4fba8b9Smrg    char *result = NULL;
5434d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
54359a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
5436d4fba8b9Smrg	result = xw->work.user_keys[keycode].str;
5437d4fba8b9Smrg	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5438d4fba8b9Smrg    } else {
5439d4fba8b9Smrg	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5440d4fba8b9Smrg    }
5441d4fba8b9Smrg    return result;
5442d4fba8b9Smrg}
5443d4fba8b9Smrg
5444d4fba8b9Smrg#if OPT_REPORT_ICONS
5445d4fba8b9Smrgvoid
5446d4fba8b9Smrgreport_icons(const char *fmt, ...)
5447d4fba8b9Smrg{
5448d4fba8b9Smrg    if (resource.reportIcons) {
5449d4fba8b9Smrg	va_list ap;
5450d4fba8b9Smrg	va_start(ap, fmt);
5451d4fba8b9Smrg	vfprintf(stdout, fmt, ap);
5452d4fba8b9Smrg	va_end(ap);
5453d4fba8b9Smrg#if OPT_TRACE
5454d4fba8b9Smrg	va_start(ap, fmt);
5455d4fba8b9Smrg	TraceVA(fmt, ap);
5456d4fba8b9Smrg	va_end(ap);
5457d4fba8b9Smrg#endif
5458d522f475Smrg    }
5459d522f475Smrg}
5460d4fba8b9Smrg#endif
5461d522f475Smrg
54623367019cSmrg#ifdef HAVE_LIBXPM
54633367019cSmrg
54643367019cSmrg#ifndef PIXMAP_ROOTDIR
54653367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
54663367019cSmrg#endif
54673367019cSmrg
54683367019cSmrgtypedef struct {
54693367019cSmrg    const char *name;
54703367019cSmrg    const char *const *data;
54713367019cSmrg} XPM_DATA;
54723367019cSmrg
54733367019cSmrgstatic char *
5474d4fba8b9Smrgx_find_icon(char **work, int *state, const char *filename, const char *suffix)
54753367019cSmrg{
54763367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
54773367019cSmrg    const char *larger = "_48x48";
54783367019cSmrg    char *result = 0;
54793367019cSmrg
54803367019cSmrg    if (*state >= 0) {
54813367019cSmrg	if ((*state & 1) == 0)
54823367019cSmrg	    suffix = "";
54833367019cSmrg	if ((*state & 2) == 0)
54843367019cSmrg	    larger = "";
54853367019cSmrg	if ((*state & 4) == 0) {
54863367019cSmrg	    prefix = "";
54873367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
54883367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
54893367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
54903367019cSmrg	    *state = -1;
54913367019cSmrg	} else if (*state >= 8) {
54923367019cSmrg	    *state = -1;
54933367019cSmrg	}
54943367019cSmrg    }
54953367019cSmrg
54963367019cSmrg    if (*state >= 0) {
5497037a25ddSmrg	size_t length;
5498037a25ddSmrg
5499d4fba8b9Smrg	FreeAndNull(*work);
55003367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
55013367019cSmrg	    strlen(suffix);
55023367019cSmrg	if ((result = malloc(length)) != 0) {
55033367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
55043367019cSmrg	    *work = result;
55053367019cSmrg	}
55063367019cSmrg	*state += 1;
55073367019cSmrg    }
5508d4fba8b9Smrg    TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
55093367019cSmrg    return result;
55103367019cSmrg}
55113367019cSmrg
55123367019cSmrg#if OPT_BUILTIN_XPMS
5513d4fba8b9Smrg
55143367019cSmrgstatic const XPM_DATA *
5515d4fba8b9Smrgbuilt_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
55163367019cSmrg{
55173367019cSmrg    const XPM_DATA *result = 0;
55183367019cSmrg    if (!IsEmpty(find)) {
55193367019cSmrg	Cardinal n;
55203367019cSmrg	for (n = 0; n < length; ++n) {
55213367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
55223367019cSmrg		result = table + n;
5523d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[n].name));
55243367019cSmrg		break;
55253367019cSmrg	    }
55263367019cSmrg	}
55273367019cSmrg
55283367019cSmrg	/*
55293367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
55303367019cSmrg	 * which are all _HHxWW format.
55313367019cSmrg	 */
55323367019cSmrg	if (result == 0) {
55333367019cSmrg	    const char *base = table[0].name;
55343367019cSmrg	    const char *last = strchr(base, '_');
55353367019cSmrg	    if (last != 0
55363367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
55373367019cSmrg		result = table + length - 1;
5538d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[0].name));
55393367019cSmrg	    }
55403367019cSmrg	}
55413367019cSmrg    }
55423367019cSmrg    return result;
55433367019cSmrg}
5544d4fba8b9Smrg#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
55453367019cSmrg#endif /* OPT_BUILTIN_XPMS */
55463367019cSmrg
55473367019cSmrgtypedef enum {
55483367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
55493367019cSmrg    ,eHintNone
55503367019cSmrg    ,eHintSearch
55513367019cSmrg} ICON_HINT;
55523367019cSmrg#endif /* HAVE_LIBXPM */
55533367019cSmrg
55543367019cSmrgint
55553367019cSmrggetVisualDepth(XtermWidget xw)
55563367019cSmrg{
55573367019cSmrg    int result = 0;
55583367019cSmrg
5559fa3f02f3Smrg    if (getVisualInfo(xw)) {
5560fa3f02f3Smrg	result = xw->visInfo->depth;
55613367019cSmrg    }
55623367019cSmrg    return result;
55633367019cSmrg}
55643367019cSmrg
55653367019cSmrg/*
55663367019cSmrg * WM_ICON_SIZE should be honored if possible.
55673367019cSmrg */
55683367019cSmrgvoid
5569d4fba8b9SmrgxtermLoadIcon(XtermWidget xw, const char *icon_hint)
55703367019cSmrg{
55713367019cSmrg#ifdef HAVE_LIBXPM
55723367019cSmrg    Display *dpy = XtDisplay(xw);
55733367019cSmrg    Pixmap myIcon = 0;
55743367019cSmrg    Pixmap myMask = 0;
55753367019cSmrg    char *workname = 0;
5576d4fba8b9Smrg    ICON_HINT hint = eHintDefault;
5577fa3f02f3Smrg#include <builtin_icons.h>
55783367019cSmrg
5579d4fba8b9Smrg    ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5580d4fba8b9Smrg    if (!IsEmpty(icon_hint)) {
5581d4fba8b9Smrg	if (!x_strcasecmp(icon_hint, "none")) {
5582d4fba8b9Smrg	    hint = eHintNone;
5583d4fba8b9Smrg	} else {
5584d4fba8b9Smrg	    hint = eHintSearch;
5585d4fba8b9Smrg	}
5586d4fba8b9Smrg    }
55873367019cSmrg
55883367019cSmrg    if (hint == eHintSearch) {
55893367019cSmrg	int state = 0;
5590d4fba8b9Smrg	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) {
55913367019cSmrg	    Pixmap resIcon = 0;
55923367019cSmrg	    Pixmap shapemask = 0;
55933367019cSmrg	    XpmAttributes attributes;
5594d4fba8b9Smrg	    struct stat sb;
55953367019cSmrg
55963367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
55973367019cSmrg	    attributes.valuemask = XpmDepth;
55983367019cSmrg
5599d4fba8b9Smrg	    if (IsEmpty(workname)
5600d4fba8b9Smrg		|| lstat(workname, &sb) != 0
5601d4fba8b9Smrg		|| !S_ISREG(sb.st_mode)) {
5602d4fba8b9Smrg		TRACE(("...failure (no such file)\n"));
5603d4fba8b9Smrg	    } else {
5604d4fba8b9Smrg		int rc = XpmReadFileToPixmap(dpy,
5605d4fba8b9Smrg					     DefaultRootWindow(dpy),
5606d4fba8b9Smrg					     workname,
5607d4fba8b9Smrg					     &resIcon,
5608d4fba8b9Smrg					     &shapemask,
5609d4fba8b9Smrg					     &attributes);
5610d4fba8b9Smrg		if (rc == XpmSuccess) {
5611d4fba8b9Smrg		    myIcon = resIcon;
5612d4fba8b9Smrg		    myMask = shapemask;
5613d4fba8b9Smrg		    TRACE(("...success\n"));
5614d4fba8b9Smrg		    ReportIcons(("found/loaded icon-file %s\n", workname));
5615d4fba8b9Smrg		    break;
5616d4fba8b9Smrg		} else {
5617d4fba8b9Smrg		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5618d4fba8b9Smrg		}
56193367019cSmrg	    }
56203367019cSmrg	}
56213367019cSmrg    }
56223367019cSmrg
56233367019cSmrg    /*
56243367019cSmrg     * If no external file was found, look for the name in the built-in table.
56253367019cSmrg     * If that fails, just use the biggest mini-icon.
56263367019cSmrg     */
56273367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
56283367019cSmrg	char **data;
56293367019cSmrg#if OPT_BUILTIN_XPMS
56303367019cSmrg	const XPM_DATA *myData = 0;
5631d4fba8b9Smrg	myData = BuiltInXPM(mini_xterm_xpms);
56323367019cSmrg	if (myData == 0)
5633d4fba8b9Smrg	    myData = BuiltInXPM(filled_xterm_xpms);
56343367019cSmrg	if (myData == 0)
5635d4fba8b9Smrg	    myData = BuiltInXPM(xterm_color_xpms);
56363367019cSmrg	if (myData == 0)
5637d4fba8b9Smrg	    myData = BuiltInXPM(xterm_xpms);
56383367019cSmrg	if (myData == 0)
56393367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
564094644356Smrg	data = (char **) myData->data;
56413367019cSmrg#else
56423367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
56433367019cSmrg#endif
56443367019cSmrg	if (XpmCreatePixmapFromData(dpy,
56453367019cSmrg				    DefaultRootWindow(dpy),
56463367019cSmrg				    data,
5647d4fba8b9Smrg				    &myIcon, &myMask, 0) == 0) {
5648d4fba8b9Smrg	    ReportIcons(("loaded built-in pixmap icon\n"));
5649d4fba8b9Smrg	} else {
56503367019cSmrg	    myIcon = 0;
56513367019cSmrg	    myMask = 0;
56523367019cSmrg	}
56533367019cSmrg    }
56543367019cSmrg
56553367019cSmrg    if (myIcon != 0) {
56563367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
56573367019cSmrg	if (!hints)
56583367019cSmrg	    hints = XAllocWMHints();
56593367019cSmrg
56603367019cSmrg	if (hints) {
56613367019cSmrg	    hints->flags |= IconPixmapHint;
56623367019cSmrg	    hints->icon_pixmap = myIcon;
56633367019cSmrg	    if (myMask) {
56643367019cSmrg		hints->flags |= IconMaskHint;
56653367019cSmrg		hints->icon_mask = myMask;
56663367019cSmrg	    }
56673367019cSmrg
56683367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
56693367019cSmrg	    XFree(hints);
5670d4fba8b9Smrg	    ReportIcons(("updated window-manager hints\n"));
56713367019cSmrg	}
56723367019cSmrg    }
56733367019cSmrg
5674d4fba8b9Smrg    free(workname);
56753367019cSmrg
56763367019cSmrg#else
56773367019cSmrg    (void) xw;
5678d4fba8b9Smrg    (void) icon_hint;
56793367019cSmrg#endif
56803367019cSmrg}
56813367019cSmrg
56823367019cSmrgvoid
5683cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
5684d522f475Smrg{
5685d522f475Smrg    Arg args[1];
5686cd3331d0Smrg    Boolean changed = True;
5687d522f475Smrg    Widget w = CURRENT_EMU();
5688d522f475Smrg    Widget top = SHELL_OF(w);
5689d522f475Smrg
5690d4fba8b9Smrg    char *my_attr = NULL;
5691d4fba8b9Smrg    char *old_value = value;
5692d4fba8b9Smrg#if OPT_WIDE_CHARS
5693d4fba8b9Smrg    Boolean titleIsUTF8;
5694d4fba8b9Smrg#endif
5695d522f475Smrg
5696b7c89284Ssnj    if (!AllowTitleOps(xw))
5697d522f475Smrg	return;
5698d522f475Smrg
5699d4fba8b9Smrg    /*
5700d4fba8b9Smrg     * Ignore empty or too-long requests.
5701d4fba8b9Smrg     */
5702d4fba8b9Smrg    if (value == 0 || strlen(value) > 1000)
5703d4fba8b9Smrg	return;
5704d4fba8b9Smrg
5705cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
5706cd3331d0Smrg	const char *temp;
5707cd3331d0Smrg	char *test;
5708cd3331d0Smrg
5709d4fba8b9Smrg	/* this allocates a new string, if no error is detected */
5710cd3331d0Smrg	value = x_decode_hex(value, &temp);
5711d4fba8b9Smrg	if (value == 0 || *temp != '\0') {
57123367019cSmrg	    free(value);
5713cd3331d0Smrg	    return;
57143367019cSmrg	}
5715cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
5716cd3331d0Smrg	    if (CharOf(*test) < 32) {
5717cd3331d0Smrg		*test = '\0';
5718cd3331d0Smrg		break;
5719cd3331d0Smrg	    }
5720cd3331d0Smrg	}
5721cd3331d0Smrg    }
5722d4fba8b9Smrg#if OPT_WIDE_CHARS
5723d522f475Smrg    /*
5724d4fba8b9Smrg     * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
5725d4fba8b9Smrg     * the WM_NAME property, rather than doing this directly.  That relies on
5726d4fba8b9Smrg     * the application to tell it if the format should be something other than
5727d4fba8b9Smrg     * STRING, i.e., by setting the XtNtitleEncoding resource.
5728d4fba8b9Smrg     *
5729d4fba8b9Smrg     * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
5730d4fba8b9Smrg     * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
5731d4fba8b9Smrg     * added UTF8_STRING (the documentation for that was discarded by an Xorg
5732d4fba8b9Smrg     * developer, although the source-code provides this feature).
5733d4fba8b9Smrg     *
5734d4fba8b9Smrg     * Since X11R5, if the X11 library fails to store a text property as
5735d4fba8b9Smrg     * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
5736d4fba8b9Smrg     * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
5737d4fba8b9Smrg     * case, limit the resulting characters to the printable ISO-8859-1 set.
5738d522f475Smrg     */
5739d4fba8b9Smrg    titleIsUTF8 = isValidUTF8((Char *) value);
5740d4fba8b9Smrg    if (IsSetUtf8Title(xw) && titleIsUTF8) {
5741d4fba8b9Smrg	char *testc = malloc(strlen(value) + 1);
5742d4fba8b9Smrg	Char *nextc = (Char *) value;
5743d4fba8b9Smrg	Boolean ok8bit = True;
5744d522f475Smrg
5745d4fba8b9Smrg	if (testc != NULL) {
5746d4fba8b9Smrg	    /*
5747d4fba8b9Smrg	     * Check if the data fits in STRING.  Along the way, replace
5748d4fba8b9Smrg	     * control characters.
5749d4fba8b9Smrg	     */
5750d4fba8b9Smrg	    Char *lastc = (Char *) testc;
5751d4fba8b9Smrg	    while (*nextc != '\0') {
5752d4fba8b9Smrg		unsigned ch;
5753d4fba8b9Smrg		nextc = convertFromUTF8(nextc, &ch);
5754d4fba8b9Smrg		if (ch > 255) {
5755d4fba8b9Smrg		    ok8bit = False;
5756d4fba8b9Smrg		} else if (!IsLatin1(ch)) {
5757d4fba8b9Smrg		    ch = OnlyLatin1(ch);
5758d4fba8b9Smrg		}
5759d4fba8b9Smrg		*lastc++ = (Char) ch;
5760d4fba8b9Smrg	    }
5761d4fba8b9Smrg	    *lastc = '\0';
5762d4fba8b9Smrg	    if (ok8bit) {
5763d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
5764d4fba8b9Smrg		if (value != old_value)
5765d4fba8b9Smrg		    free(value);
5766d4fba8b9Smrg		value = testc;
5767d4fba8b9Smrg		titleIsUTF8 = False;
5768d4fba8b9Smrg	    } else {
5769d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
5770d4fba8b9Smrg		       "\t%s\n", value));
5771d4fba8b9Smrg		free(testc);
5772d4fba8b9Smrg		nextc = (Char *) value;
5773d4fba8b9Smrg		while (*nextc != '\0') {
5774d4fba8b9Smrg		    unsigned ch;
5775d4fba8b9Smrg		    Char *skip = convertFromUTF8(nextc, &ch);
5776d4fba8b9Smrg		    if (iswcntrl((wint_t) ch)) {
5777d4fba8b9Smrg			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
5778d4fba8b9Smrg		    }
5779d4fba8b9Smrg		    nextc = skip;
5780d4fba8b9Smrg		}
5781cd3331d0Smrg	    }
5782d522f475Smrg	}
5783d4fba8b9Smrg    } else
5784d4fba8b9Smrg#endif
5785d4fba8b9Smrg    {
5786d4fba8b9Smrg	Char *c1 = (Char *) value;
5787d4fba8b9Smrg
5788d4fba8b9Smrg	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
5789d4fba8b9Smrg	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
5790d4fba8b9Smrg	    *c1 = (Char) OnlyLatin1(*c1);
5791d4fba8b9Smrg	}
5792d4fba8b9Smrg    }
5793d4fba8b9Smrg
5794d4fba8b9Smrg    my_attr = x_strdup(attribute);
5795d4fba8b9Smrg
5796d4fba8b9Smrg    ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
5797d522f475Smrg
5798d522f475Smrg#if OPT_WIDE_CHARS
5799d4fba8b9Smrg    /*
5800d4fba8b9Smrg     * If we're running in UTF-8 mode, and have not been told that the
5801d4fba8b9Smrg     * title string is in UTF-8, it is likely that non-ASCII text in the
5802d4fba8b9Smrg     * string will be rejected because it is not printable in the current
5803d4fba8b9Smrg     * locale.  So we convert it to UTF-8, allowing the X library to
5804d4fba8b9Smrg     * convert it back.
5805d4fba8b9Smrg     */
5806d4fba8b9Smrg    TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
5807d4fba8b9Smrg    if (xtermEnvUTF8() && !titleIsUTF8) {
5808d4fba8b9Smrg	size_t limit = strlen(value);
5809d4fba8b9Smrg	Char *c1 = (Char *) value;
5810d4fba8b9Smrg	int n;
5811cd3331d0Smrg
5812d4fba8b9Smrg	for (n = 0; c1[n] != '\0'; ++n) {
5813d4fba8b9Smrg	    if (c1[n] > 127) {
5814d4fba8b9Smrg		Char *converted;
5815d4fba8b9Smrg		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
5816d4fba8b9Smrg		    Char *temp = converted;
5817d4fba8b9Smrg		    while (*c1 != 0) {
5818d4fba8b9Smrg			temp = convertToUTF8(temp, *c1++);
5819d522f475Smrg		    }
5820d4fba8b9Smrg		    *temp = 0;
5821d4fba8b9Smrg		    if (value != old_value)
5822d4fba8b9Smrg			free(value);
5823d4fba8b9Smrg		    value = (char *) converted;
5824d4fba8b9Smrg		    ReportIcons(("...converted{%s}\n", value));
5825d522f475Smrg		}
5826d4fba8b9Smrg		break;
5827d522f475Smrg	    }
5828d522f475Smrg	}
5829d4fba8b9Smrg    }
5830d522f475Smrg#endif
5831d522f475Smrg
5832d522f475Smrg#if OPT_SAME_NAME
5833d4fba8b9Smrg    /* If the attribute isn't going to change, then don't bother... */
5834d4fba8b9Smrg    if (resource.sameName) {
5835d4fba8b9Smrg	char *buf = 0;
5836d4fba8b9Smrg	XtSetArg(args[0], my_attr, &buf);
5837d4fba8b9Smrg	XtGetValues(top, args, 1);
5838d4fba8b9Smrg	TRACE(("...comparing{%s}\n", NonNull(buf)));
5839d4fba8b9Smrg	if (buf != 0 && strcmp(value, buf) == 0)
5840d4fba8b9Smrg	    changed = False;
5841d4fba8b9Smrg    }
5842d522f475Smrg#endif /* OPT_SAME_NAME */
5843d522f475Smrg
5844d4fba8b9Smrg    if (changed) {
5845d4fba8b9Smrg	ReportIcons(("...updating %s\n", my_attr));
5846d4fba8b9Smrg	ReportIcons(("...value is %s\n", value));
5847d4fba8b9Smrg	XtSetArg(args[0], my_attr, value);
5848d4fba8b9Smrg	XtSetValues(top, args, 1);
5849d4fba8b9Smrg    }
5850d522f475Smrg#if OPT_WIDE_CHARS
5851d4fba8b9Smrg    if (xtermEnvUTF8()) {
5852d4fba8b9Smrg	Display *dpy = XtDisplay(xw);
5853d4fba8b9Smrg	const char *propname = (!strcmp(my_attr, XtNtitle)
5854d4fba8b9Smrg				? "_NET_WM_NAME"
5855d4fba8b9Smrg				: "_NET_WM_ICON_NAME");
5856d4fba8b9Smrg	Atom my_atom = XInternAtom(dpy, propname, False);
5857d4fba8b9Smrg
5858d4fba8b9Smrg	if (my_atom != None) {
5859d4fba8b9Smrg	    changed = True;
5860d4fba8b9Smrg
5861d4fba8b9Smrg	    if (IsSetUtf8Title(xw)) {
5862d4fba8b9Smrg#if OPT_SAME_NAME
5863d4fba8b9Smrg		if (resource.sameName) {
5864d4fba8b9Smrg		    Atom actual_type;
5865d4fba8b9Smrg		    Atom requested_type = XA_UTF8_STRING(dpy);
5866d4fba8b9Smrg		    int actual_format = 0;
5867d4fba8b9Smrg		    long long_length = 1024;
5868d4fba8b9Smrg		    unsigned long nitems = 0;
5869d4fba8b9Smrg		    unsigned long bytes_after = 0;
5870d4fba8b9Smrg		    unsigned char *prop = 0;
5871d4fba8b9Smrg
5872d4fba8b9Smrg		    if (xtermGetWinProp(dpy,
5873d4fba8b9Smrg					VShellWindow(xw),
5874d4fba8b9Smrg					my_atom,
5875d4fba8b9Smrg					0L,
5876d4fba8b9Smrg					long_length,
5877d4fba8b9Smrg					requested_type,
5878d4fba8b9Smrg					&actual_type,
5879d4fba8b9Smrg					&actual_format,
5880d4fba8b9Smrg					&nitems,
5881d4fba8b9Smrg					&bytes_after,
588250027b5bSmrg					&prop)) {
588350027b5bSmrg			if (actual_type == requested_type
588450027b5bSmrg			    && actual_format == 8
588550027b5bSmrg			    && prop != 0
588650027b5bSmrg			    && nitems == strlen(value)
588750027b5bSmrg			    && memcmp(value, prop, nitems) == 0) {
588850027b5bSmrg			    changed = False;
588950027b5bSmrg			}
589050027b5bSmrg			XFree(prop);
5891cd3331d0Smrg		    }
5892cd3331d0Smrg		}
5893d4fba8b9Smrg#endif /* OPT_SAME_NAME */
5894d4fba8b9Smrg		if (changed) {
5895d4fba8b9Smrg		    ReportIcons(("...updating %s\n", propname));
5896d4fba8b9Smrg		    ReportIcons(("...value is %s\n", value));
5897d4fba8b9Smrg		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
5898d4fba8b9Smrg				    XA_UTF8_STRING(dpy), 8,
5899d4fba8b9Smrg				    PropModeReplace,
5900d4fba8b9Smrg				    (Char *) value,
5901d4fba8b9Smrg				    (int) strlen(value));
5902d4fba8b9Smrg		}
5903d4fba8b9Smrg	    } else {
5904d4fba8b9Smrg		ReportIcons(("...deleting %s\n", propname));
5905d4fba8b9Smrg		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5906d522f475Smrg	    }
5907d522f475Smrg	}
5908d522f475Smrg    }
5909d4fba8b9Smrg#endif
5910d4fba8b9Smrg    if (value != old_value) {
59113367019cSmrg	free(value);
59123367019cSmrg    }
59133367019cSmrg    free(my_attr);
59143367019cSmrg
5915cd3331d0Smrg    return;
5916d522f475Smrg}
5917d522f475Smrg
5918d522f475Smrgvoid
5919b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
5920d522f475Smrg{
5921cd3331d0Smrg    if (name == 0) {
59223367019cSmrg	name = emptyString;
59233367019cSmrg    }
59243367019cSmrg    if (!showZIconBeep(xw, name))
5925b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
5926d522f475Smrg}
5927d522f475Smrg
5928d522f475Smrgvoid
5929b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
5930d522f475Smrg{
5931b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
5932d522f475Smrg}
5933d522f475Smrg
5934712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
5935d522f475Smrg
5936d522f475Smrgvoid
5937d522f475SmrgChangeXprop(char *buf)
5938d522f475Smrg{
5939d522f475Smrg    Display *dpy = XtDisplay(toplevel);
5940d522f475Smrg    Window w = XtWindow(toplevel);
5941d522f475Smrg    XTextProperty text_prop;
5942d522f475Smrg    Atom aprop;
5943d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
5944d522f475Smrg
5945d522f475Smrg    if (pchEndPropName)
5946d522f475Smrg	*pchEndPropName = '\0';
5947d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
5948d522f475Smrg    if (pchEndPropName == NULL) {
5949d522f475Smrg	/* no "=value" given, so delete the property */
5950d522f475Smrg	XDeleteProperty(dpy, w, aprop);
5951d522f475Smrg    } else {
5952d522f475Smrg	text_prop.value = pchEndPropName + 1;
5953d522f475Smrg	text_prop.encoding = XA_STRING;
5954d522f475Smrg	text_prop.format = 8;
5955d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
5956d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
5957d522f475Smrg    }
5958d522f475Smrg}
5959d522f475Smrg
5960d522f475Smrg/***====================================================================***/
5961d522f475Smrg
5962d522f475Smrg/*
5963d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
5964d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
5965d522f475Smrg */
5966d522f475Smrgvoid
59679a64e1c5SmrgReverseOldColors(XtermWidget xw)
5968d522f475Smrg{
59699a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
5970d522f475Smrg    Pixel tmpPix;
5971d522f475Smrg    char *tmpName;
5972d522f475Smrg
5973d522f475Smrg    if (pOld) {
5974d4fba8b9Smrg	/* change text cursor, if necessary */
5975d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
5976d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
5977d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
59789a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
5979d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
5980d522f475Smrg	    }
5981d522f475Smrg	    if (pOld->names[TEXT_BG]) {
5982d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
5983d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
5984d522f475Smrg		}
5985d522f475Smrg	    }
5986d522f475Smrg	}
5987d522f475Smrg
5988d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
5989d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
5990d522f475Smrg
5991d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
5992d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
5993d522f475Smrg
5994d522f475Smrg#if OPT_TEK4014
5995d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
5996d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
5997d522f475Smrg#endif
5998d4fba8b9Smrg	FreeMarkGCs(xw);
5999d522f475Smrg    }
6000d522f475Smrg    return;
6001d522f475Smrg}
6002d522f475Smrg
6003d522f475SmrgBool
6004d522f475SmrgAllocateTermColor(XtermWidget xw,
6005d522f475Smrg		  ScrnColors * pNew,
6006d522f475Smrg		  int ndx,
6007cd3331d0Smrg		  const char *name,
6008cd3331d0Smrg		  Bool always)
6009d522f475Smrg{
6010cd3331d0Smrg    Bool result = False;
6011d522f475Smrg
6012cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
6013cd3331d0Smrg	XColor def;
6014cd3331d0Smrg	char *newName;
6015cd3331d0Smrg
6016712a7ff4Smrg	result = True;
6017712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
6018712a7ff4Smrg	    def.pixel = xw->old_foreground;
6019712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
6020712a7ff4Smrg	    def.pixel = xw->old_background;
60213367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
6022712a7ff4Smrg	    result = False;
6023712a7ff4Smrg	}
6024712a7ff4Smrg
6025712a7ff4Smrg	if (result
6026cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
6027712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
6028cd3331d0Smrg		free(pNew->names[ndx]);
6029712a7ff4Smrg	    }
6030cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
6031cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
6032712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
6033712a7ff4Smrg		   ndx, newName, def.pixel));
6034cd3331d0Smrg	} else {
6035cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
6036712a7ff4Smrg	    result = False;
6037cd3331d0Smrg	}
6038cd3331d0Smrg    }
6039cd3331d0Smrg    return result;
6040d522f475Smrg}
6041d522f475Smrg/***====================================================================***/
6042d522f475Smrg
6043d522f475Smrg/* ARGSUSED */
6044d522f475Smrgvoid
6045cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
6046d522f475Smrg{
60473367019cSmrg    if_DEBUG({
60483367019cSmrg	xtermWarning(s, a);
60493367019cSmrg    });
6050d522f475Smrg}
6051d522f475Smrg
6052d522f475Smrgconst char *
6053d522f475SmrgSysErrorMsg(int code)
6054d522f475Smrg{
605594644356Smrg    static const char unknown[] = "unknown error";
6056d4fba8b9Smrg    const char *s = strerror(code);
6057d522f475Smrg    return s ? s : unknown;
6058d522f475Smrg}
6059d522f475Smrg
6060d522f475Smrgconst char *
6061d522f475SmrgSysReasonMsg(int code)
6062d522f475Smrg{
6063d522f475Smrg    /* *INDENT-OFF* */
6064d522f475Smrg    static const struct {
6065d522f475Smrg	int code;
6066d522f475Smrg	const char *name;
6067d522f475Smrg    } table[] = {
6068d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
6069d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
6070d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
6071d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
6072d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
6073d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
6074d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
6075d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
6076d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
6077d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
6078d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
6079d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
6080d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
6081d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
6082d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
6083d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
6084d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
6085d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
6086d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
6087d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
6088d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6089d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
6090d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
6091d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6092d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6093d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6094d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
6095d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6096d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6097d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
6098d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6099d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6100d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6101d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
6102d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6103d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6104d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6105d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6106d522f475Smrg    };
6107d522f475Smrg    /* *INDENT-ON* */
6108d522f475Smrg
6109d522f475Smrg    Cardinal n;
6110d522f475Smrg    const char *result = "?";
6111d522f475Smrg
6112d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
6113d522f475Smrg	if (code == table[n].code) {
6114d522f475Smrg	    result = table[n].name;
6115d522f475Smrg	    break;
6116d522f475Smrg	}
6117d522f475Smrg    }
6118d522f475Smrg    return result;
6119d522f475Smrg}
6120d522f475Smrg
6121d522f475Smrgvoid
6122d522f475SmrgSysError(int code)
6123d522f475Smrg{
6124d522f475Smrg    int oerrno = errno;
6125d522f475Smrg
6126c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6127d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6128d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6129d522f475Smrg
6130d522f475Smrg    Cleanup(code);
6131d522f475Smrg}
6132d522f475Smrg
6133d522f475Smrgvoid
61343367019cSmrgNormalExit(void)
6135d522f475Smrg{
6136d522f475Smrg    static Bool cleaning;
6137d522f475Smrg
6138d522f475Smrg    /*
6139d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
6140d522f475Smrg     */
61413367019cSmrg    if (cleaning) {
61423367019cSmrg	hold_screen = 0;
61433367019cSmrg	return;
61443367019cSmrg    }
6145d522f475Smrg
61463367019cSmrg    cleaning = True;
61473367019cSmrg    need_cleanup = False;
6148d522f475Smrg
61493367019cSmrg    if (hold_screen) {
61503367019cSmrg	hold_screen = 2;
61513367019cSmrg	while (hold_screen) {
6152d4fba8b9Smrg	    xtermFlushDbe(term);
6153d4fba8b9Smrg	    xevents(term);
6154d4fba8b9Smrg	    Sleep(EVENT_DELAY);
6155d522f475Smrg	}
61563367019cSmrg    }
6157d522f475Smrg#if OPT_SESSION_MGT
61583367019cSmrg    if (resource.sessionMgt) {
61593367019cSmrg	XtVaSetValues(toplevel,
61603367019cSmrg		      XtNjoinSession, False,
61613367019cSmrg		      (void *) 0);
6162d522f475Smrg    }
61633367019cSmrg#endif
61643367019cSmrg    Cleanup(0);
61653367019cSmrg}
61663367019cSmrg
6167d4fba8b9Smrg#if USE_DOUBLE_BUFFER
6168d4fba8b9Smrgvoid
6169d4fba8b9SmrgxtermFlushDbe(XtermWidget xw)
6170d4fba8b9Smrg{
6171d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
6172d4fba8b9Smrg    if (resource.buffered && screen->needSwap) {
6173d4fba8b9Smrg	XdbeSwapInfo swap;
6174d4fba8b9Smrg	swap.swap_window = VWindow(screen);
6175d4fba8b9Smrg	swap.swap_action = XdbeCopied;
6176d4fba8b9Smrg	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6177d4fba8b9Smrg	XFlush(XtDisplay(xw));
6178d4fba8b9Smrg	screen->needSwap = 0;
6179d4fba8b9Smrg	ScrollBarDrawThumb(xw, 2);
6180d4fba8b9Smrg	X_GETTIMEOFDAY(&screen->buffered_at);
6181d4fba8b9Smrg    }
6182d4fba8b9Smrg}
6183d4fba8b9Smrg
6184d4fba8b9Smrgvoid
6185d4fba8b9SmrgxtermTimedDbe(XtermWidget xw)
6186d4fba8b9Smrg{
6187d4fba8b9Smrg    if (resource.buffered) {
6188d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
6189d4fba8b9Smrg	struct timeval now;
6190d4fba8b9Smrg	long elapsed;
6191d4fba8b9Smrg	long limit = DbeMsecs(xw);
6192d4fba8b9Smrg
6193d4fba8b9Smrg	X_GETTIMEOFDAY(&now);
6194d4fba8b9Smrg	if (screen->buffered_at.tv_sec) {
6195d4fba8b9Smrg	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6196d4fba8b9Smrg		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6197d4fba8b9Smrg	} else {
6198d4fba8b9Smrg	    elapsed = limit;
6199d4fba8b9Smrg	}
6200d4fba8b9Smrg	if (elapsed >= limit) {
6201d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
6202d4fba8b9Smrg	    xtermFlushDbe(xw);
6203d4fba8b9Smrg	}
6204d4fba8b9Smrg    }
6205d4fba8b9Smrg}
6206d4fba8b9Smrg#endif
6207d4fba8b9Smrg
62083367019cSmrg/*
62093367019cSmrg * cleanup by sending SIGHUP to client processes
62103367019cSmrg */
62113367019cSmrgvoid
62123367019cSmrgCleanup(int code)
62133367019cSmrg{
62143367019cSmrg    TScreen *screen = TScreenOf(term);
62153367019cSmrg
62163367019cSmrg    TRACE(("Cleanup %d\n", code));
6217d522f475Smrg
6218d522f475Smrg    if (screen->pid > 1) {
6219d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
6220d522f475Smrg    }
6221d522f475Smrg    Exit(code);
6222d522f475Smrg}
6223d522f475Smrg
6224fa3f02f3Smrg#ifndef S_IXOTH
6225fa3f02f3Smrg#define S_IXOTH 1
6226fa3f02f3Smrg#endif
6227fa3f02f3Smrg
6228fa3f02f3SmrgBoolean
6229fa3f02f3SmrgvalidProgram(const char *pathname)
6230fa3f02f3Smrg{
6231fa3f02f3Smrg    Boolean result = False;
6232fa3f02f3Smrg    struct stat sb;
6233fa3f02f3Smrg
6234fa3f02f3Smrg    if (!IsEmpty(pathname)
6235fa3f02f3Smrg	&& *pathname == '/'
6236fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
6237fa3f02f3Smrg	&& stat(pathname, &sb) == 0
6238fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
6239fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
6240fa3f02f3Smrg	result = True;
6241fa3f02f3Smrg    }
6242fa3f02f3Smrg    return result;
6243fa3f02f3Smrg}
6244fa3f02f3Smrg
6245d522f475Smrg#ifndef VMS
62463367019cSmrg#ifndef PATH_MAX
62473367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
62483367019cSmrg#endif
6249d522f475Smrgchar *
6250d522f475SmrgxtermFindShell(char *leaf, Bool warning)
6251d522f475Smrg{
62523367019cSmrg    char *s0;
6253d522f475Smrg    char *s;
6254d522f475Smrg    char *d;
6255d522f475Smrg    char *tmp;
6256d522f475Smrg    char *result = leaf;
62573367019cSmrg    Bool allocated = False;
6258d522f475Smrg
6259d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
62603367019cSmrg
62613367019cSmrg    if (!strncmp("./", result, (size_t) 2)
62623367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
62633367019cSmrg	size_t need = PATH_MAX;
62643367019cSmrg	size_t used = strlen(result) + 2;
62653367019cSmrg	char *buffer = malloc(used + need);
62663367019cSmrg	if (buffer != 0) {
62673367019cSmrg	    if (getcwd(buffer, need) != 0) {
62683367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
62693367019cSmrg		result = buffer;
62703367019cSmrg		allocated = True;
62713367019cSmrg	    } else {
62723367019cSmrg		free(buffer);
62733367019cSmrg	    }
62743367019cSmrg	}
62753367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
6276d522f475Smrg	/* find it in $PATH */
62773367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
62780d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
6279d522f475Smrg		Bool found = False;
6280d522f475Smrg		while (*s != '\0') {
6281d522f475Smrg		    strcpy(tmp, s);
6282d522f475Smrg		    for (d = tmp;; ++d) {
6283d522f475Smrg			if (*d == ':' || *d == '\0') {
6284d522f475Smrg			    int skip = (*d != '\0');
6285d522f475Smrg			    *d = '/';
6286d522f475Smrg			    strcpy(d + 1, leaf);
6287d522f475Smrg			    if (skip)
6288d522f475Smrg				++d;
6289d522f475Smrg			    s += (d - tmp);
6290fa3f02f3Smrg			    if (validProgram(tmp)) {
6291d522f475Smrg				result = x_strdup(tmp);
6292d522f475Smrg				found = True;
62933367019cSmrg				allocated = True;
6294d522f475Smrg			    }
6295d522f475Smrg			    break;
6296d522f475Smrg			}
6297d522f475Smrg		    }
6298d522f475Smrg		    if (found)
6299d522f475Smrg			break;
6300d522f475Smrg		}
6301d522f475Smrg		free(tmp);
6302d522f475Smrg	    }
63033367019cSmrg	    free(s0);
6304d522f475Smrg	}
6305d522f475Smrg    }
6306d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
6307fa3f02f3Smrg    if (!validProgram(result)) {
6308d522f475Smrg	if (warning)
63093367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
63103367019cSmrg	if (allocated)
63113367019cSmrg	    free(result);
6312d522f475Smrg	result = 0;
6313d522f475Smrg    }
63143367019cSmrg    /* be consistent, so that caller can always free the result */
63153367019cSmrg    if (result != 0 && !allocated)
63163367019cSmrg	result = x_strdup(result);
6317d522f475Smrg    return result;
6318d522f475Smrg}
6319d522f475Smrg#endif /* VMS */
6320d522f475Smrg
63210d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6322d522f475Smrg
63233367019cSmrg/*
63243367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
63253367019cSmrg * This could happen on some older machines due to the uneven standardization
63263367019cSmrg * process for the two functions.
63273367019cSmrg *
63283367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
63293367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
63303367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
63313367019cSmrg * could copy environ.
63323367019cSmrg */
63333367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
63343367019cSmrg#undef HAVE_PUTENV
63353367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
63363367019cSmrg#undef HAVE_UNSETENV
63373367019cSmrg#endif
63383367019cSmrg
6339d522f475Smrg/*
6340d522f475Smrg * copy the environment before Setenv'ing.
6341d522f475Smrg */
6342d522f475Smrgvoid
6343d522f475SmrgxtermCopyEnv(char **oldenv)
6344d522f475Smrg{
63453367019cSmrg#ifdef HAVE_PUTENV
63463367019cSmrg    (void) oldenv;
63473367019cSmrg#else
6348d522f475Smrg    unsigned size;
6349d522f475Smrg    char **newenv;
6350d522f475Smrg
6351d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
6352d522f475Smrg	;
6353d522f475Smrg    }
6354d522f475Smrg
6355d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
6356d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
6357d522f475Smrg    environ = newenv;
63583367019cSmrg#endif
63593367019cSmrg}
63603367019cSmrg
63613367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
63623367019cSmrgstatic int
63633367019cSmrgfindEnv(const char *var, int *lengthp)
63643367019cSmrg{
63653367019cSmrg    char *test;
63663367019cSmrg    int envindex = 0;
63673367019cSmrg    size_t len = strlen(var);
63683367019cSmrg    int found = -1;
63693367019cSmrg
63703367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
63713367019cSmrg
63723367019cSmrg    while ((test = environ[envindex]) != NULL) {
63733367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
63743367019cSmrg	    found = envindex;
63753367019cSmrg	    break;
63763367019cSmrg	}
63773367019cSmrg	envindex++;
63783367019cSmrg    }
63793367019cSmrg    *lengthp = envindex;
63803367019cSmrg    return found;
6381d522f475Smrg}
63823367019cSmrg#endif
6383d522f475Smrg
6384d522f475Smrg/*
6385d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6386d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
6387d522f475Smrg * This procedure assumes the memory for the first level of environ
6388d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
6389d522f475Smrg * to have to do a realloc().
6390d522f475Smrg */
6391d522f475Smrgvoid
6392cd3331d0SmrgxtermSetenv(const char *var, const char *value)
6393d522f475Smrg{
6394d522f475Smrg    if (value != 0) {
63953367019cSmrg#ifdef HAVE_PUTENV
63963367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
63973367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
63983367019cSmrg	if (both) {
63993367019cSmrg	    sprintf(both, "%s=%s", var, value);
64003367019cSmrg	    putenv(both);
64013367019cSmrg	}
64023367019cSmrg#else
6403d522f475Smrg	size_t len = strlen(var);
64043367019cSmrg	int envindex;
64053367019cSmrg	int found = findEnv(var, &envindex);
6406d522f475Smrg
6407d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6408d522f475Smrg
6409d522f475Smrg	if (found < 0) {
6410d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
6411d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
6412d522f475Smrg
6413d522f475Smrg	    if (need > have) {
6414d522f475Smrg		char **newenv;
6415d522f475Smrg		newenv = TypeMallocN(char *, need);
6416d522f475Smrg		if (newenv == 0) {
64173367019cSmrg		    xtermWarning("Cannot increase environment\n");
6418d522f475Smrg		    return;
6419d522f475Smrg		}
6420d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
6421d522f475Smrg		free(environ);
6422d522f475Smrg		environ = newenv;
6423d522f475Smrg	    }
6424d522f475Smrg
6425d522f475Smrg	    found = envindex;
6426d522f475Smrg	    environ[found + 1] = NULL;
6427d522f475Smrg	    environ = environ;
6428d522f475Smrg	}
6429d522f475Smrg
6430d4fba8b9Smrg	environ[found] = malloc(2 + len + strlen(value));
6431d522f475Smrg	if (environ[found] == 0) {
64323367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
6433d522f475Smrg	    return;
6434d522f475Smrg	}
6435d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
64363367019cSmrg#endif
6437d522f475Smrg    }
6438d522f475Smrg}
6439d522f475Smrg
64403367019cSmrgvoid
64413367019cSmrgxtermUnsetenv(const char *var)
64423367019cSmrg{
64433367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
64443367019cSmrg#ifdef HAVE_UNSETENV
64453367019cSmrg    unsetenv(var);
64463367019cSmrg#else
64473367019cSmrg    {
64483367019cSmrg	int ignore;
64493367019cSmrg	int item = findEnv(var, &ignore);
64503367019cSmrg	if (item >= 0) {
64513367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
64523367019cSmrg		++item;
64533367019cSmrg	    }
64543367019cSmrg	}
64553367019cSmrg    }
64563367019cSmrg#endif
64573367019cSmrg}
64583367019cSmrg
6459d522f475Smrg/*ARGSUSED*/
6460d522f475Smrgint
64619a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
6462d522f475Smrg{
64633367019cSmrg    xtermWarning("warning, error event received:\n");
6464d4fba8b9Smrg    TRACE_X_ERR(d, ev);
6465d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6466d522f475Smrg    Exit(ERROR_XERROR);
6467d522f475Smrg    return 0;			/* appease the compiler */
6468d522f475Smrg}
6469d522f475Smrg
6470712a7ff4Smrgvoid
6471712a7ff4Smrgice_error(IceConn iceConn)
6472712a7ff4Smrg{
6473712a7ff4Smrg    (void) iceConn;
6474712a7ff4Smrg
64753367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
64763367019cSmrg		 (long) getpid(), errno);
6477712a7ff4Smrg
6478712a7ff4Smrg    Exit(ERROR_ICEERROR);
6479712a7ff4Smrg}
6480712a7ff4Smrg
6481d522f475Smrg/*ARGSUSED*/
6482d522f475Smrgint
6483fa3f02f3Smrgxioerror(Display *dpy)
6484d522f475Smrg{
6485d522f475Smrg    int the_error = errno;
6486d522f475Smrg
64873367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
64883367019cSmrg		 the_error, SysErrorMsg(the_error),
64893367019cSmrg		 DisplayString(dpy));
6490d522f475Smrg
6491d522f475Smrg    Exit(ERROR_XIOERROR);
6492d522f475Smrg    return 0;			/* appease the compiler */
6493d522f475Smrg}
6494d522f475Smrg
6495728ff447Schristosvoid
6496d522f475Smrgxt_error(String message)
6497d522f475Smrg{
64983367019cSmrg    xtermWarning("Xt error: %s\n", message);
6499d522f475Smrg
6500d522f475Smrg    /*
6501d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
6502d522f475Smrg     */
6503d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
65043367019cSmrg	xtermWarning("DISPLAY is not set\n");
6505d522f475Smrg    }
6506d522f475Smrg    exit(1);
6507d522f475Smrg}
6508d522f475Smrg
6509d522f475Smrgint
6510d522f475SmrgXStrCmp(char *s1, char *s2)
6511d522f475Smrg{
6512d522f475Smrg    if (s1 && s2)
6513d522f475Smrg	return (strcmp(s1, s2));
6514d522f475Smrg    if (s1 && *s1)
6515d522f475Smrg	return (1);
6516d522f475Smrg    if (s2 && *s2)
6517d522f475Smrg	return (-1);
6518d522f475Smrg    return (0);
6519d522f475Smrg}
6520d522f475Smrg
6521d522f475Smrg#if OPT_TEK4014
6522d522f475Smrgstatic void
6523fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
6524d522f475Smrg{
6525d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
6526d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
6527d522f475Smrg    XWithdrawWindow(dpy, w, scr);
6528d522f475Smrg    return;
6529d522f475Smrg}
6530d522f475Smrg#endif
6531d522f475Smrg
6532d522f475Smrgvoid
6533d522f475Smrgset_vt_visibility(Bool on)
6534d522f475Smrg{
6535c219fbebSmrg    XtermWidget xw = term;
6536c219fbebSmrg    TScreen *screen = TScreenOf(xw);
6537d522f475Smrg
6538d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
6539d522f475Smrg    if (on) {
6540c219fbebSmrg	if (!screen->Vshow && xw) {
6541c219fbebSmrg	    VTInit(xw);
6542c219fbebSmrg	    XtMapWidget(XtParent(xw));
6543d522f475Smrg#if OPT_TOOLBAR
6544d522f475Smrg	    /* we need both of these during initialization */
6545c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
6546d522f475Smrg	    ShowToolbar(resource.toolBar);
6547d522f475Smrg#endif
6548d522f475Smrg	    screen->Vshow = True;
6549d522f475Smrg	}
6550d522f475Smrg    }
6551d522f475Smrg#if OPT_TEK4014
6552d522f475Smrg    else {
6553c219fbebSmrg	if (screen->Vshow && xw) {
6554c219fbebSmrg	    withdraw_window(XtDisplay(xw),
6555c219fbebSmrg			    VShellWindow(xw),
6556c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
6557d522f475Smrg	    screen->Vshow = False;
6558d522f475Smrg	}
6559d522f475Smrg    }
6560d522f475Smrg    set_vthide_sensitivity();
6561d522f475Smrg    set_tekhide_sensitivity();
6562d522f475Smrg    update_vttekmode();
6563d522f475Smrg    update_tekshow();
6564d522f475Smrg    update_vtshow();
6565d522f475Smrg#endif
6566d522f475Smrg    return;
6567d522f475Smrg}
6568d522f475Smrg
6569d522f475Smrg#if OPT_TEK4014
6570d522f475Smrgvoid
6571d522f475Smrgset_tek_visibility(Bool on)
6572d522f475Smrg{
6573d4fba8b9Smrg    XtermWidget xw = term;
6574d4fba8b9Smrg
6575d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
6576d522f475Smrg
6577d522f475Smrg    if (on) {
6578d4fba8b9Smrg	if (!TEK4014_SHOWN(xw)) {
6579cd3331d0Smrg	    if (tekWidget == 0) {
6580cd3331d0Smrg		TekInit();	/* will exit on failure */
6581cd3331d0Smrg	    }
6582cd3331d0Smrg	    if (tekWidget != 0) {
6583cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
6584cd3331d0Smrg		XtRealizeWidget(tekParent);
6585cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
6586d522f475Smrg#if OPT_TOOLBAR
6587cd3331d0Smrg		/* we need both of these during initialization */
6588cd3331d0Smrg		XtMapWidget(tekParent);
6589cd3331d0Smrg		XtMapWidget(tekWidget);
6590d522f475Smrg#endif
6591cd3331d0Smrg		XtOverrideTranslations(tekParent,
6592cd3331d0Smrg				       XtParseTranslationTable
6593cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6594cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
6595cd3331d0Smrg				       XtWindow(tekParent),
6596cd3331d0Smrg				       &wm_delete_window, 1);
6597d4fba8b9Smrg		TEK4014_SHOWN(xw) = True;
6598cd3331d0Smrg	    }
6599d522f475Smrg	}
6600d522f475Smrg    } else {
6601d4fba8b9Smrg	if (TEK4014_SHOWN(xw) && tekWidget) {
6602d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
6603d522f475Smrg			    TShellWindow,
6604d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6605d4fba8b9Smrg	    TEK4014_SHOWN(xw) = False;
6606d522f475Smrg	}
6607d522f475Smrg    }
6608d522f475Smrg    set_tekhide_sensitivity();
6609d522f475Smrg    set_vthide_sensitivity();
6610d522f475Smrg    update_vtshow();
6611d522f475Smrg    update_tekshow();
6612d522f475Smrg    update_vttekmode();
6613d522f475Smrg    return;
6614d522f475Smrg}
6615d522f475Smrg
6616d522f475Smrgvoid
6617d522f475Smrgend_tek_mode(void)
6618d522f475Smrg{
6619cd3331d0Smrg    XtermWidget xw = term;
6620cd3331d0Smrg
6621cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
6622cd3331d0Smrg	FlushLog(xw);
6623dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
6624dfb07bc7Smrg	xtermSetWinSize(xw);
6625d522f475Smrg	longjmp(Tekend, 1);
6626d522f475Smrg    }
6627d522f475Smrg    return;
6628d522f475Smrg}
6629d522f475Smrg
6630d522f475Smrgvoid
6631d522f475Smrgend_vt_mode(void)
6632d522f475Smrg{
6633cd3331d0Smrg    XtermWidget xw = term;
6634cd3331d0Smrg
6635cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
6636cd3331d0Smrg	FlushLog(xw);
6637d4fba8b9Smrg	set_tek_visibility(True);
6638cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
6639dfb07bc7Smrg	TekSetWinSize(tekWidget);
6640d522f475Smrg	longjmp(VTend, 1);
6641d522f475Smrg    }
6642d522f475Smrg    return;
6643d522f475Smrg}
6644d522f475Smrg
6645d522f475Smrgvoid
6646d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
6647d522f475Smrg{
6648d522f475Smrg    if (tovt) {
6649d522f475Smrg	if (tekRefreshList)
6650d522f475Smrg	    TekRefresh(tekWidget);
6651d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
6652d522f475Smrg    } else {
6653d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
6654d522f475Smrg    }
6655d522f475Smrg}
6656d522f475Smrg
6657d522f475Smrgvoid
6658d522f475Smrghide_vt_window(void)
6659d522f475Smrg{
6660d522f475Smrg    set_vt_visibility(False);
6661d522f475Smrg    if (!TEK4014_ACTIVE(term))
6662d522f475Smrg	switch_modes(False);	/* switch to tek mode */
6663d522f475Smrg}
6664d522f475Smrg
6665d522f475Smrgvoid
6666d522f475Smrghide_tek_window(void)
6667d522f475Smrg{
6668d522f475Smrg    set_tek_visibility(False);
6669d522f475Smrg    tekRefreshList = (TekLink *) 0;
6670d522f475Smrg    if (TEK4014_ACTIVE(term))
6671d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
6672d522f475Smrg}
6673d522f475Smrg#endif /* OPT_TEK4014 */
6674d522f475Smrg
6675d522f475Smrgstatic const char *
6676d522f475Smrgskip_punct(const char *s)
6677d522f475Smrg{
6678d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
6679d522f475Smrg	++s;
6680d522f475Smrg    }
6681d522f475Smrg    return s;
6682d522f475Smrg}
6683d522f475Smrg
6684d522f475Smrgstatic int
6685d522f475Smrgcmp_options(const void *a, const void *b)
6686d522f475Smrg{
6687d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
6688d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
6689d522f475Smrg    return strcmp(s1, s2);
6690d522f475Smrg}
6691d522f475Smrg
6692d522f475Smrgstatic int
6693d522f475Smrgcmp_resources(const void *a, const void *b)
6694d522f475Smrg{
6695d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
6696d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
6697d522f475Smrg}
6698d522f475Smrg
6699d522f475SmrgXrmOptionDescRec *
6700d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
6701d522f475Smrg{
6702d522f475Smrg    static XrmOptionDescRec *res_array = 0;
6703d522f475Smrg
6704d522f475Smrg#ifdef NO_LEAKS
6705cd3331d0Smrg    if (descs == 0) {
6706d4fba8b9Smrg	FreeAndNull(res_array);
6707d522f475Smrg    } else
6708d522f475Smrg#endif
6709d522f475Smrg    if (res_array == 0) {
6710d522f475Smrg	Cardinal j;
6711d522f475Smrg
6712d522f475Smrg	/* make a sorted index to 'resources' */
6713d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
6714cd3331d0Smrg	if (res_array != 0) {
6715cd3331d0Smrg	    for (j = 0; j < res_count; j++)
6716cd3331d0Smrg		res_array[j] = descs[j];
6717cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
6718cd3331d0Smrg	}
6719d522f475Smrg    }
6720d522f475Smrg    return res_array;
6721d522f475Smrg}
6722d522f475Smrg
6723d522f475Smrg/*
6724d522f475Smrg * The first time this is called, construct sorted index to the main program's
6725d522f475Smrg * list of options, taking into account the on/off options which will be
6726d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
6727d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
6728d522f475Smrg */
6729d522f475SmrgOptionHelp *
6730d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
6731d522f475Smrg{
6732d522f475Smrg    static OptionHelp *opt_array = 0;
6733d522f475Smrg
6734d522f475Smrg#ifdef NO_LEAKS
6735d522f475Smrg    if (descs == 0 && opt_array != 0) {
6736d522f475Smrg	sortedOptDescs(descs, numDescs);
6737d4fba8b9Smrg	FreeAndNull(opt_array);
6738d522f475Smrg	return 0;
6739d522f475Smrg    } else if (options == 0 || descs == 0) {
6740d522f475Smrg	return 0;
6741d522f475Smrg    }
6742d522f475Smrg#endif
6743d522f475Smrg
6744d522f475Smrg    if (opt_array == 0) {
6745cd3331d0Smrg	size_t opt_count, j;
6746d522f475Smrg#if OPT_TRACE
6747d522f475Smrg	Cardinal k;
6748d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
6749d522f475Smrg	int code;
6750cd3331d0Smrg	const char *mesg;
6751d522f475Smrg#else
6752d522f475Smrg	(void) descs;
6753d522f475Smrg	(void) numDescs;
6754d522f475Smrg#endif
6755d522f475Smrg
6756d522f475Smrg	/* count 'options' and make a sorted index to it */
6757d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
6758d522f475Smrg	    ;
6759d522f475Smrg	}
6760d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
6761d522f475Smrg	for (j = 0; j < opt_count; j++)
6762d522f475Smrg	    opt_array[j] = options[j];
6763d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
6764d522f475Smrg
6765d522f475Smrg	/* supply the "turn on/off" strings if needed */
6766d522f475Smrg#if OPT_TRACE
6767d522f475Smrg	for (j = 0; j < opt_count; j++) {
6768712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
6769c219fbebSmrg		char temp[80];
6770cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
6771d522f475Smrg		for (k = 0; k < numDescs; ++k) {
6772cd3331d0Smrg		    const char *value = res_array[k].value;
6773d522f475Smrg		    if (res_array[k].option[0] == '-') {
6774d522f475Smrg			code = -1;
6775d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
6776d522f475Smrg			code = 1;
6777d522f475Smrg		    } else {
6778d522f475Smrg			code = 0;
6779d522f475Smrg		    }
67803367019cSmrg		    sprintf(temp, "%.*s",
67813367019cSmrg			    (int) sizeof(temp) - 2,
67823367019cSmrg			    opt_array[j].desc);
6783c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
6784d522f475Smrg			code = -code;
6785d522f475Smrg		    if (code != 0
6786d522f475Smrg			&& res_array[k].value != 0
6787d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
6788d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
6789d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
6790d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
6791d522f475Smrg			    mesg = "turn on/off";
6792d522f475Smrg			} else {
6793d522f475Smrg			    mesg = "turn off/on";
6794d522f475Smrg			}
6795d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
6796d522f475Smrg			       mesg,
6797d522f475Smrg			       res_array[k].option,
6798d522f475Smrg			       res_array[k].value,
6799d522f475Smrg			       opt_array[j].opt,
6800d522f475Smrg			       opt_array[j].desc));
6801d522f475Smrg			break;
6802d522f475Smrg		    }
6803d522f475Smrg		}
6804d522f475Smrg	    }
6805d522f475Smrg	}
6806d522f475Smrg#endif
6807d522f475Smrg    }
6808d522f475Smrg    return opt_array;
6809d522f475Smrg}
6810d522f475Smrg
6811d522f475Smrg/*
6812d522f475Smrg * Report the character-type locale that xterm was started in.
6813d522f475Smrg */
68143367019cSmrgString
6815d522f475SmrgxtermEnvLocale(void)
6816d522f475Smrg{
68173367019cSmrg    static String result;
6818d522f475Smrg
6819d522f475Smrg    if (result == 0) {
6820d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
6821cd3331d0Smrg	    result = x_strdup("C");
6822cd3331d0Smrg	} else {
6823cd3331d0Smrg	    result = x_strdup(result);
6824d522f475Smrg	}
6825d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
6826d522f475Smrg    }
6827d522f475Smrg    return result;
6828d522f475Smrg}
6829d522f475Smrg
6830d522f475Smrgchar *
6831d522f475SmrgxtermEnvEncoding(void)
6832d522f475Smrg{
6833d522f475Smrg    static char *result;
6834d522f475Smrg
6835d522f475Smrg    if (result == 0) {
6836d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6837d522f475Smrg	result = nl_langinfo(CODESET);
6838d522f475Smrg#else
6839d4fba8b9Smrg	const char *locale = xtermEnvLocale();
6840d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
6841d4fba8b9Smrg	    result = x_strdup("ASCII");
6842d522f475Smrg	} else {
6843d4fba8b9Smrg	    result = x_strdup("ISO-8859-1");
6844d522f475Smrg	}
6845d522f475Smrg#endif
6846d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
6847d522f475Smrg    }
6848d522f475Smrg    return result;
6849d522f475Smrg}
6850d522f475Smrg
6851d522f475Smrg#if OPT_WIDE_CHARS
6852d522f475Smrg/*
6853d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
6854d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
6855d522f475Smrg * various library calls.
6856d522f475Smrg */
6857d522f475SmrgBool
6858d522f475SmrgxtermEnvUTF8(void)
6859d522f475Smrg{
6860d522f475Smrg    static Bool init = False;
6861d522f475Smrg    static Bool result = False;
6862d522f475Smrg
6863d522f475Smrg    if (!init) {
6864d522f475Smrg	init = True;
6865d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6866d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
6867d522f475Smrg#else
6868fa3f02f3Smrg	{
6869fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
6870fa3f02f3Smrg	    int n;
6871fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
6872fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
6873fa3f02f3Smrg	    }
6874fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
6875fa3f02f3Smrg		result = True;
6876fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
6877fa3f02f3Smrg		result = True;
6878fa3f02f3Smrg	    free(locale);
6879fa3f02f3Smrg	}
6880d522f475Smrg#endif
6881d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
6882d522f475Smrg    }
6883d522f475Smrg    return result;
6884d522f475Smrg}
6885d522f475Smrg#endif /* OPT_WIDE_CHARS */
6886d522f475Smrg
6887b7c89284Ssnj/*
6888b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
6889b7c89284Ssnj */
6890b7c89284SsnjXtermWidget
6891b7c89284SsnjgetXtermWidget(Widget w)
6892b7c89284Ssnj{
6893b7c89284Ssnj    XtermWidget xw;
6894b7c89284Ssnj
6895b7c89284Ssnj    if (w == 0) {
6896b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
6897b7c89284Ssnj	if (!IsXtermWidget(xw)) {
6898b7c89284Ssnj	    xw = 0;
6899b7c89284Ssnj	}
6900b7c89284Ssnj    } else if (IsXtermWidget(w)) {
6901b7c89284Ssnj	xw = (XtermWidget) w;
6902b7c89284Ssnj    } else {
6903b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
6904b7c89284Ssnj    }
6905b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
6906b7c89284Ssnj    return xw;
6907b7c89284Ssnj}
69083367019cSmrg
69093367019cSmrg#if OPT_SESSION_MGT
6910636d5e9fSmrg
6911636d5e9fSmrg#if OPT_TRACE
6912636d5e9fSmrgstatic void
6913636d5e9fSmrgtrace_1_SM(const char *tag, String name)
6914636d5e9fSmrg{
6915636d5e9fSmrg    Arg args[1];
6916636d5e9fSmrg    char *buf = 0;
6917636d5e9fSmrg
6918636d5e9fSmrg    XtSetArg(args[0], name, &buf);
6919636d5e9fSmrg    XtGetValues(toplevel, args, 1);
6920636d5e9fSmrg
6921636d5e9fSmrg    if (strstr(name, "Path") || strstr(name, "Directory")) {
6922636d5e9fSmrg	TRACE(("%s %s: %s\n", tag, name, NonNull(buf)));
6923636d5e9fSmrg    } else if (strstr(name, "Command")) {
6924636d5e9fSmrg	if (buf != NULL) {
6925636d5e9fSmrg	    char **vec = (char **) (void *) buf;
6926636d5e9fSmrg	    int n;
6927636d5e9fSmrg	    TRACE(("%s %s:\n", tag, name));
6928636d5e9fSmrg	    for (n = 0; vec[n] != NULL; ++n) {
6929636d5e9fSmrg		TRACE((" arg[%d] = %s\n", n, vec[n]));
6930636d5e9fSmrg	    }
6931636d5e9fSmrg	} else {
6932636d5e9fSmrg	    TRACE(("%s %s: %p\n", tag, name, buf));
6933636d5e9fSmrg	}
6934636d5e9fSmrg    } else {
6935636d5e9fSmrg	TRACE(("%s %s: %p\n", tag, name, buf));
6936636d5e9fSmrg    }
6937636d5e9fSmrg}
6938636d5e9fSmrg
6939636d5e9fSmrgstatic void
6940636d5e9fSmrgtrace_SM_props(void)
6941636d5e9fSmrg{
6942636d5e9fSmrg    /* *INDENT-OFF* */
6943636d5e9fSmrg    static struct { String app, cls; } table[] = {
6944636d5e9fSmrg	{ XtNcurrentDirectory,	XtCCurrentDirectory },
6945636d5e9fSmrg	{ XtNdieCallback,	XtNdiscardCommand },
6946636d5e9fSmrg	{ XtCDiscardCommand,	XtNenvironment },
6947636d5e9fSmrg	{ XtCEnvironment,	XtNinteractCallback },
6948636d5e9fSmrg	{ XtNjoinSession,	XtCJoinSession },
6949636d5e9fSmrg	{ XtNprogramPath,	XtCProgramPath },
6950636d5e9fSmrg	{ XtNresignCommand,	XtCResignCommand },
6951636d5e9fSmrg	{ XtNrestartCommand,	XtCRestartCommand },
6952636d5e9fSmrg	{ XtNrestartStyle,	XtCRestartStyle },
6953636d5e9fSmrg	{ XtNsaveCallback,	XtNsaveCompleteCallback },
6954636d5e9fSmrg	{ XtNsessionID,		XtCSessionID },
6955636d5e9fSmrg	{ XtNshutdownCommand,	XtCShutdownCommand },
6956636d5e9fSmrg    };
6957636d5e9fSmrg    /* *INDENT-ON* */
6958636d5e9fSmrg    Cardinal n;
6959636d5e9fSmrg    TRACE(("Session properties:\n"));
6960636d5e9fSmrg    for (n = 0; n < XtNumber(table); ++n) {
6961636d5e9fSmrg	trace_1_SM("app", table[n].app);
6962636d5e9fSmrg	trace_1_SM("cls", table[n].cls);
6963636d5e9fSmrg    }
6964636d5e9fSmrg}
6965636d5e9fSmrg#define TRACE_SM_PROPS()	trace_SM_props()
6966636d5e9fSmrg#else
6967636d5e9fSmrg#define TRACE_SM_PROPS()	/* nothing */
6968636d5e9fSmrg#endif
6969636d5e9fSmrg
69703367019cSmrgstatic void
69713367019cSmrgdie_callback(Widget w GCC_UNUSED,
69723367019cSmrg	     XtPointer client_data GCC_UNUSED,
69733367019cSmrg	     XtPointer call_data GCC_UNUSED)
69743367019cSmrg{
6975c48a5815Smrg    TRACE(("die_callback client=%p, call=%p\n",
6976c48a5815Smrg	   (void *) client_data,
6977c48a5815Smrg	   (void *) call_data));
6978636d5e9fSmrg    TRACE_SM_PROPS();
69793367019cSmrg    NormalExit();
69803367019cSmrg}
69813367019cSmrg
69823367019cSmrgstatic void
69833367019cSmrgsave_callback(Widget w GCC_UNUSED,
69843367019cSmrg	      XtPointer client_data GCC_UNUSED,
69853367019cSmrg	      XtPointer call_data)
69863367019cSmrg{
69873367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
6988636d5e9fSmrg    TRACE(("save_callback:\n"));
6989636d5e9fSmrg    TRACE(("... save_type            <-%d\n", token->save_type));
6990636d5e9fSmrg    TRACE(("... interact_style       <-%d\n", token->interact_style));
6991636d5e9fSmrg    TRACE(("... shutdown             <-%s\n", BtoS(token->shutdown)));
6992636d5e9fSmrg    TRACE(("... fast                 <-%s\n", BtoS(token->fast)));
6993636d5e9fSmrg    TRACE(("... cancel_shutdown      <-%s\n", BtoS(token->cancel_shutdown)));
6994636d5e9fSmrg    TRACE(("... phase                <-%d\n", token->phase));
6995636d5e9fSmrg    TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type));
6996636d5e9fSmrg    TRACE(("... request_cancel       ->%s\n", BtoS(token->request_cancel)));
6997636d5e9fSmrg    TRACE(("... request_next_phase   ->%s\n", BtoS(token->request_next_phase)));
6998636d5e9fSmrg    TRACE(("... save_success         ->%s\n", BtoS(token->save_success)));
6999636d5e9fSmrg    xtermUpdateRestartCommand(term);
7000636d5e9fSmrg    /* we have nothing more to save */
70013367019cSmrg    token->save_success = True;
70023367019cSmrg}
70033367019cSmrg
70043367019cSmrgstatic void
70053367019cSmrgicewatch(IceConn iceConn,
70063367019cSmrg	 IcePointer clientData GCC_UNUSED,
70073367019cSmrg	 Bool opening,
70083367019cSmrg	 IcePointer * watchData GCC_UNUSED)
70093367019cSmrg{
70103367019cSmrg    if (opening) {
70113367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
70123367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
70133367019cSmrg    } else {
70143367019cSmrg	ice_fd = -1;
70153367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
70163367019cSmrg    }
70173367019cSmrg}
70183367019cSmrg
70193367019cSmrgvoid
70203367019cSmrgxtermOpenSession(void)
70213367019cSmrg{
70223367019cSmrg    if (resource.sessionMgt) {
70233367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
70243367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
70253367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
7026636d5e9fSmrg
7027636d5e9fSmrg	TRACE_SM_PROPS();
70283367019cSmrg    }
70293367019cSmrg}
70303367019cSmrg
70313367019cSmrgvoid
70323367019cSmrgxtermCloseSession(void)
70333367019cSmrg{
70343367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
70353367019cSmrg}
7036636d5e9fSmrg
7037636d5e9fSmrgtypedef enum {
7038636d5e9fSmrg    B_ARG = 0,
7039636d5e9fSmrg    I_ARG,
7040636d5e9fSmrg    D_ARG,
7041636d5e9fSmrg    S_ARG
7042636d5e9fSmrg} ParamType;
7043636d5e9fSmrg
7044636d5e9fSmrg#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) }
7045636d5e9fSmrg#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) }
7046636d5e9fSmrg#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) }
7047636d5e9fSmrg#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) }
7048636d5e9fSmrg
7049636d5e9fSmrgtypedef struct {
7050636d5e9fSmrg    const char name[30];
7051636d5e9fSmrg    ParamType type;
7052636d5e9fSmrg    Cardinal offset;
7053636d5e9fSmrg} FontParams;
7054636d5e9fSmrg
7055636d5e9fSmrg/* *INDENT-OFF* */
7056636d5e9fSmrgstatic const FontParams fontParams[] = {
7057636d5e9fSmrg    Iarg(XtNinitialFont,     screen.menu_font_number),	/* "-fc" */
7058636d5e9fSmrg    Barg(XtNallowBoldFonts,  screen.allowBoldFonts),	/* menu */
7059636d5e9fSmrg#if OPT_BOX_CHARS
7060636d5e9fSmrg    Barg(XtNforceBoxChars,   screen.force_box_chars),	/* "-fbx" */
7061636d5e9fSmrg    Barg(XtNforcePackedFont, screen.force_packed),	/* menu */
7062636d5e9fSmrg#endif
7063636d5e9fSmrg#if OPT_DEC_CHRSET
7064636d5e9fSmrg    Barg(XtNfontDoublesize,  screen.font_doublesize),	/* menu */
7065636d5e9fSmrg#endif
7066636d5e9fSmrg#if OPT_WIDE_CHARS
7067636d5e9fSmrg    Barg(XtNutf8Fonts,       screen.utf8_fonts),	/* menu */
7068636d5e9fSmrg#endif
7069636d5e9fSmrg#if OPT_RENDERFONT
7070636d5e9fSmrg    Darg(XtNfaceSize,        misc.face_size[0]),	/* "-fs" */
7071636d5e9fSmrg    Sarg(XtNfaceName,        misc.default_xft.f_n),	/* "-fa" */
7072636d5e9fSmrg    Sarg(XtNrenderFont,      misc.render_font_s),	/* (resource) */
7073636d5e9fSmrg#endif
7074636d5e9fSmrg};
7075636d5e9fSmrg/* *INDENT-ON* */
7076636d5e9fSmrg
7077636d5e9fSmrg#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2)
7078636d5e9fSmrg#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset)
7079636d5e9fSmrg
7080636d5e9fSmrg/*
7081636d5e9fSmrg * If no widget is given, no value is used.
7082636d5e9fSmrg */
7083636d5e9fSmrgstatic char *
7084636d5e9fSmrgformatFontParam(char *result, XtermWidget xw, const FontParams * parameter)
7085636d5e9fSmrg{
7086636d5e9fSmrg    sprintf(result, "%s*%s:", ProgramName, parameter->name);
7087636d5e9fSmrg    if (xw != None) {
7088636d5e9fSmrg	char *next = result + strlen(result);
7089636d5e9fSmrg	switch (parameter->type) {
7090636d5e9fSmrg	case B_ARG:
7091636d5e9fSmrg	    sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset)
7092636d5e9fSmrg		    ? "true"
7093636d5e9fSmrg		    : "false");
7094636d5e9fSmrg	    break;
7095636d5e9fSmrg	case I_ARG:
7096636d5e9fSmrg	    sprintf(next, "%d", TypedPtr(int));
7097636d5e9fSmrg	    break;
7098636d5e9fSmrg	case D_ARG:
7099636d5e9fSmrg	    sprintf(next, "%.1f", TypedPtr(float));
7100636d5e9fSmrg	    break;
7101636d5e9fSmrg	case S_ARG:
7102636d5e9fSmrg	    strcpy(next, TypedPtr(char *));
7103636d5e9fSmrg#if OPT_RENDERFONT
7104636d5e9fSmrg	    if (!strcmp(parameter->name, XtNfaceName)) {
7105636d5e9fSmrg		if (IsEmpty(next)
7106636d5e9fSmrg		    && xw->work.render_font) {
7107636d5e9fSmrg		    strcpy(next, DEFFACENAME_AUTO);
7108636d5e9fSmrg		}
7109636d5e9fSmrg	    } else if (!strcmp(parameter->name, XtNrenderFont)) {
7110636d5e9fSmrg		if (xw->work.render_font == erDefault
7111636d5e9fSmrg		    && IsEmpty(xw->misc.default_xft.f_n)) {
7112636d5e9fSmrg		    strcpy(next, "DefaultOff");
7113636d5e9fSmrg		}
7114636d5e9fSmrg	    }
7115636d5e9fSmrg#endif
7116636d5e9fSmrg	    break;
7117636d5e9fSmrg	}
7118636d5e9fSmrg    }
7119636d5e9fSmrg    return result;
7120636d5e9fSmrg}
7121636d5e9fSmrg
7122636d5e9fSmrg#if OPT_TRACE
7123636d5e9fSmrgstatic void
7124636d5e9fSmrgdumpFontParams(XtermWidget xw)
7125636d5e9fSmrg{
7126636d5e9fSmrg    char buffer[1024];
7127636d5e9fSmrg    Cardinal n;
7128636d5e9fSmrg
7129636d5e9fSmrg    TRACE(("FontParams:\n"));
7130636d5e9fSmrg    for (n = 0; n < XtNumber(fontParams); ++n) {
7131636d5e9fSmrg	TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n)));
7132636d5e9fSmrg    }
7133636d5e9fSmrg}
7134636d5e9fSmrg#else
7135636d5e9fSmrg#define dumpFontParams(xw)	/* nothing */
7136636d5e9fSmrg#endif
7137636d5e9fSmrg
7138636d5e9fSmrgstatic Boolean
7139636d5e9fSmrgfindFontParams(int argc, char **argv)
7140636d5e9fSmrg{
7141636d5e9fSmrg    Boolean result = False;
7142636d5e9fSmrg
7143636d5e9fSmrg    if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) {
7144636d5e9fSmrg	int n;
7145636d5e9fSmrg
7146636d5e9fSmrg	for (n = 0; n < RESTART_PARAMS; ++n) {
7147636d5e9fSmrg	    int my_index = argc - restart_params - n - 1;
7148636d5e9fSmrg	    int my_param = (RESTART_PARAMS - n - 1) / 2;
7149636d5e9fSmrg	    char *actual = argv[my_index];
7150636d5e9fSmrg	    char expect[1024];
7151636d5e9fSmrg	    Boolean value = (Boolean) ((n % 2) == 0);
7152636d5e9fSmrg
7153636d5e9fSmrg	    result = False;
7154636d5e9fSmrg	    TRACE(("...index: %d\n", my_index));
7155636d5e9fSmrg	    TRACE(("...param: %d\n", my_param));
7156636d5e9fSmrg	    TRACE(("...actual %s\n", actual));
7157636d5e9fSmrg	    if (IsEmpty(actual))
7158636d5e9fSmrg		break;
7159636d5e9fSmrg
7160636d5e9fSmrg	    if (value) {
7161636d5e9fSmrg		formatFontParam(expect, None, fontParams + my_param);
7162636d5e9fSmrg	    } else {
7163636d5e9fSmrg		strcpy(expect, "-xrm");
7164636d5e9fSmrg	    }
7165636d5e9fSmrg
7166636d5e9fSmrg	    TRACE(("...expect %s\n", expect));
7167636d5e9fSmrg
7168636d5e9fSmrg	    if (value) {
7169636d5e9fSmrg		if (strlen(expect) >= strlen(actual))
7170636d5e9fSmrg		    break;
7171636d5e9fSmrg		if (strncmp(expect, actual, strlen(expect)))
7172636d5e9fSmrg		    break;
7173636d5e9fSmrg	    } else {
7174636d5e9fSmrg		if (strcmp(actual, expect))
7175636d5e9fSmrg		    break;
7176636d5e9fSmrg	    }
7177636d5e9fSmrg	    TRACE(("fixme/ok:%d\n", n));
7178636d5e9fSmrg	    result = True;
7179636d5e9fSmrg	}
7180636d5e9fSmrg	TRACE(("findFontParams: %s (tested %d of %d parameters)\n",
7181636d5e9fSmrg	       BtoS(result), n + 1, RESTART_PARAMS));
7182636d5e9fSmrg    }
7183636d5e9fSmrg    return result;
7184636d5e9fSmrg}
7185636d5e9fSmrg
7186636d5e9fSmrgstatic int
7187c48a5815SmrginsertFontParams(XtermWidget xw, int *targetp, Bool first)
7188636d5e9fSmrg{
7189636d5e9fSmrg    int changed = 0;
7190636d5e9fSmrg    int n;
7191636d5e9fSmrg    int target = *targetp;
7192636d5e9fSmrg    char buffer[1024];
7193636d5e9fSmrg    const char *option = "-xrm";
7194636d5e9fSmrg
7195636d5e9fSmrg    for (n = 0; n < (int) XtNumber(fontParams); ++n) {
7196636d5e9fSmrg	formatFontParam(buffer, xw, fontParams + n);
7197636d5e9fSmrg	TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer));
7198636d5e9fSmrg	if (restart_command[target] == NULL)
7199636d5e9fSmrg	    restart_command[target] = x_strdup(option);
7200636d5e9fSmrg	++target;
7201636d5e9fSmrg	if (first) {
7202636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7203636d5e9fSmrg	    ++changed;
7204636d5e9fSmrg	} else if (restart_command[target] == NULL
7205636d5e9fSmrg		   || strcmp(restart_command[target], buffer)) {
7206636d5e9fSmrg	    free(restart_command[target]);
7207636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7208636d5e9fSmrg	    ++changed;
7209636d5e9fSmrg	}
7210636d5e9fSmrg	++target;
7211636d5e9fSmrg    }
7212636d5e9fSmrg    *targetp = target;
7213636d5e9fSmrg    return changed;
7214636d5e9fSmrg}
7215636d5e9fSmrg
7216636d5e9fSmrgvoid
7217636d5e9fSmrgxtermUpdateRestartCommand(XtermWidget xw)
7218636d5e9fSmrg{
7219636d5e9fSmrg    if (resource.sessionMgt) {
7220636d5e9fSmrg	Arg args[1];
7221636d5e9fSmrg	char **argv = 0;
7222636d5e9fSmrg
7223636d5e9fSmrg	XtSetArg(args[0], XtNrestartCommand, &argv);
7224636d5e9fSmrg	XtGetValues(toplevel, args, 1);
7225636d5e9fSmrg	if (argv != NULL) {
7226636d5e9fSmrg	    static int my_params = 0;
7227636d5e9fSmrg
7228636d5e9fSmrg	    int changes = 0;
7229636d5e9fSmrg	    Boolean first = False;
7230636d5e9fSmrg	    int argc;
7231636d5e9fSmrg	    int want;
7232636d5e9fSmrg	    int source, target;
7233636d5e9fSmrg
7234636d5e9fSmrg	    TRACE(("xtermUpdateRestartCommand\n"));
7235636d5e9fSmrg	    dumpFontParams(xw);
7236636d5e9fSmrg	    for (argc = 0; argv[argc] != NULL; ++argc) {
7237636d5e9fSmrg		TRACE((" arg[%d] = %s\n", argc, argv[argc]));
7238636d5e9fSmrg		;
7239636d5e9fSmrg	    }
7240636d5e9fSmrg	    want = argc - (restart_params + RESTART_PARAMS);
7241636d5e9fSmrg
7242636d5e9fSmrg	    TRACE((" argc:           %d\n", argc));
7243636d5e9fSmrg	    TRACE((" restart_params: %d\n", restart_params));
7244636d5e9fSmrg	    TRACE((" want to insert: %d\n", want));
7245636d5e9fSmrg
7246636d5e9fSmrg	    /*
7247636d5e9fSmrg	     * If we already have the font-choice option, do not add it again.
7248636d5e9fSmrg	     */
7249636d5e9fSmrg	    if (findFontParams(argc, argv)) {
7250636d5e9fSmrg		my_params = (want);
7251636d5e9fSmrg	    } else {
7252636d5e9fSmrg		first = True;
7253636d5e9fSmrg		my_params = (argc - restart_params);
7254636d5e9fSmrg	    }
7255636d5e9fSmrg	    TRACE((" my_params:      %d\n", my_params));
7256636d5e9fSmrg
7257636d5e9fSmrg	    if (my_params > argc) {
7258636d5e9fSmrg		TRACE((" re-allocate restartCommand\n"));
7259636d5e9fSmrg		FreeAndNull(restart_command);
7260636d5e9fSmrg	    }
7261636d5e9fSmrg
7262636d5e9fSmrg	    if (restart_command == NULL) {
7263636d5e9fSmrg		int need = argc + RESTART_PARAMS + 1;
7264636d5e9fSmrg
7265636d5e9fSmrg		restart_command = TypeCallocN(char *, need);
7266636d5e9fSmrg
7267636d5e9fSmrg		TRACE(("..inserting font-parameters\n"));
7268636d5e9fSmrg		for (source = target = 0; source < argc; ++source) {
7269636d5e9fSmrg		    if (source == my_params) {
7270636d5e9fSmrg			changes += insertFontParams(xw, &target, first);
7271636d5e9fSmrg			if (!first) {
7272636d5e9fSmrg			    source += (RESTART_PARAMS - 1);
7273636d5e9fSmrg			    continue;
7274636d5e9fSmrg			}
7275636d5e9fSmrg		    }
7276636d5e9fSmrg		    if (argv[source] == NULL)
7277636d5e9fSmrg			break;
7278636d5e9fSmrg		    restart_command[target++] = x_strdup(argv[source]);
7279636d5e9fSmrg		}
7280636d5e9fSmrg		restart_command[target] = NULL;
7281636d5e9fSmrg	    } else {
7282636d5e9fSmrg		TRACE(("..replacing font-parameters\n"));
7283636d5e9fSmrg		target = my_params;
7284636d5e9fSmrg		changes += insertFontParams(xw, &target, first);
7285636d5e9fSmrg	    }
7286636d5e9fSmrg	    if (changes) {
7287636d5e9fSmrg		TRACE(("..%d parameters changed\n", changes));
7288636d5e9fSmrg		XtSetArg(args[0], XtNrestartCommand, restart_command);
7289636d5e9fSmrg		XtSetValues(toplevel, args, 1);
7290636d5e9fSmrg	    } else {
7291636d5e9fSmrg		TRACE(("..NO parameters changed\n"));
7292636d5e9fSmrg	    }
7293636d5e9fSmrg	}
7294636d5e9fSmrg	TRACE_SM_PROPS();
7295636d5e9fSmrg    }
7296636d5e9fSmrg}
72973367019cSmrg#endif /* OPT_SESSION_MGT */
72983367019cSmrg
72993367019cSmrgWidget
73003367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
73013367019cSmrg		     String my_class,
73023367019cSmrg		     XrmOptionDescRec * options,
73033367019cSmrg		     Cardinal num_options,
73043367019cSmrg		     int *argc_in_out,
7305d4fba8b9Smrg		     char **argv_in_out,
7306fa3f02f3Smrg		     String *fallback_resources,
73073367019cSmrg		     WidgetClass widget_class,
73083367019cSmrg		     ArgList args,
73093367019cSmrg		     Cardinal num_args)
73103367019cSmrg{
73113367019cSmrg    Widget result;
73123367019cSmrg
73133367019cSmrg    XtSetErrorHandler(xt_error);
73143367019cSmrg#if OPT_SESSION_MGT
73153367019cSmrg    result = XtOpenApplication(app_context_return,
73163367019cSmrg			       my_class,
73173367019cSmrg			       options,
73183367019cSmrg			       num_options,
73193367019cSmrg			       argc_in_out,
73203367019cSmrg			       argv_in_out,
73213367019cSmrg			       fallback_resources,
73223367019cSmrg			       widget_class,
73233367019cSmrg			       args,
73243367019cSmrg			       num_args);
73253367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
73263367019cSmrg#else
73279a64e1c5Smrg    (void) widget_class;
73289a64e1c5Smrg    (void) args;
73299a64e1c5Smrg    (void) num_args;
73303367019cSmrg    result = XtAppInitialize(app_context_return,
73313367019cSmrg			     my_class,
73323367019cSmrg			     options,
73333367019cSmrg			     num_options,
73343367019cSmrg			     argc_in_out,
73353367019cSmrg			     argv_in_out,
73363367019cSmrg			     fallback_resources,
73373367019cSmrg			     NULL, 0);
73383367019cSmrg#endif /* OPT_SESSION_MGT */
7339e8264990Smrg    XtSetErrorHandler(NULL);
73403367019cSmrg
73413367019cSmrg    return result;
73423367019cSmrg}
73433367019cSmrg
7344d4fba8b9Smrg/*
7345d4fba8b9Smrg * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
7346d4fba8b9Smrg * our own error-handler.
7347d4fba8b9Smrg */
7348d4fba8b9Smrg/* ARGSUSED */
7349d4fba8b9Smrgint
7350d4fba8b9Smrgignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
7351d4fba8b9Smrg{
7352d4fba8b9Smrg    return 1;
7353d4fba8b9Smrg}
7354d4fba8b9Smrg
73553367019cSmrgstatic int x11_errors;
73563367019cSmrg
73573367019cSmrgstatic int
73589a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
73593367019cSmrg{
73603367019cSmrg    (void) display;
73613367019cSmrg    (void) error_event;
73623367019cSmrg    ++x11_errors;
73633367019cSmrg    return 0;
73643367019cSmrg}
73653367019cSmrg
73663367019cSmrgBoolean
7367fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
73683367019cSmrg{
73693367019cSmrg    Boolean result = False;
73703367019cSmrg    Status code;
73713367019cSmrg
73723367019cSmrg    memset(attrs, 0, sizeof(*attrs));
73733367019cSmrg    if (win != None) {
73743367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
73753367019cSmrg	x11_errors = 0;
73763367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
73773367019cSmrg	XSetErrorHandler(save);
73783367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
73793367019cSmrg	if (result) {
73803367019cSmrg	    TRACE_WIN_ATTRS(attrs);
73813367019cSmrg	} else {
73823367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
73833367019cSmrg	}
73843367019cSmrg    }
73853367019cSmrg    return result;
73863367019cSmrg}
73873367019cSmrg
73883367019cSmrgBoolean
7389fa3f02f3SmrgxtermGetWinProp(Display *display,
73903367019cSmrg		Window win,
73913367019cSmrg		Atom property,
73923367019cSmrg		long long_offset,
73933367019cSmrg		long long_length,
73943367019cSmrg		Atom req_type,
73959a64e1c5Smrg		Atom *actual_type_return,
73963367019cSmrg		int *actual_format_return,
73973367019cSmrg		unsigned long *nitems_return,
73983367019cSmrg		unsigned long *bytes_after_return,
73993367019cSmrg		unsigned char **prop_return)
74003367019cSmrg{
7401d4fba8b9Smrg    Boolean result = False;
74023367019cSmrg
74033367019cSmrg    if (win != None) {
74043367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
74053367019cSmrg	x11_errors = 0;
74063367019cSmrg	if (XGetWindowProperty(display,
74073367019cSmrg			       win,
74083367019cSmrg			       property,
74093367019cSmrg			       long_offset,
74103367019cSmrg			       long_length,
74113367019cSmrg			       False,
74123367019cSmrg			       req_type,
74133367019cSmrg			       actual_type_return,
74143367019cSmrg			       actual_format_return,
74153367019cSmrg			       nitems_return,
74163367019cSmrg			       bytes_after_return,
74173367019cSmrg			       prop_return) == Success
74183367019cSmrg	    && x11_errors == 0) {
74193367019cSmrg	    result = True;
74203367019cSmrg	}
74213367019cSmrg	XSetErrorHandler(save);
74223367019cSmrg    }
74233367019cSmrg    return result;
74243367019cSmrg}
74253367019cSmrg
74263367019cSmrgvoid
74273367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
74283367019cSmrg{
74293367019cSmrg    Display *dpy = XtDisplay(toplevel);
74303367019cSmrg    XWindowAttributes attrs;
74313367019cSmrg
74323367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
74333367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
74343367019cSmrg	XtermWidget xw = term;
74353367019cSmrg	TScreen *screen = TScreenOf(xw);
74363367019cSmrg
74373367019cSmrg	XtRealizeWidget(toplevel);
74383367019cSmrg
74393367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
74403367019cSmrg	       XtWindow(toplevel),
74413367019cSmrg	       winToEmbedInto));
74423367019cSmrg	XReparentWindow(dpy,
74433367019cSmrg			XtWindow(toplevel),
74443367019cSmrg			winToEmbedInto, 0, 0);
74453367019cSmrg
74463367019cSmrg	screen->embed_high = (Dimension) attrs.height;
74473367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
74483367019cSmrg    }
74493367019cSmrg}
745094644356Smrg
745194644356Smrgvoid
745294644356Smrgfree_string(String value)
745394644356Smrg{
745494644356Smrg    free((void *) value);
745594644356Smrg}
7456dfb07bc7Smrg
7457dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
7458d4fba8b9Smrgint
745950027b5bSmrgupdate_winsize(TScreen *screen, int rows, int cols, int height, int width)
7460dfb07bc7Smrg{
7461d4fba8b9Smrg    int code = -1;
7462dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
7463d4fba8b9Smrg    static int last_rows = -1;
7464d4fba8b9Smrg    static int last_cols = -1;
7465636d5e9fSmrg    static int last_high = -1;
7466636d5e9fSmrg    static int last_wide = -1;
7467636d5e9fSmrg
7468636d5e9fSmrg    TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n",
7469636d5e9fSmrg	   last_rows, last_cols, last_high, last_wide,
7470636d5e9fSmrg	   rows, cols, height, width));
7471dfb07bc7Smrg
7472636d5e9fSmrg    if (rows != last_rows
7473636d5e9fSmrg	|| cols != last_cols
7474636d5e9fSmrg	|| last_high != height
7475636d5e9fSmrg	|| last_wide != width) {
7476d4fba8b9Smrg	TTYSIZE_STRUCT ts;
7477d4fba8b9Smrg
7478d4fba8b9Smrg	last_rows = rows;
7479d4fba8b9Smrg	last_cols = cols;
7480636d5e9fSmrg	last_high = height;
7481636d5e9fSmrg	last_wide = width;
748250027b5bSmrg#if OPT_STATUS_LINE
748350027b5bSmrg	if (IsStatusShown(screen)) {
748450027b5bSmrg	    ++rows;
748550027b5bSmrg	    height += FontHeight(screen);
748650027b5bSmrg	    TRACE(("... account for status-line -> %dx%d (%dx%d)\n",
748750027b5bSmrg		   rows, cols, height, width));
748850027b5bSmrg	}
748950027b5bSmrg#endif
7490d4fba8b9Smrg	setup_winsize(ts, rows, cols, height, width);
749150027b5bSmrg	TRACE_RC(code, SET_TTYSIZE(screen->respond, ts));
7492d4fba8b9Smrg	trace_winsize(ts, "from SET_TTYSIZE");
7493d4fba8b9Smrg    }
7494dfb07bc7Smrg#endif
7495dfb07bc7Smrg
7496dfb07bc7Smrg    (void) rows;
7497dfb07bc7Smrg    (void) cols;
7498dfb07bc7Smrg    (void) height;
7499dfb07bc7Smrg    (void) width;
7500d4fba8b9Smrg
7501d4fba8b9Smrg    return code;
7502dfb07bc7Smrg}
7503dfb07bc7Smrg
7504dfb07bc7Smrg/*
7505dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
7506dfb07bc7Smrg * manipulation 18 and 19.
7507dfb07bc7Smrg */
7508dfb07bc7Smrgvoid
7509dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
7510dfb07bc7Smrg{
7511dfb07bc7Smrg#if OPT_TEK4014
7512dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
7513dfb07bc7Smrg#endif
7514dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
7515dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
7516dfb07bc7Smrg
7517dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
751850027b5bSmrg	    update_winsize(screen,
7519dfb07bc7Smrg			   MaxRows(screen),
7520dfb07bc7Smrg			   MaxCols(screen),
7521dfb07bc7Smrg			   Height(screen),
7522dfb07bc7Smrg			   Width(screen));
7523dfb07bc7Smrg	}
7524dfb07bc7Smrg}
7525d4fba8b9Smrg
7526d4fba8b9Smrg#if OPT_XTERM_SGR
7527d4fba8b9Smrg
7528d4fba8b9Smrg#if OPT_TRACE
7529d4fba8b9Smrgstatic char *
7530d4fba8b9SmrgtraceIFlags(IFlags flags)
7531d4fba8b9Smrg{
7532d4fba8b9Smrg    static char result[1000];
7533d4fba8b9Smrg    result[0] = '\0';
7534d4fba8b9Smrg#define DATA(name) if (flags & name) { strcat(result, " " #name); }
7535d4fba8b9Smrg    DATA(INVERSE);
7536d4fba8b9Smrg    DATA(UNDERLINE);
7537d4fba8b9Smrg    DATA(BOLD);
7538d4fba8b9Smrg    DATA(BLINK);
7539d4fba8b9Smrg    DATA(INVISIBLE);
7540d4fba8b9Smrg    DATA(BG_COLOR);
7541d4fba8b9Smrg    DATA(FG_COLOR);
7542d4fba8b9Smrg
7543d4fba8b9Smrg#if OPT_WIDE_ATTRS
7544d4fba8b9Smrg    DATA(ATR_FAINT);
7545d4fba8b9Smrg    DATA(ATR_ITALIC);
7546d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7547d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7548d4fba8b9Smrg    DATA(ATR_DIRECT_FG);
7549d4fba8b9Smrg    DATA(ATR_DIRECT_BG);
7550d4fba8b9Smrg#endif
7551d4fba8b9Smrg#undef DATA
7552d4fba8b9Smrg    return result;
7553d4fba8b9Smrg}
7554d4fba8b9Smrg
7555d4fba8b9Smrgstatic char *
7556d4fba8b9SmrgtraceIStack(unsigned flags)
7557d4fba8b9Smrg{
7558d4fba8b9Smrg    static char result[1000];
7559d4fba8b9Smrg    result[0] = '\0';
7560d4fba8b9Smrg#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7561d4fba8b9Smrg    DATA(INVERSE);
7562d4fba8b9Smrg    DATA(UNDERLINE);
7563d4fba8b9Smrg    DATA(BOLD);
7564d4fba8b9Smrg    DATA(BLINK);
7565d4fba8b9Smrg    DATA(INVISIBLE);
7566d4fba8b9Smrg#if OPT_ISO_COLORS
7567d4fba8b9Smrg    DATA(BG_COLOR);
7568d4fba8b9Smrg    DATA(FG_COLOR);
7569d4fba8b9Smrg#endif
7570d4fba8b9Smrg
7571d4fba8b9Smrg#if OPT_WIDE_ATTRS
7572d4fba8b9Smrg    DATA(ATR_FAINT);
7573d4fba8b9Smrg    DATA(ATR_ITALIC);
7574d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7575d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7576d4fba8b9Smrg    /* direct-colors are a special case of ISO-colors (see above) */
7577d4fba8b9Smrg#endif
7578d4fba8b9Smrg#undef DATA
7579d4fba8b9Smrg    return result;
7580d4fba8b9Smrg}
7581d4fba8b9Smrg#endif
7582d4fba8b9Smrg
7583d4fba8b9Smrgvoid
7584d4fba8b9SmrgxtermPushSGR(XtermWidget xw, int value)
7585d4fba8b9Smrg{
7586d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7587d4fba8b9Smrg
7588d4fba8b9Smrg    TRACE(("xtermPushSGR %d mask %#x %s\n",
7589d4fba8b9Smrg	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
7590d4fba8b9Smrg
7591d4fba8b9Smrg    if (s->used < MAX_SAVED_SGR) {
7592d4fba8b9Smrg	s->stack[s->used].mask = (IFlags) value;
7593d4fba8b9Smrg#define PUSH_FLAG(name) \
7594d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7595d4fba8b9Smrg	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
7596d4fba8b9Smrg#define PUSH_DATA(name) \
7597d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7598d4fba8b9Smrg	    TRACE(("...may pop %s %d\n", #name, xw->name))
7599d4fba8b9Smrg	PUSH_FLAG(flags);
7600d4fba8b9Smrg#if OPT_ISO_COLORS
7601d4fba8b9Smrg	PUSH_DATA(sgr_foreground);
7602d4fba8b9Smrg	PUSH_DATA(sgr_background);
7603d4fba8b9Smrg	PUSH_DATA(sgr_38_xcolors);
7604d4fba8b9Smrg#endif
7605d4fba8b9Smrg    }
7606d4fba8b9Smrg    s->used++;
7607d4fba8b9Smrg}
7608d4fba8b9Smrg
7609d4fba8b9Smrg#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
7610d4fba8b9Smrg
7611d4fba8b9Smrgvoid
7612d4fba8b9SmrgxtermReportSGR(XtermWidget xw, XTermRect *value)
7613d4fba8b9Smrg{
7614d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
7615d4fba8b9Smrg    char reply[BUFSIZ];
7616d4fba8b9Smrg    CellData working;
7617d4fba8b9Smrg    int row, col;
7618d4fba8b9Smrg    Boolean first = True;
7619d4fba8b9Smrg
7620d4fba8b9Smrg    TRACE(("xtermReportSGR %d,%d - %d,%d\n",
7621d4fba8b9Smrg	   value->top, value->left,
7622d4fba8b9Smrg	   value->bottom, value->right));
7623d4fba8b9Smrg
7624d4fba8b9Smrg    memset(&working, 0, sizeof(working));
7625d4fba8b9Smrg    for (row = value->top - 1; row < value->bottom; ++row) {
7626d4fba8b9Smrg	LineData *ld = getLineData(screen, row);
7627d4fba8b9Smrg	if (ld == 0)
7628d4fba8b9Smrg	    continue;
7629d4fba8b9Smrg	for (col = value->left - 1; col < value->right; ++col) {
7630d4fba8b9Smrg	    if (first) {
7631d4fba8b9Smrg		first = False;
7632d4fba8b9Smrg		saveCellData(screen, &working, 0, ld, NULL, col);
7633d4fba8b9Smrg	    }
7634d4fba8b9Smrg	    working.attribs &= ld->attribs[col];
7635d4fba8b9Smrg#if OPT_ISO_COLORS
7636d4fba8b9Smrg	    if (working.attribs & FG_COLOR
7637d4fba8b9Smrg		&& GetCellColorFG(working.color)
7638d4fba8b9Smrg		!= GetCellColorFG(ld->color[col])) {
7639d4fba8b9Smrg		IAttrClr(working.attribs, FG_COLOR);
7640d4fba8b9Smrg	    }
7641d4fba8b9Smrg	    if (working.attribs & BG_COLOR
7642d4fba8b9Smrg		&& GetCellColorBG(working.color)
7643d4fba8b9Smrg		!= GetCellColorBG(ld->color[col])) {
7644d4fba8b9Smrg		IAttrClr(working.attribs, BG_COLOR);
7645d4fba8b9Smrg	    }
7646d4fba8b9Smrg#endif
7647d4fba8b9Smrg	}
7648d4fba8b9Smrg    }
7649d4fba8b9Smrg    xtermFormatSGR(xw, reply,
7650d4fba8b9Smrg		   working.attribs,
7651d4fba8b9Smrg		   GetCellColorFG(working.color),
7652d4fba8b9Smrg		   GetCellColorBG(working.color));
7653d4fba8b9Smrg    unparseputc1(xw, ANSI_CSI);
7654d4fba8b9Smrg    unparseputs(xw, reply);
7655d4fba8b9Smrg    unparseputc(xw, 'm');
7656d4fba8b9Smrg    unparse_end(xw);
7657d4fba8b9Smrg}
7658d4fba8b9Smrg
7659d4fba8b9Smrgvoid
7660d4fba8b9SmrgxtermPopSGR(XtermWidget xw)
7661d4fba8b9Smrg{
7662d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7663d4fba8b9Smrg
7664d4fba8b9Smrg    TRACE(("xtermPopSGR %d\n", s->used));
7665d4fba8b9Smrg
7666d4fba8b9Smrg    if (s->used > 0) {
7667d4fba8b9Smrg	if (s->used-- <= MAX_SAVED_SGR) {
7668d4fba8b9Smrg	    IFlags mask = s->stack[s->used].mask;
7669d4fba8b9Smrg	    Boolean changed = False;
7670d4fba8b9Smrg
7671d4fba8b9Smrg	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
7672d4fba8b9Smrg	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
7673d4fba8b9Smrg	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
7674d4fba8b9Smrg#define POP_FLAG(name) \
7675d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7676d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7677d4fba8b9Smrg		    changed = True; \
7678d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7679d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7680d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7681d4fba8b9Smrg		} \
7682d4fba8b9Smrg	    }
7683d4fba8b9Smrg#define POP_FLAG2(name,part) \
7684d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7685d4fba8b9Smrg	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
7686d4fba8b9Smrg		    changed = True; \
7687d4fba8b9Smrg		    UIntClr(xw->flags, part); \
7688d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
7689d4fba8b9Smrg		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
7690d4fba8b9Smrg		} \
7691d4fba8b9Smrg	    }
7692d4fba8b9Smrg#define POP_DATA(name,value) \
7693d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7694d4fba8b9Smrg	        Bool always = False; \
7695d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7696d4fba8b9Smrg		    always = changed = True; \
7697d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7698d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7699d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7700d4fba8b9Smrg		} \
7701d4fba8b9Smrg		if (always || (xw->value != s->stack[s->used].value)) { \
7702d4fba8b9Smrg		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
7703d4fba8b9Smrg		    xw->value = s->stack[s->used].value; \
7704d4fba8b9Smrg		    changed = True; \
7705d4fba8b9Smrg		} \
7706d4fba8b9Smrg	    }
7707d4fba8b9Smrg	    POP_FLAG(BOLD);
7708d4fba8b9Smrg	    POP_FLAG(UNDERLINE);
7709d4fba8b9Smrg	    POP_FLAG(BLINK);
7710d4fba8b9Smrg	    POP_FLAG(INVERSE);
7711d4fba8b9Smrg	    POP_FLAG(INVISIBLE);
7712d4fba8b9Smrg#if OPT_WIDE_ATTRS
7713d4fba8b9Smrg	    if (xBIT(psATR_ITALIC - 1) & mask) {
7714d4fba8b9Smrg		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
7715d4fba8b9Smrg	    }
7716d4fba8b9Smrg	    POP_FLAG(ATR_ITALIC);
7717d4fba8b9Smrg	    POP_FLAG(ATR_FAINT);
7718d4fba8b9Smrg	    POP_FLAG(ATR_STRIKEOUT);
7719d4fba8b9Smrg	    POP_FLAG(ATR_DBL_UNDER);
7720d4fba8b9Smrg#endif
7721d4fba8b9Smrg#if OPT_ISO_COLORS
7722d4fba8b9Smrg	    POP_DATA(FG_COLOR, sgr_foreground);
7723d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_background);
7724d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_38_xcolors);
7725d4fba8b9Smrg#if OPT_DIRECT_COLOR
7726d4fba8b9Smrg	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
7727d4fba8b9Smrg	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
7728d4fba8b9Smrg#endif
7729d4fba8b9Smrg	    if (changed) {
7730d4fba8b9Smrg		setExtendedColors(xw);
7731d4fba8b9Smrg	    }
7732d4fba8b9Smrg#else
7733d4fba8b9Smrg	    (void) changed;
7734d4fba8b9Smrg#endif
7735d4fba8b9Smrg	}
7736d4fba8b9Smrg#if OPT_ISO_COLORS
7737d4fba8b9Smrg	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
7738d4fba8b9Smrg	       traceIFlags(xw->flags),
7739d4fba8b9Smrg	       xw->sgr_foreground,
7740d4fba8b9Smrg	       xw->sgr_background,
7741d4fba8b9Smrg	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
7742d4fba8b9Smrg#else
7743d4fba8b9Smrg	TRACE(("xtermP -> flags%s\n",
7744d4fba8b9Smrg	       traceIFlags(xw->flags)));
7745d4fba8b9Smrg#endif
7746d4fba8b9Smrg    }
7747d4fba8b9Smrg}
7748d4fba8b9Smrg
7749d4fba8b9Smrg#if OPT_ISO_COLORS
7750d4fba8b9Smrgstatic ColorSlot *
7751d4fba8b9SmrgallocColorSlot(XtermWidget xw, int slot)
7752d4fba8b9Smrg{
7753d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7754d4fba8b9Smrg    ColorSlot *result = NULL;
7755d4fba8b9Smrg
7756d4fba8b9Smrg    if (slot >= 0 && slot < MAX_SAVED_SGR) {
7757d4fba8b9Smrg	if (s->palettes[slot] == NULL) {
7758d4fba8b9Smrg	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
7759d4fba8b9Smrg						     sizeof(ColorSlot)
7760d4fba8b9Smrg						     + (sizeof(ColorRes)
7761d4fba8b9Smrg							* MAXCOLORS));
7762d4fba8b9Smrg	}
7763d4fba8b9Smrg	result = s->palettes[slot];
7764d4fba8b9Smrg    }
7765d4fba8b9Smrg    return result;
7766d4fba8b9Smrg}
7767d4fba8b9Smrg
7768d4fba8b9Smrgstatic void
7769d4fba8b9SmrgpopOldColors(XtermWidget xw, ScrnColors * source)
7770d4fba8b9Smrg{
7771d4fba8b9Smrg    Boolean changed = False;
7772d4fba8b9Smrg    ScrnColors *target = xw->work.oldColors;
7773d4fba8b9Smrg
7774d4fba8b9Smrg    if (source->which != target->which) {
7775d4fba8b9Smrg	changed = True;
7776d4fba8b9Smrg    } else {
7777d4fba8b9Smrg	int n;
7778d4fba8b9Smrg	for (n = 0; n < NCOLORS; ++n) {
7779d4fba8b9Smrg	    if (COLOR_DEFINED(source, n)) {
7780d4fba8b9Smrg		if (COLOR_DEFINED(target, n)) {
7781d4fba8b9Smrg		    if (source->colors[n] != target->colors[n]) {
7782d4fba8b9Smrg			changed = True;
7783d4fba8b9Smrg			break;
7784d4fba8b9Smrg		    }
7785d4fba8b9Smrg		} else {
7786d4fba8b9Smrg		    changed = True;
7787d4fba8b9Smrg		    break;
7788d4fba8b9Smrg		}
7789d4fba8b9Smrg	    } else if (COLOR_DEFINED(target, n)) {
7790d4fba8b9Smrg		changed = True;
7791d4fba8b9Smrg		break;
7792d4fba8b9Smrg	    }
7793d4fba8b9Smrg	}
7794d4fba8b9Smrg    }
7795d4fba8b9Smrg    if (changed) {
7796d4fba8b9Smrg	ChangeColors(xw, source);
7797d4fba8b9Smrg	UpdateOldColors(xw, source);
7798d4fba8b9Smrg    }
7799d4fba8b9Smrg}
7800d4fba8b9Smrg#endif /* OPT_ISO_COLORS */
7801d4fba8b9Smrg
7802d4fba8b9Smrg#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
7803d4fba8b9Smrg#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
7804d4fba8b9Smrg
7805d4fba8b9Smrg/*
7806d4fba8b9Smrg * By default, a "push" increments the stack after copying to the current
7807d4fba8b9Smrg * slot.  But a specific target allows one to copy into a specific slot.
7808d4fba8b9Smrg */
7809d4fba8b9Smrgvoid
7810d4fba8b9SmrgxtermPushColors(XtermWidget xw, int value)
7811d4fba8b9Smrg{
7812d4fba8b9Smrg#if OPT_ISO_COLORS
7813d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7814d4fba8b9Smrg    int pushed = s->used;
7815d4fba8b9Smrg    int actual = (value <= 0) ? pushed : (value - 1);
7816d4fba8b9Smrg
7817d4fba8b9Smrg    TRACE(("xtermPushColors %d:%d\n", actual, pushed));
7818d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7819d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7820d4fba8b9Smrg	ColorSlot *palette;
7821d4fba8b9Smrg
7822d4fba8b9Smrg	if ((palette = allocColorSlot(xw, actual)) != NULL) {
7823d4fba8b9Smrg	    GetColors(xw, &(palette->base));
7824d4fba8b9Smrg	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
7825d4fba8b9Smrg	    if (value < 0) {
7826d4fba8b9Smrg		s->used++;
7827d4fba8b9Smrg		if (s->last < s->used)
7828d4fba8b9Smrg		    s->last = s->used;
7829d4fba8b9Smrg	    } else {
7830d4fba8b9Smrg		s->used = value;
7831d4fba8b9Smrg	    }
7832d4fba8b9Smrg	}
7833d4fba8b9Smrg    }
7834d4fba8b9Smrg#else
7835d4fba8b9Smrg    (void) xw;
7836d4fba8b9Smrg    (void) value;
7837d4fba8b9Smrg#endif
7838d4fba8b9Smrg}
7839d4fba8b9Smrg
7840d4fba8b9Smrgvoid
7841d4fba8b9SmrgxtermPopColors(XtermWidget xw, int value)
7842d4fba8b9Smrg{
7843d4fba8b9Smrg#if OPT_ISO_COLORS
7844d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7845d4fba8b9Smrg    int popped = (s->used - 1);
7846d4fba8b9Smrg    int actual = (value <= 0) ? popped : (value - 1);
7847d4fba8b9Smrg
7848d4fba8b9Smrg    TRACE(("xtermPopColors %d:%d\n", actual, popped));
7849d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7850d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7851d4fba8b9Smrg	ColorSlot *palette;
7852d4fba8b9Smrg
7853d4fba8b9Smrg	if ((palette = s->palettes[actual]) != NULL) {
7854d4fba8b9Smrg	    Boolean changed = DiffColorSlot(screen->Acolors,
7855d4fba8b9Smrg					    palette->ansi,
7856d4fba8b9Smrg					    MAXCOLORS);
7857d4fba8b9Smrg
7858d4fba8b9Smrg	    GetOldColors(xw);
7859d4fba8b9Smrg	    popOldColors(xw, &(palette->base));
7860d4fba8b9Smrg	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
7861d4fba8b9Smrg	    s->used = actual;
7862d4fba8b9Smrg	    if (changed)
7863d4fba8b9Smrg		xtermRepaint(xw);
7864d4fba8b9Smrg	}
7865d4fba8b9Smrg    }
7866d4fba8b9Smrg#else
7867d4fba8b9Smrg    (void) xw;
7868d4fba8b9Smrg    (void) value;
7869d4fba8b9Smrg#endif
7870d4fba8b9Smrg}
7871d4fba8b9Smrg
7872d4fba8b9Smrgvoid
7873d4fba8b9SmrgxtermReportColors(XtermWidget xw)
7874d4fba8b9Smrg{
7875d4fba8b9Smrg    ANSI reply;
7876d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7877d4fba8b9Smrg
7878d4fba8b9Smrg    memset(&reply, 0, sizeof(reply));
7879d4fba8b9Smrg    reply.a_type = ANSI_CSI;
7880d4fba8b9Smrg    reply.a_pintro = '?';
7881d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->used;
7882d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->last;
7883d4fba8b9Smrg    reply.a_inters = '#';
7884d4fba8b9Smrg    reply.a_final = 'Q';
7885d4fba8b9Smrg    unparseseq(xw, &reply);
7886d4fba8b9Smrg}
7887d4fba8b9Smrg#endif /* OPT_XTERM_SGR */
7888