misc.c revision 728ff447
18f44fb3bSmrg/* $XTermId: misc.c,v 1.979 2021/03/24 00:27:48 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4d4fba8b9Smrg * Copyright 1999-2020,2021 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,
1847d4fba8b9Smrg			    &prop_return)
1848d4fba8b9Smrg	    && prop_return != 0
1849d4fba8b9Smrg	    && actual_return_type == requested_type
1850d4fba8b9Smrg	    && actual_format_return == 32) {
1851d4fba8b9Smrg	    unsigned long n;
1852d4fba8b9Smrg	    for (n = 0; n < nitems_return; ++n) {
1853d4fba8b9Smrg		unsigned long check = (((unsigned long *)
1854d4fba8b9Smrg					(void *) prop_return)[n]);
1855d4fba8b9Smrg		if (check == is_hidden) {
1856d4fba8b9Smrg		    result = True;
1857d4fba8b9Smrg		    break;
1858d4fba8b9Smrg		}
1859d4fba8b9Smrg	    }
1860d4fba8b9Smrg	}
1861d4fba8b9Smrg    }
1862d4fba8b9Smrg    TRACE(("...window %#lx is%s iconified\n",
1863d4fba8b9Smrg	   target,
1864d4fba8b9Smrg	   result ? "" : " not"));
1865d4fba8b9Smrg    return result;
1866d4fba8b9Smrg}
1867d4fba8b9Smrg
1868d522f475Smrg#if OPT_MAXIMIZE
1869d522f475Smrg/*ARGSUSED*/
1870d522f475Smrgvoid
1871b7c89284SsnjHandleDeIconify(Widget w,
18729a64e1c5Smrg		XEvent *event GCC_UNUSED,
1873fa3f02f3Smrg		String *params GCC_UNUSED,
1874d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1875d522f475Smrg{
1876b7c89284Ssnj    XtermWidget xw;
1877b7c89284Ssnj
1878b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1879d4fba8b9Smrg	xtermDeiconify(xw);
1880d522f475Smrg    }
1881d522f475Smrg}
1882d522f475Smrg
1883d522f475Smrg/*ARGSUSED*/
1884d522f475Smrgvoid
1885b7c89284SsnjHandleIconify(Widget w,
18869a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1887fa3f02f3Smrg	      String *params GCC_UNUSED,
1888d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1889d522f475Smrg{
1890b7c89284Ssnj    XtermWidget xw;
1891b7c89284Ssnj
1892b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1893d4fba8b9Smrg	xtermIconify(xw);
1894d522f475Smrg    }
1895d522f475Smrg}
1896d522f475Smrg
1897d522f475Smrgint
1898c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1899d522f475Smrg{
1900c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1901d522f475Smrg    XSizeHints hints;
1902d522f475Smrg    long supp = 0;
1903d522f475Smrg    Window root_win;
1904d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1905d522f475Smrg    int root_y = -1;
1906d522f475Smrg    unsigned root_border;
1907d522f475Smrg    unsigned root_depth;
19083367019cSmrg    int code;
1909d522f475Smrg
1910d522f475Smrg    if (XGetGeometry(screen->display,
1911c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1912d522f475Smrg		     &root_win,
1913d522f475Smrg		     &root_x,
1914d522f475Smrg		     &root_y,
1915d522f475Smrg		     width,
1916d522f475Smrg		     height,
1917d522f475Smrg		     &root_border,
1918d522f475Smrg		     &root_depth)) {
1919d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1920d522f475Smrg	       root_x,
1921d522f475Smrg	       root_y,
1922d522f475Smrg	       *width,
1923d522f475Smrg	       *height,
1924d522f475Smrg	       root_border));
1925d522f475Smrg
1926d522f475Smrg	*width -= (root_border * 2);
1927d522f475Smrg	*height -= (root_border * 2);
1928d522f475Smrg
1929d522f475Smrg	hints.flags = PMaxSize;
1930d522f475Smrg	if (XGetWMNormalHints(screen->display,
1931c219fbebSmrg			      VShellWindow(xw),
1932d522f475Smrg			      &hints,
1933d522f475Smrg			      &supp)
1934d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1935d522f475Smrg
1936d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1937d522f475Smrg		   hints.max_width,
1938d522f475Smrg		   hints.max_height));
1939d522f475Smrg
1940d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1941b7c89284Ssnj		*width = (unsigned) hints.max_width;
1942d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1943b7c89284Ssnj		*height = (unsigned) hints.max_height;
1944d522f475Smrg	}
19453367019cSmrg	code = 1;
19463367019cSmrg    } else {
19473367019cSmrg	*width = 0;
19483367019cSmrg	*height = 0;
19493367019cSmrg	code = 0;
1950d522f475Smrg    }
19513367019cSmrg    return code;
1952d522f475Smrg}
1953d522f475Smrg
1954d522f475Smrgvoid
1955c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1956d522f475Smrg{
1957c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1958d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1959d4fba8b9Smrg    unsigned root_width = 0, root_height = 0;
19603367019cSmrg    Boolean success = False;
1961d522f475Smrg
19623367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
19633367019cSmrg	   maximize,
19643367019cSmrg	   (maximize
19653367019cSmrg	    ? "maximize"
19663367019cSmrg	    : "restore")));
1967d522f475Smrg
19683367019cSmrg    /*
19693367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
19703367019cSmrg     * as well as the estimated root-window size.
19713367019cSmrg     */
19723367019cSmrg    if (maximize
19733367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
19743367019cSmrg	&& xtermGetWinAttrs(screen->display,
19753367019cSmrg			    WMFrameWindow(xw),
19763367019cSmrg			    &wm_attrs)
19773367019cSmrg	&& xtermGetWinAttrs(screen->display,
19783367019cSmrg			    VShellWindow(xw),
19793367019cSmrg			    &vshell_attrs)) {
19803367019cSmrg
19813367019cSmrg	if (screen->restore_data != True
19823367019cSmrg	    || screen->restore_width != root_width
19833367019cSmrg	    || screen->restore_height != root_height) {
19843367019cSmrg	    screen->restore_data = True;
1985d4fba8b9Smrg	    screen->restore_x = wm_attrs.x;
1986d4fba8b9Smrg	    screen->restore_y = wm_attrs.y;
19873367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
19883367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
19893367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1990d522f475Smrg		   screen->restore_x,
1991d522f475Smrg		   screen->restore_y,
1992d522f475Smrg		   screen->restore_width,
1993d522f475Smrg		   screen->restore_height));
19943367019cSmrg	}
1995d522f475Smrg
19963367019cSmrg	/* subtract wm decoration dimensions */
1997d4fba8b9Smrg	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
1998d4fba8b9Smrg	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
19993367019cSmrg	success = True;
20003367019cSmrg    } else if (screen->restore_data) {
20013367019cSmrg	success = True;
20023367019cSmrg	maximize = 0;
20033367019cSmrg    }
20043367019cSmrg
20053367019cSmrg    if (success) {
20063367019cSmrg	switch (maximize) {
20073367019cSmrg	case 3:
20083367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
20093367019cSmrg	    break;
20103367019cSmrg	case 2:
20113367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
20123367019cSmrg	    break;
20133367019cSmrg	case 1:
20143367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
2015d4fba8b9Smrg	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
2016d4fba8b9Smrg		   0,
2017d4fba8b9Smrg		   0,
2018d4fba8b9Smrg		   root_width,
2019d4fba8b9Smrg		   root_height));
20203367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
2021d4fba8b9Smrg			      0,	/* x */
2022d4fba8b9Smrg			      0,	/* y */
20233367019cSmrg			      root_width,
20243367019cSmrg			      root_height);
20253367019cSmrg	    break;
20263367019cSmrg
20273367019cSmrg	default:
20283367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
20293367019cSmrg	    if (screen->restore_data) {
20303367019cSmrg		screen->restore_data = False;
20313367019cSmrg
2032d4fba8b9Smrg		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
20333367019cSmrg		       screen->restore_x,
20343367019cSmrg		       screen->restore_y,
20353367019cSmrg		       screen->restore_width,
20363367019cSmrg		       screen->restore_height));
20373367019cSmrg
20383367019cSmrg		XMoveResizeWindow(screen->display,
20393367019cSmrg				  VShellWindow(xw),
20403367019cSmrg				  screen->restore_x,
20413367019cSmrg				  screen->restore_y,
20423367019cSmrg				  screen->restore_width,
20433367019cSmrg				  screen->restore_height);
20443367019cSmrg	    }
20453367019cSmrg	    break;
2046d522f475Smrg	}
2047d522f475Smrg    }
2048d522f475Smrg}
2049d522f475Smrg
2050d522f475Smrg/*ARGSUSED*/
2051d522f475Smrgvoid
2052b7c89284SsnjHandleMaximize(Widget w,
20539a64e1c5Smrg	       XEvent *event GCC_UNUSED,
2054fa3f02f3Smrg	       String *params GCC_UNUSED,
2055d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
2056d522f475Smrg{
2057b7c89284Ssnj    XtermWidget xw;
2058b7c89284Ssnj
2059b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2060b7c89284Ssnj	RequestMaximize(xw, 1);
2061d522f475Smrg    }
2062d522f475Smrg}
2063d522f475Smrg
2064d522f475Smrg/*ARGSUSED*/
2065d522f475Smrgvoid
2066b7c89284SsnjHandleRestoreSize(Widget w,
20679a64e1c5Smrg		  XEvent *event GCC_UNUSED,
2068fa3f02f3Smrg		  String *params GCC_UNUSED,
2069d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
2070d522f475Smrg{
2071b7c89284Ssnj    XtermWidget xw;
2072b7c89284Ssnj
2073b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2074b7c89284Ssnj	RequestMaximize(xw, 0);
2075d522f475Smrg    }
2076d522f475Smrg}
2077d522f475Smrg#endif /* OPT_MAXIMIZE */
2078d522f475Smrg
2079d522f475Smrgvoid
2080d522f475SmrgRedraw(void)
2081d522f475Smrg{
2082d4fba8b9Smrg    XtermWidget xw = term;
2083d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2084d522f475Smrg    XExposeEvent event;
2085d522f475Smrg
2086d522f475Smrg    TRACE(("Redraw\n"));
2087d522f475Smrg
2088d522f475Smrg    event.type = Expose;
2089d522f475Smrg    event.display = screen->display;
2090d522f475Smrg    event.x = 0;
2091d522f475Smrg    event.y = 0;
2092d522f475Smrg    event.count = 0;
2093d522f475Smrg
2094d522f475Smrg    if (VWindow(screen)) {
2095d522f475Smrg	event.window = VWindow(screen);
2096d4fba8b9Smrg	event.width = xw->core.width;
2097d4fba8b9Smrg	event.height = xw->core.height;
2098d4fba8b9Smrg	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
2099d4fba8b9Smrg						     (XEvent *) &event,
2100d4fba8b9Smrg						     NULL);
2101d522f475Smrg	if (ScrollbarWidth(screen)) {
2102d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
21039a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
2104d522f475Smrg	}
2105d522f475Smrg    }
2106d522f475Smrg#if OPT_TEK4014
2107d4fba8b9Smrg    if (TEK4014_SHOWN(xw)) {
2108cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
2109d522f475Smrg	event.window = TWindow(tekscr);
2110d522f475Smrg	event.width = tekWidget->core.width;
2111d522f475Smrg	event.height = tekWidget->core.height;
21129a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
2113d522f475Smrg    }
2114d522f475Smrg#endif
2115d522f475Smrg}
2116d522f475Smrg
2117d522f475Smrg#ifdef VMS
2118d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
2119d522f475Smrg#else
2120d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
2121d522f475Smrg#endif
2122d522f475Smrg
2123d522f475Smrgvoid
2124d522f475Smrgtimestamp_filename(char *dst, const char *src)
2125d522f475Smrg{
2126d522f475Smrg    time_t tstamp;
2127d522f475Smrg    struct tm *tstruct;
2128d522f475Smrg
2129d522f475Smrg    tstamp = time((time_t *) 0);
2130d522f475Smrg    tstruct = localtime(&tstamp);
2131d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
2132d522f475Smrg	    src,
21333367019cSmrg	    (int) tstruct->tm_year + 1900,
2134d522f475Smrg	    tstruct->tm_mon + 1,
2135d522f475Smrg	    tstruct->tm_mday,
2136d522f475Smrg	    tstruct->tm_hour,
2137d522f475Smrg	    tstruct->tm_min,
2138d522f475Smrg	    tstruct->tm_sec);
2139d522f475Smrg}
2140d522f475Smrg
2141d4fba8b9SmrgFILE *
2142d4fba8b9Smrgcreate_printfile(XtermWidget xw, const char *suffix)
2143d4fba8b9Smrg{
2144d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2145d4fba8b9Smrg    char fname[1024];
2146d4fba8b9Smrg    int fd;
2147d4fba8b9Smrg    FILE *fp;
2148d4fba8b9Smrg
2149d4fba8b9Smrg#ifdef VMS
2150d4fba8b9Smrg    sprintf(fname, "sys$scratch:xterm%s", suffix);
2151d4fba8b9Smrg#elif defined(HAVE_STRFTIME)
2152d4fba8b9Smrg    {
2153d4fba8b9Smrg	char format[1024];
2154d4fba8b9Smrg	time_t now;
2155d4fba8b9Smrg	struct tm *ltm;
2156d4fba8b9Smrg
2157d4fba8b9Smrg	now = time((time_t *) 0);
2158d4fba8b9Smrg	ltm = localtime(&now);
2159d4fba8b9Smrg
2160d4fba8b9Smrg	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2161d4fba8b9Smrg	if (strftime(fname, sizeof fname, format, ltm) == 0) {
2162d4fba8b9Smrg	    sprintf(fname, "xterm%s", suffix);
2163d4fba8b9Smrg	}
2164d4fba8b9Smrg    }
2165d4fba8b9Smrg#else
2166d4fba8b9Smrg    sprintf(fname, "xterm%s", suffix);
2167d4fba8b9Smrg#endif
2168d4fba8b9Smrg    fd = open_userfile(screen->uid, screen->gid, fname, False);
2169d4fba8b9Smrg    fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2170d4fba8b9Smrg    return fp;
2171d4fba8b9Smrg}
2172d4fba8b9Smrg
2173d522f475Smrgint
2174d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2175d522f475Smrg{
2176d522f475Smrg    int fd;
2177d522f475Smrg    struct stat sb;
2178d522f475Smrg
2179d522f475Smrg#ifdef VMS
2180d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
2181d522f475Smrg	int the_error = errno;
21823367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
21833367019cSmrg		     path,
21843367019cSmrg		     the_error,
21853367019cSmrg		     SysErrorMsg(the_error));
2186d522f475Smrg	return -1;
2187d522f475Smrg    }
2188d522f475Smrg    chown(path, uid, gid);
2189d522f475Smrg#else
2190d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
2191d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
2192d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2193d522f475Smrg	int the_error = errno;
21943367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
21953367019cSmrg		     path,
21963367019cSmrg		     the_error,
21973367019cSmrg		     SysErrorMsg(the_error));
2198d522f475Smrg	return -1;
2199d522f475Smrg    }
2200d522f475Smrg#endif
2201d522f475Smrg
2202d522f475Smrg    /*
2203d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
2204d522f475Smrg     * we do any damage, and that it is not world-writable.
2205d522f475Smrg     */
2206d522f475Smrg    if (fstat(fd, &sb) < 0
2207d522f475Smrg	|| sb.st_uid != uid
2208d522f475Smrg	|| (sb.st_mode & 022) != 0) {
22093367019cSmrg	xtermWarning("you do not own %s\n", path);
2210d522f475Smrg	close(fd);
2211d522f475Smrg	return -1;
2212d522f475Smrg    }
2213d522f475Smrg    return fd;
2214d522f475Smrg}
2215d522f475Smrg
2216d522f475Smrg#ifndef VMS
2217d522f475Smrg/*
2218d522f475Smrg * Create a file only if we could with the permissions of the real user id.
2219d522f475Smrg * We could emulate this with careful use of access() and following
2220d522f475Smrg * symbolic links, but that is messy and has race conditions.
2221d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2222d522f475Smrg * being available.
2223d522f475Smrg *
2224d522f475Smrg * Note: When called for user logging, we have ensured that the real and
2225d522f475Smrg * effective user ids are the same, so this remains as a convenience function
2226d522f475Smrg * for the debug logs.
2227d522f475Smrg *
2228d522f475Smrg * Returns
2229d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
2230d522f475Smrg *	-1 on error, e.g., cannot fork
2231d522f475Smrg *	 0 otherwise.
2232d522f475Smrg */
2233d522f475Smrgint
2234712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2235d522f475Smrg{
2236d522f475Smrg    int fd;
2237d522f475Smrg    pid_t pid;
2238d522f475Smrg    int retval = 0;
2239d522f475Smrg    int childstat = 0;
2240d522f475Smrg#ifndef HAVE_WAITPID
2241d522f475Smrg    int waited;
22423367019cSmrg    void (*chldfunc) (int);
2243d522f475Smrg
2244d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
2245d522f475Smrg#endif /* HAVE_WAITPID */
2246d522f475Smrg
2247d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2248d522f475Smrg	   (int) uid, (int) geteuid(),
2249d522f475Smrg	   (int) gid, (int) getegid(),
2250d522f475Smrg	   append,
2251d522f475Smrg	   pathname,
2252d522f475Smrg	   mode));
2253d522f475Smrg
2254d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
2255d522f475Smrg	fd = open(pathname,
2256d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2257d522f475Smrg		  mode);
2258d522f475Smrg	if (fd >= 0)
2259d522f475Smrg	    close(fd);
2260d522f475Smrg	return (fd >= 0);
2261d522f475Smrg    }
2262d522f475Smrg
2263d522f475Smrg    pid = fork();
2264d522f475Smrg    switch (pid) {
2265d522f475Smrg    case 0:			/* child */
2266d522f475Smrg	if (setgid(gid) == -1
2267d522f475Smrg	    || setuid(uid) == -1) {
2268d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
2269d522f475Smrg	    retval = 1;
2270d522f475Smrg	} else {
2271d522f475Smrg	    fd = open(pathname,
2272d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2273d522f475Smrg		      mode);
2274d522f475Smrg	    if (fd >= 0) {
2275d522f475Smrg		close(fd);
2276d522f475Smrg		retval = 0;
2277d522f475Smrg	    } else {
2278d522f475Smrg		retval = 1;
2279d522f475Smrg	    }
2280d522f475Smrg	}
2281d522f475Smrg	_exit(retval);
2282d522f475Smrg	/* NOTREACHED */
2283d522f475Smrg    case -1:			/* error */
2284d522f475Smrg	return retval;
2285d522f475Smrg    default:			/* parent */
2286d522f475Smrg#ifdef HAVE_WAITPID
2287d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
2288d522f475Smrg#ifdef EINTR
2289d522f475Smrg	    if (errno == EINTR)
2290d522f475Smrg		continue;
2291d522f475Smrg#endif /* EINTR */
2292d522f475Smrg#ifdef ERESTARTSYS
2293d522f475Smrg	    if (errno == ERESTARTSYS)
2294d522f475Smrg		continue;
2295d522f475Smrg#endif /* ERESTARTSYS */
2296d522f475Smrg	    break;
2297d522f475Smrg	}
2298d522f475Smrg#else /* HAVE_WAITPID */
2299d522f475Smrg	waited = wait(&childstat);
2300d522f475Smrg	signal(SIGCHLD, chldfunc);
2301d522f475Smrg	/*
2302d522f475Smrg	   Since we had the signal handler uninstalled for a while,
2303d522f475Smrg	   we might have missed the termination of our screen child.
2304d522f475Smrg	   If we can check for this possibility without hanging, do so.
2305d522f475Smrg	 */
2306d522f475Smrg	do
2307cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
23083367019cSmrg		NormalExit();
2309d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
2310d522f475Smrg#endif /* HAVE_WAITPID */
2311d522f475Smrg#ifndef WIFEXITED
2312d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
2313d522f475Smrg#endif
2314d522f475Smrg	if (WIFEXITED(childstat))
2315d522f475Smrg	    retval = 1;
2316d522f475Smrg	return retval;
2317d522f475Smrg    }
2318d522f475Smrg}
2319d522f475Smrg#endif /* !VMS */
2320d522f475Smrg
2321d522f475Smrgint
2322fa3f02f3SmrgxtermResetIds(TScreen *screen)
2323d522f475Smrg{
2324d522f475Smrg    int result = 0;
2325d522f475Smrg    if (setgid(screen->gid) == -1) {
23263367019cSmrg	xtermWarning("unable to reset group-id\n");
2327d522f475Smrg	result = -1;
2328d522f475Smrg    }
2329d522f475Smrg    if (setuid(screen->uid) == -1) {
23303367019cSmrg	xtermWarning("unable to reset user-id\n");
2331d522f475Smrg	result = -1;
2332d522f475Smrg    }
2333d522f475Smrg    return result;
2334d522f475Smrg}
2335d522f475Smrg
2336d522f475Smrg#ifdef ALLOWLOGGING
2337d522f475Smrg
2338d522f475Smrg/*
2339d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
2340d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
2341d522f475Smrg */
2342d522f475Smrg
2343d522f475Smrg#ifdef ALLOWLOGFILEEXEC
23443367019cSmrgstatic void
2345d4fba8b9Smrghandle_SIGPIPE(int sig GCC_UNUSED)
2346d522f475Smrg{
2347cd3331d0Smrg    XtermWidget xw = term;
2348cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2349d522f475Smrg
23503367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2351d522f475Smrg#ifdef SYSV
2352d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2353d522f475Smrg#endif /* SYSV */
2354d522f475Smrg    if (screen->logging)
2355cd3331d0Smrg	CloseLog(xw);
2356d522f475Smrg}
2357d4fba8b9Smrg
2358d4fba8b9Smrg/*
2359d4fba8b9Smrg * Open a command to pipe log data to it.
2360d4fba8b9Smrg * Warning, enabling this "feature" allows arbitrary programs
2361d4fba8b9Smrg * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2362d4fba8b9Smrg * done through escape sequences....  You have been warned.
2363d4fba8b9Smrg */
2364d4fba8b9Smrgstatic void
2365d4fba8b9SmrgStartLogExec(TScreen *screen)
2366d4fba8b9Smrg{
2367d4fba8b9Smrg    int pid;
2368d4fba8b9Smrg    int p[2];
2369d4fba8b9Smrg    static char *shell;
2370d4fba8b9Smrg    struct passwd pw;
2371d4fba8b9Smrg
2372d4fba8b9Smrg    if ((shell = x_getenv("SHELL")) == NULL) {
2373d4fba8b9Smrg
2374d4fba8b9Smrg	if (x_getpwuid(screen->uid, &pw)) {
2375d4fba8b9Smrg	    char *name = x_getlogin(screen->uid, &pw);
2376d4fba8b9Smrg	    if (*(pw.pw_shell)) {
2377d4fba8b9Smrg		shell = pw.pw_shell;
2378d4fba8b9Smrg	    }
2379d4fba8b9Smrg	    free(name);
2380d4fba8b9Smrg	}
2381d4fba8b9Smrg    }
2382d4fba8b9Smrg
2383d4fba8b9Smrg    if (shell == 0) {
2384d4fba8b9Smrg	static char dummy[] = "/bin/sh";
2385d4fba8b9Smrg	shell = dummy;
2386d4fba8b9Smrg    }
2387d4fba8b9Smrg
2388d4fba8b9Smrg    if (access(shell, X_OK) != 0) {
2389d4fba8b9Smrg	xtermPerror("Can't execute `%s'\n", shell);
2390d4fba8b9Smrg	return;
2391d4fba8b9Smrg    }
2392d4fba8b9Smrg
2393d4fba8b9Smrg    if (pipe(p) < 0) {
2394d4fba8b9Smrg	xtermPerror("Can't make a pipe connection\n");
2395d4fba8b9Smrg	return;
2396d4fba8b9Smrg    } else if ((pid = fork()) < 0) {
2397d4fba8b9Smrg	xtermPerror("Can't fork...\n");
2398d4fba8b9Smrg	return;
2399d4fba8b9Smrg    }
2400d4fba8b9Smrg    if (pid == 0) {		/* child */
2401d4fba8b9Smrg	/*
2402d4fba8b9Smrg	 * Close our output (we won't be talking back to the
2403d4fba8b9Smrg	 * parent), and redirect our child's output to the
2404d4fba8b9Smrg	 * original stderr.
2405d4fba8b9Smrg	 */
2406d4fba8b9Smrg	close(p[1]);
2407d4fba8b9Smrg	dup2(p[0], 0);
2408d4fba8b9Smrg	close(p[0]);
2409d4fba8b9Smrg	dup2(fileno(stderr), 1);
2410d4fba8b9Smrg	dup2(fileno(stderr), 2);
2411d4fba8b9Smrg
2412d4fba8b9Smrg	close(fileno(stderr));
2413d4fba8b9Smrg	close(ConnectionNumber(screen->display));
2414d4fba8b9Smrg	close(screen->respond);
2415d4fba8b9Smrg
2416d4fba8b9Smrg	signal(SIGHUP, SIG_DFL);
2417d4fba8b9Smrg	signal(SIGCHLD, SIG_DFL);
2418d4fba8b9Smrg
2419d4fba8b9Smrg	/* (this is redundant) */
2420d4fba8b9Smrg	if (xtermResetIds(screen) < 0)
2421d4fba8b9Smrg	    exit(ERROR_SETUID);
2422d4fba8b9Smrg
2423d4fba8b9Smrg	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2424d4fba8b9Smrg	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2425d4fba8b9Smrg	exit(ERROR_LOGEXEC);
2426d4fba8b9Smrg    }
2427d4fba8b9Smrg    close(p[0]);
2428d4fba8b9Smrg    screen->logfd = p[1];
2429d4fba8b9Smrg    signal(SIGPIPE, handle_SIGPIPE);
2430d4fba8b9Smrg}
2431d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2432d522f475Smrg
2433d4fba8b9Smrg/*
2434d4fba8b9Smrg * Generate a path for a logfile if no default path is given.
2435d4fba8b9Smrg */
2436d4fba8b9Smrgstatic char *
2437d4fba8b9SmrgGenerateLogPath(void)
2438d4fba8b9Smrg{
2439d4fba8b9Smrg    static char *log_default = NULL;
2440d4fba8b9Smrg
2441d4fba8b9Smrg    /* once opened we just reuse the same log name */
2442d4fba8b9Smrg    if (log_default)
2443d4fba8b9Smrg	return (log_default);
2444d4fba8b9Smrg
2445d4fba8b9Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2446d4fba8b9Smrg    {
2447d4fba8b9Smrg#define LEN_HOSTNAME 255
2448d4fba8b9Smrg	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
2449d4fba8b9Smrg	 * the total length of a domain name (i.e., label octets and label
2450d4fba8b9Smrg	 * length octets) is restricted to 255 octets or less.''
2451d4fba8b9Smrg	 */
2452d4fba8b9Smrg#define LEN_GETPID 9
2453d4fba8b9Smrg	/*
2454d4fba8b9Smrg	 * This is arbitrary...
2455d4fba8b9Smrg	 */
2456d4fba8b9Smrg	const char form[] = "Xterm.log.%s%s.%lu";
2457d4fba8b9Smrg	char where[LEN_HOSTNAME + 1];
2458d4fba8b9Smrg	char when[LEN_TIMESTAMP];
2459d4fba8b9Smrg	time_t now = time((time_t *) 0);
2460d4fba8b9Smrg	struct tm *ltm = (struct tm *) localtime(&now);
2461d4fba8b9Smrg
2462d4fba8b9Smrg	if ((gethostname(where, sizeof(where)) == 0) &&
2463d4fba8b9Smrg	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2464d4fba8b9Smrg	    ((log_default = (char *) malloc((sizeof(form)
2465d4fba8b9Smrg					     + strlen(where)
2466d4fba8b9Smrg					     + strlen(when)
2467d4fba8b9Smrg					     + LEN_GETPID))) != NULL)) {
2468d4fba8b9Smrg	    (void) sprintf(log_default,
2469d4fba8b9Smrg			   form,
2470d4fba8b9Smrg			   where, when,
2471d4fba8b9Smrg			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
2472d4fba8b9Smrg	}
2473d4fba8b9Smrg    }
2474d4fba8b9Smrg#else
2475d4fba8b9Smrg    static const char log_def_name[] = "XtermLog.XXXXXX";
2476d4fba8b9Smrg    if ((log_default = x_strdup(log_def_name)) != NULL) {
2477d4fba8b9Smrg	mktemp(log_default);
2478d4fba8b9Smrg    }
2479d4fba8b9Smrg#endif
2480d4fba8b9Smrg
2481d4fba8b9Smrg    return (log_default);
2482d4fba8b9Smrg}
2483d4fba8b9Smrg
2484d522f475Smrgvoid
2485cd3331d0SmrgStartLog(XtermWidget xw)
2486d522f475Smrg{
2487cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2488d522f475Smrg
2489d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2490d522f475Smrg	return;
2491d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
2492d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
2493d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2494d522f475Smrg			 0640);
2495d522f475Smrg    if (screen->logfd < 0)
2496d522f475Smrg	return;			/* open failed */
2497d522f475Smrg#else /*VMS */
24983367019cSmrg
2499d4fba8b9Smrg    /* if we weren't supplied with a logfile path, generate one */
2500d4fba8b9Smrg    if (IsEmpty(screen->logfile))
2501d4fba8b9Smrg	screen->logfile = GenerateLogPath();
25023367019cSmrg
2503d4fba8b9Smrg    /* give up if we were unable to allocate the filename */
2504d4fba8b9Smrg    if (!screen->logfile)
2505d4fba8b9Smrg	return;
2506d522f475Smrg
2507d4fba8b9Smrg    if (*screen->logfile == '|') {	/* exec command */
2508d4fba8b9Smrg#ifdef ALLOWLOGFILEEXEC
2509d4fba8b9Smrg	StartLogExec(screen);
2510d522f475Smrg#else
2511cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2512cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2513d522f475Smrg	return;
2514d522f475Smrg#endif
2515d4fba8b9Smrg    } else if (strcmp(screen->logfile, "-") == 0) {
2516d4fba8b9Smrg	screen->logfd = STDOUT_FILENO;
2517d522f475Smrg    } else {
2518d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2519d522f475Smrg					   screen->gid,
2520d522f475Smrg					   screen->logfile,
2521d4fba8b9Smrg					   True)) < 0)
2522d522f475Smrg	    return;
2523d522f475Smrg    }
2524d522f475Smrg#endif /*VMS */
2525d522f475Smrg    screen->logstart = VTbuffer->next;
2526d522f475Smrg    screen->logging = True;
2527d522f475Smrg    update_logging();
2528d522f475Smrg}
2529d522f475Smrg
2530d522f475Smrgvoid
2531cd3331d0SmrgCloseLog(XtermWidget xw)
2532d522f475Smrg{
2533cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2534cd3331d0Smrg
2535d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2536d522f475Smrg	return;
2537cd3331d0Smrg    FlushLog(xw);
2538d522f475Smrg    close(screen->logfd);
2539d522f475Smrg    screen->logging = False;
2540d522f475Smrg    update_logging();
2541d522f475Smrg}
2542d522f475Smrg
2543d522f475Smrgvoid
2544cd3331d0SmrgFlushLog(XtermWidget xw)
2545d522f475Smrg{
2546cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2547cd3331d0Smrg
2548d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2549d522f475Smrg	Char *cp;
2550d522f475Smrg	int i;
2551d522f475Smrg
2552d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2553d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2554d522f475Smrg	if (!tt_new_output)
2555d522f475Smrg	    return;
2556d522f475Smrg	tt_new_output = False;
2557d522f475Smrg#endif /* VMS */
2558d522f475Smrg	cp = VTbuffer->next;
2559d522f475Smrg	if (screen->logstart != 0
2560cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2561cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2562d522f475Smrg	}
2563d522f475Smrg	screen->logstart = VTbuffer->next;
2564d522f475Smrg    }
2565d522f475Smrg}
2566d522f475Smrg
2567d522f475Smrg#endif /* ALLOWLOGGING */
2568d522f475Smrg
2569d522f475Smrg/***====================================================================***/
2570d522f475Smrg
2571d4fba8b9Smrgstatic unsigned
2572d4fba8b9SmrgmaskToShift(unsigned long mask)
2573d4fba8b9Smrg{
2574d4fba8b9Smrg    unsigned result = 0;
2575d4fba8b9Smrg    if (mask != 0) {
2576d4fba8b9Smrg	while ((mask & 1) == 0) {
2577d4fba8b9Smrg	    mask >>= 1;
2578d4fba8b9Smrg	    ++result;
2579d4fba8b9Smrg	}
2580d4fba8b9Smrg    }
2581d4fba8b9Smrg    return result;
2582d4fba8b9Smrg}
2583d4fba8b9Smrg
2584d4fba8b9Smrgstatic unsigned
2585d4fba8b9SmrgmaskToWidth(unsigned long mask)
2586d4fba8b9Smrg{
2587d4fba8b9Smrg    unsigned result = 0;
2588d4fba8b9Smrg    while (mask != 0) {
2589d4fba8b9Smrg	if ((mask & 1) != 0)
2590d4fba8b9Smrg	    ++result;
2591d4fba8b9Smrg	mask >>= 1;
2592d4fba8b9Smrg    }
2593d4fba8b9Smrg    return result;
2594d4fba8b9Smrg}
2595d4fba8b9Smrg
2596fa3f02f3Smrgint
2597fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2598fa3f02f3Smrg{
2599fa3f02f3Smrg#define MYFMT "getVisualInfo \
2600fa3f02f3Smrgdepth %d, \
2601fa3f02f3Smrgtype %d (%s), \
2602fa3f02f3Smrgsize %d \
2603fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2604fa3f02f3Smrg#define MYARG \
2605fa3f02f3Smrg       vi->depth,\
2606fa3f02f3Smrg       vi->class,\
2607fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2608fa3f02f3Smrg       vi->colormap_size,\
2609fa3f02f3Smrg       vi->red_mask,\
2610fa3f02f3Smrg       vi->green_mask,\
2611fa3f02f3Smrg       vi->blue_mask
2612d522f475Smrg
2613fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2614fa3f02f3Smrg    Display *dpy = screen->display;
2615fa3f02f3Smrg    XVisualInfo myTemplate;
2616fa3f02f3Smrg
2617fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2618fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2619fa3f02f3Smrg								XDefaultScreen(dpy)));
2620fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2621fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2622fa3f02f3Smrg
2623fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2624fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2625d4fba8b9Smrg	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2626d4fba8b9Smrg	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2627d4fba8b9Smrg	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2628d4fba8b9Smrg	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2629d4fba8b9Smrg	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2630d4fba8b9Smrg	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2631d4fba8b9Smrg
2632d4fba8b9Smrg	    xw->has_rgb = ((vi->red_mask != 0) &&
2633d4fba8b9Smrg			   (vi->green_mask != 0) &&
2634d4fba8b9Smrg			   (vi->blue_mask != 0) &&
2635d4fba8b9Smrg			   ((vi->red_mask & vi->green_mask) == 0) &&
2636d4fba8b9Smrg			   ((vi->green_mask & vi->blue_mask) == 0) &&
2637d4fba8b9Smrg			   ((vi->blue_mask & vi->red_mask) == 0));
2638d4fba8b9Smrg
2639fa3f02f3Smrg	    if (resource.reportColors) {
2640fa3f02f3Smrg		printf(MYFMT, MYARG);
2641fa3f02f3Smrg	    }
2642fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2643d4fba8b9Smrg	    TRACE(("...shifts %u/%u/%u\n",
2644d4fba8b9Smrg		   xw->rgb_shifts[0],
2645d4fba8b9Smrg		   xw->rgb_shifts[1],
2646d4fba8b9Smrg		   xw->rgb_shifts[2]));
2647fa3f02f3Smrg	}
2648fa3f02f3Smrg    }
2649fa3f02f3Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0);
2650fa3f02f3Smrg#undef MYFMT
2651fa3f02f3Smrg#undef MYARG
2652fa3f02f3Smrg}
26533367019cSmrg
26549a64e1c5Smrg#if OPT_ISO_COLORS
2655d4fba8b9Smrgstatic Bool
2656d4fba8b9SmrgReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
26579a64e1c5Smrg{
2658d4fba8b9Smrg    Bool result = False;
2659d4fba8b9Smrg
26609a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
26619a64e1c5Smrg	XColor color;
26629a64e1c5Smrg	Colormap cmap = xw->core.colormap;
26639a64e1c5Smrg	char buffer[80];
26649a64e1c5Smrg
26659a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
26669a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
26679a64e1c5Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
2668d4fba8b9Smrg	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2669d4fba8b9Smrg		opcode,
2670d4fba8b9Smrg		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
26719a64e1c5Smrg		color.red,
26729a64e1c5Smrg		color.green,
26739a64e1c5Smrg		color.blue);
26749a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
26759a64e1c5Smrg	unparseputs(xw, buffer);
26769a64e1c5Smrg	unparseputc1(xw, final);
2677d4fba8b9Smrg	result = True;
26789a64e1c5Smrg    }
2679d4fba8b9Smrg    return result;
26809a64e1c5Smrg}
26819a64e1c5Smrg
2682fa3f02f3Smrgstatic void
2683fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2684fa3f02f3Smrg{
2685fa3f02f3Smrg    if (getVisualInfo(xw)) {
2686fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2687fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2688fa3f02f3Smrg    } else {
2689fa3f02f3Smrg	*typep = 0;
2690fa3f02f3Smrg	*sizep = 0;
2691fa3f02f3Smrg    }
26923367019cSmrg}
26933367019cSmrg
26943367019cSmrg#define MAX_COLORTABLE 4096
26953367019cSmrg
26963367019cSmrg/*
26973367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
26983367019cSmrg */
26993367019cSmrgstatic Boolean
27003367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
27013367019cSmrg{
27023367019cSmrg    Colormap cmap = xw->core.colormap;
27033367019cSmrg    TScreen *screen = TScreenOf(xw);
2704fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
27053367019cSmrg
2706fa3f02f3Smrg    if (!result
27073367019cSmrg	&& length != 0
27083367019cSmrg	&& length < MAX_COLORTABLE) {
27093367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2710037a25ddSmrg
27113367019cSmrg	if (screen->cmap_data != 0) {
2712037a25ddSmrg	    unsigned i;
2713d4fba8b9Smrg	    unsigned shift;
2714d4fba8b9Smrg
2715d4fba8b9Smrg	    if (getVisualInfo(xw))
2716d4fba8b9Smrg		shift = xw->rgb_shifts[2];
2717d4fba8b9Smrg	    else
2718d4fba8b9Smrg		shift = 0;
2719037a25ddSmrg
27203367019cSmrg	    screen->cmap_size = length;
27213367019cSmrg
27223367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
2723d4fba8b9Smrg		screen->cmap_data[i].pixel = (unsigned long) i << shift;
27243367019cSmrg	    }
27253367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
27263367019cSmrg					     cmap,
27273367019cSmrg					     screen->cmap_data,
27283367019cSmrg					     (int) screen->cmap_size) != 0);
27293367019cSmrg	}
27303367019cSmrg    }
2731d522f475Smrg    return result;
2732d522f475Smrg}
2733d522f475Smrg
2734d522f475Smrg/*
2735d522f475Smrg * Find closest color for "def" in "cmap".
2736d522f475Smrg * Set "def" to the resulting color.
2737d522f475Smrg *
2738d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2739d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2740d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2741d522f475Smrg *
2742d522f475Smrg * Return False if not able to find or allocate a color.
2743d522f475Smrg */
2744d522f475Smrgstatic Boolean
27459a64e1c5SmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def)
2746d522f475Smrg{
27473367019cSmrg    TScreen *screen = TScreenOf(xw);
2748d522f475Smrg    Boolean result = False;
27493367019cSmrg    unsigned cmap_type;
2750d522f475Smrg    unsigned cmap_size;
2751d522f475Smrg
2752fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2753d522f475Smrg
27543367019cSmrg    if ((cmap_type & 1) != 0) {
27553367019cSmrg
27563367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2757037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2758d522f475Smrg
2759d522f475Smrg	    if (tried != 0) {
2760037a25ddSmrg		unsigned attempts;
2761d522f475Smrg
2762d522f475Smrg		/*
2763d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2764d522f475Smrg		 * approximation to the requested color.
2765d522f475Smrg		 */
2766d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2767d522f475Smrg		    Boolean first = True;
2768037a25ddSmrg		    double bestRGB = 0.0;
2769037a25ddSmrg		    unsigned bestInx = 0;
2770037a25ddSmrg		    unsigned i;
2771d522f475Smrg
2772d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2773d522f475Smrg			if (!tried[bestInx]) {
2774037a25ddSmrg			    double diff, thisRGB = 0.0;
2775037a25ddSmrg
2776d522f475Smrg			    /*
2777d522f475Smrg			     * Look for the best match based on luminance.
2778d522f475Smrg			     * Measure this by the least-squares difference of
2779d522f475Smrg			     * the weighted R/G/B components from the color map
2780d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2781d522f475Smrg			     * component of the YIQ color space model for
2782d522f475Smrg			     * weights that correspond to the luminance.
2783d522f475Smrg			     */
2784d522f475Smrg#define AddColorWeight(weight, color) \
27853367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2786037a25ddSmrg			    thisRGB += diff * diff
2787d522f475Smrg
2788d522f475Smrg			    AddColorWeight(0.30, red);
2789d522f475Smrg			    AddColorWeight(0.61, green);
2790d522f475Smrg			    AddColorWeight(0.11, blue);
2791d522f475Smrg
2792d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2793d522f475Smrg				first = False;
2794d522f475Smrg				bestInx = i;
2795d522f475Smrg				bestRGB = thisRGB;
2796d522f475Smrg			    }
2797d522f475Smrg			}
2798d522f475Smrg		    }
27993367019cSmrg		    if (XAllocColor(screen->display, cmap,
28003367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
28013367019cSmrg			*def = screen->cmap_data[bestInx];
28023367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
28033367019cSmrg			       def->green, def->blue));
2804d522f475Smrg			result = True;
2805d522f475Smrg			break;
2806d522f475Smrg		    }
2807d522f475Smrg		    /*
2808d522f475Smrg		     * It failed - either the color map entry was readonly, or
2809d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2810d522f475Smrg		     * so we will ignore it
2811d522f475Smrg		     */
2812d522f475Smrg		    tried[bestInx] = True;
2813d522f475Smrg		}
2814d522f475Smrg		free(tried);
2815d522f475Smrg	    }
2816d522f475Smrg	}
2817d522f475Smrg    }
2818d522f475Smrg    return result;
2819d522f475Smrg}
2820d522f475Smrg
28213367019cSmrg#ifndef ULONG_MAX
28223367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
28233367019cSmrg#endif
28243367019cSmrg
28253367019cSmrg#define CheckColor(result, value) \
28263367019cSmrg	    result = 0; \
28273367019cSmrg	    if (value.red) \
28283367019cSmrg		result |= 1; \
28293367019cSmrg	    if (value.green) \
28303367019cSmrg		result |= 2; \
28313367019cSmrg	    if (value.blue) \
28323367019cSmrg		result |= 4
28333367019cSmrg
28343367019cSmrg#define SelectColor(state, value, result) \
28353367019cSmrg	switch (state) { \
28363367019cSmrg	default: \
28373367019cSmrg	case 1: \
28383367019cSmrg	    result = value.red; \
28393367019cSmrg	    break; \
28403367019cSmrg	case 2: \
28413367019cSmrg	    result = value.green; \
28423367019cSmrg	    break; \
28433367019cSmrg	case 4: \
28443367019cSmrg	    result = value.blue; \
28453367019cSmrg	    break; \
28463367019cSmrg	}
28473367019cSmrg
28483367019cSmrg/*
28493367019cSmrg * Check if the color map consists of values in exactly one of the red, green
28503367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
28513367019cSmrg * match.
28523367019cSmrg */
28533367019cSmrgstatic int
28549a64e1c5SmrgsimpleColors(XColor *colortable, unsigned length)
28553367019cSmrg{
28563367019cSmrg    unsigned n;
2857fa3f02f3Smrg    int state = 0;
28583367019cSmrg    int check;
28593367019cSmrg
28603367019cSmrg    for (n = 0; n < length; ++n) {
28613367019cSmrg	if (state > 0) {
28623367019cSmrg	    CheckColor(check, colortable[n]);
28633367019cSmrg	    if (check > 0 && check != state) {
28643367019cSmrg		state = 0;
28653367019cSmrg		break;
28663367019cSmrg	    }
2867fa3f02f3Smrg	} else {
2868fa3f02f3Smrg	    CheckColor(state, colortable[n]);
28693367019cSmrg	}
28703367019cSmrg    }
28713367019cSmrg    switch (state) {
28723367019cSmrg    case 1:
28733367019cSmrg    case 2:
28743367019cSmrg    case 4:
28753367019cSmrg	break;
28763367019cSmrg    default:
28773367019cSmrg	state = 0;
28783367019cSmrg	break;
28793367019cSmrg    }
28803367019cSmrg    return state;
28813367019cSmrg}
28823367019cSmrg
2883fa3f02f3Smrg/*
2884fa3f02f3Smrg * Shift the mask left or right to put its most significant bit at the 16-bit
2885fa3f02f3Smrg * mark.
2886fa3f02f3Smrg */
2887fa3f02f3Smrgstatic unsigned
2888fa3f02f3SmrgnormalizeMask(unsigned mask)
2889fa3f02f3Smrg{
2890fa3f02f3Smrg    while (mask < 0x8000) {
2891fa3f02f3Smrg	mask <<= 1;
2892fa3f02f3Smrg    }
2893fa3f02f3Smrg    while (mask >= 0x10000) {
2894fa3f02f3Smrg	mask >>= 1;
2895fa3f02f3Smrg    }
2896fa3f02f3Smrg    return mask;
2897fa3f02f3Smrg}
2898fa3f02f3Smrg
28993367019cSmrgstatic unsigned
29009a64e1c5SmrgsearchColors(XColor *colortable, unsigned mask, unsigned length, unsigned
2901fa3f02f3Smrg	     color, int state)
29023367019cSmrg{
29033367019cSmrg    unsigned result = 0;
29043367019cSmrg    unsigned n;
29053367019cSmrg    unsigned long best = ULONG_MAX;
29063367019cSmrg    unsigned value;
29073367019cSmrg
2908fa3f02f3Smrg    mask = normalizeMask(mask);
29093367019cSmrg    for (n = 0; n < length; ++n) {
2910037a25ddSmrg	unsigned long diff;
2911037a25ddSmrg
29123367019cSmrg	SelectColor(state, colortable[n], value);
2913fa3f02f3Smrg	diff = ((color & mask) - (value & mask));
29143367019cSmrg	diff *= diff;
29153367019cSmrg	if (diff < best) {
29163367019cSmrg#if 0
29173367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
29183367019cSmrg		   n, color,
29193367019cSmrg		   colortable[n].red,
29203367019cSmrg		   colortable[n].green,
29213367019cSmrg		   colortable[n].blue,
29223367019cSmrg		   diff));
29233367019cSmrg#endif
29243367019cSmrg	    result = n;
29253367019cSmrg	    best = diff;
29263367019cSmrg	}
29273367019cSmrg    }
29283367019cSmrg    SelectColor(state, colortable[result], value);
29293367019cSmrg    return value;
29303367019cSmrg}
29313367019cSmrg
29323367019cSmrg/*
29333367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
29343367019cSmrg *
29353367019cSmrg * According to
29363367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
29373367019cSmrg *
29383367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
29393367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
29403367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
29413367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
29423367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
29433367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
29443367019cSmrg *     actual RGB values allocated.
29453367019cSmrg *
29463367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
2947fa3f02f3Smrg * case, allocateClosestRGB() is useful for the dynamic display classes such as
29483367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
29493367019cSmrg * return regular RGB triples (unless a different scheme was used for
29503367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
29513367019cSmrg * is filled in with the colors that the server supports.
29523367019cSmrg *
29533367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
29543367019cSmrg * described.  For some TrueColor configurations it merely returns a close
29553367019cSmrg * approximation, but not the closest.
29563367019cSmrg */
29573367019cSmrgstatic Boolean
29589a64e1c5SmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def)
29593367019cSmrg{
29603367019cSmrg    XColor save = *def;
29613367019cSmrg    TScreen *screen = TScreenOf(xw);
29623367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
29633367019cSmrg
29643367019cSmrg    /*
2965fa3f02f3Smrg     * If this is a statically allocated display with too many items to store
2966fa3f02f3Smrg     * in our array, i.e., TrueColor, see if we can improve on the result by
2967fa3f02f3Smrg     * using the color values actually supported by the server.
29683367019cSmrg     */
29693367019cSmrg    if (result) {
29703367019cSmrg	unsigned cmap_type;
29713367019cSmrg	unsigned cmap_size;
29723367019cSmrg
2973fa3f02f3Smrg	getColormapInfo(xw, &cmap_type, &cmap_size);
29743367019cSmrg
2975fa3f02f3Smrg	if (cmap_type == TrueColor) {
29763367019cSmrg	    XColor temp = *def;
2977037a25ddSmrg	    int state;
29783367019cSmrg
29793367019cSmrg	    if (loadColorTable(xw, cmap_size)
29803367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
2981fa3f02f3Smrg#define SearchColors(which) \
2982fa3f02f3Smrg	temp.which = (unsigned short) searchColors(screen->cmap_data, \
2983fa3f02f3Smrg						   (unsigned) xw->visInfo->which##_mask,\
2984fa3f02f3Smrg						   cmap_size, \
2985fa3f02f3Smrg						   save.which, \
2986fa3f02f3Smrg						   state)
29873367019cSmrg		SearchColors(red);
29883367019cSmrg		SearchColors(green);
29893367019cSmrg		SearchColors(blue);
29903367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
29913367019cSmrg#if OPT_TRACE
29923367019cSmrg		    if (temp.red != save.red
29933367019cSmrg			|| temp.green != save.green
29943367019cSmrg			|| temp.blue != save.blue) {
29953367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
29963367019cSmrg			       save.red, save.green, save.blue,
29973367019cSmrg			       temp.red, temp.green, temp.blue));
29983367019cSmrg		    } else {
29993367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
30003367019cSmrg			       save.red, save.green, save.blue));
30013367019cSmrg		    }
30023367019cSmrg#endif
30033367019cSmrg		    *def = temp;
30043367019cSmrg		}
30053367019cSmrg	    }
30063367019cSmrg	}
30073367019cSmrg    }
30083367019cSmrg
30093367019cSmrg    return result;
30103367019cSmrg}
30113367019cSmrg
3012d522f475Smrg/*
3013d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
3014d522f475Smrg * to 256.
3015d522f475Smrg *
3016d522f475Smrg * Returns
3017d522f475Smrg *	-1 on error
3018d522f475Smrg *	0 on no change
3019d522f475Smrg *	1 if a new color was allocated.
3020d522f475Smrg */
3021d522f475Smrgstatic int
3022d522f475SmrgAllocateAnsiColor(XtermWidget xw,
3023d522f475Smrg		  ColorRes * res,
3024cd3331d0Smrg		  const char *spec)
3025d522f475Smrg{
3026d522f475Smrg    int result;
3027d522f475Smrg    XColor def;
3028d522f475Smrg
30293367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
3030d522f475Smrg	if (
3031d522f475Smrg#if OPT_COLOR_RES
3032d522f475Smrg	       res->mode == True &&
3033d522f475Smrg#endif
3034d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
3035d522f475Smrg	    result = 0;
3036d522f475Smrg	} else {
3037d522f475Smrg	    result = 1;
3038d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
30393367019cSmrg	    res->red = def.red;
30403367019cSmrg	    res->green = def.green;
30413367019cSmrg	    res->blue = def.blue;
30423367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
30433367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
30443367019cSmrg		   def.red,
30453367019cSmrg		   def.green,
30463367019cSmrg		   def.blue,
30473367019cSmrg		   def.pixel));
3048d522f475Smrg#if OPT_COLOR_RES
3049d522f475Smrg	    if (!res->mode)
3050d522f475Smrg		result = 0;
3051d522f475Smrg	    res->mode = True;
3052d522f475Smrg#endif
3053d522f475Smrg	}
3054d522f475Smrg    } else {
3055d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
3056d522f475Smrg	result = -1;
3057d522f475Smrg    }
3058d522f475Smrg    return (result);
3059d522f475Smrg}
3060d522f475Smrg
3061d522f475Smrg#if OPT_COLOR_RES
3062d522f475SmrgPixel
3063cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
3064d522f475Smrg{
3065d522f475Smrg    Pixel result = 0;
3066d522f475Smrg
3067d522f475Smrg    if (res->mode) {
3068d522f475Smrg	result = res->value;
3069d522f475Smrg    } else {
3070d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
3071cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
3072d522f475Smrg
3073cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
3074cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
3075d522f475Smrg
3076cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
3077cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
3078d522f475Smrg		res->mode = -True;
30793367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
30803367019cSmrg			     NonNull(res->resource));
3081d522f475Smrg	    }
3082d522f475Smrg	    result = res->value;
3083d522f475Smrg	} else {
3084d522f475Smrg	    result = 0;
3085d522f475Smrg	}
3086d522f475Smrg    }
3087d522f475Smrg    return result;
3088d522f475Smrg}
3089d522f475Smrg#endif
3090d522f475Smrg
3091cd3331d0Smrgstatic int
3092cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
3093cd3331d0Smrg{
3094cd3331d0Smrg    int code;
3095cd3331d0Smrg
3096cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
3097cd3331d0Smrg	code = -1;
3098cd3331d0Smrg    } else {
3099cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
3100cd3331d0Smrg
3101cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
3102cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
3103cd3331d0Smrg    }
3104cd3331d0Smrg    return code;
3105cd3331d0Smrg}
3106cd3331d0Smrg
3107cd3331d0Smrg/*
3108cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
3109cd3331d0Smrg * values from the given buffer.
3110cd3331d0Smrg *
3111cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
3112cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
3113cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
3114cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
3115cd3331d0Smrg * 'first' set to the beginning of those indices.
3116cd3331d0Smrg *
3117cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
3118cd3331d0Smrg */
3119d522f475Smrgstatic Bool
3120d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
3121d4fba8b9Smrg		       int opcode,
3122d522f475Smrg		       char *buf,
3123cd3331d0Smrg		       int first,
3124d522f475Smrg		       int final)
3125d522f475Smrg{
3126d522f475Smrg    int repaint = False;
3127d522f475Smrg    int code;
3128cd3331d0Smrg    int last = (MAXCOLORS - first);
3129d4fba8b9Smrg    int queried = 0;
3130d522f475Smrg
3131d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
3132d522f475Smrg
3133d522f475Smrg    while (buf && *buf) {
3134037a25ddSmrg	int color;
3135037a25ddSmrg	char *name = strchr(buf, ';');
3136037a25ddSmrg
3137d522f475Smrg	if (name == NULL)
3138d522f475Smrg	    break;
3139d522f475Smrg	*name = '\0';
3140d522f475Smrg	name++;
3141d522f475Smrg	color = atoi(buf);
3142cd3331d0Smrg	if (color < 0 || color >= last)
3143cd3331d0Smrg	    break;		/* quit on any error */
3144d522f475Smrg	buf = strchr(name, ';');
3145d522f475Smrg	if (buf) {
3146d522f475Smrg	    *buf = '\0';
3147d522f475Smrg	    buf++;
3148d522f475Smrg	}
3149cd3331d0Smrg	if (!strcmp(name, "?")) {
3150d4fba8b9Smrg	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3151d4fba8b9Smrg		++queried;
3152cd3331d0Smrg	} else {
3153cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
3154d522f475Smrg	    if (code < 0) {
3155d522f475Smrg		/* stop on any error */
3156d522f475Smrg		break;
3157d522f475Smrg	    } else if (code > 0) {
3158d522f475Smrg		repaint = True;
3159d522f475Smrg	    }
3160d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
3161d522f475Smrg	     * change style (dynamic colors).
3162d522f475Smrg	     */
3163d522f475Smrg	}
3164d522f475Smrg    }
3165d4fba8b9Smrg    if (queried)
3166d4fba8b9Smrg	unparse_end(xw);
3167d522f475Smrg
3168d522f475Smrg    return (repaint);
3169d522f475Smrg}
3170cd3331d0Smrg
3171cd3331d0Smrgstatic Bool
3172cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
3173cd3331d0Smrg{
3174cd3331d0Smrg    Bool repaint = False;
3175cd3331d0Smrg    int last = MAXCOLORS - start;
3176cd3331d0Smrg
3177cd3331d0Smrg    if (color >= 0 && color < last) {
3178cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3179cd3331d0Smrg
3180cd3331d0Smrg	if (res->mode) {
3181cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
3182cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3183cd3331d0Smrg		repaint = True;
3184cd3331d0Smrg	    }
3185cd3331d0Smrg	}
3186cd3331d0Smrg    }
3187cd3331d0Smrg    return repaint;
3188cd3331d0Smrg}
3189cd3331d0Smrg
3190cd3331d0Smrgint
3191cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3192cd3331d0Smrg{
3193cd3331d0Smrg    int repaint = 0;
3194cd3331d0Smrg    int color;
3195cd3331d0Smrg
3196cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3197cd3331d0Smrg    if (*buf != '\0') {
3198cd3331d0Smrg	/* reset specific colors */
3199cd3331d0Smrg	while (!IsEmpty(buf)) {
3200cd3331d0Smrg	    char *next;
3201cd3331d0Smrg
3202037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
3203037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
3204cd3331d0Smrg		break;		/* no number at all */
3205cd3331d0Smrg	    if (next != 0) {
3206cd3331d0Smrg		if (strchr(";", *next) == 0)
3207cd3331d0Smrg		    break;	/* unexpected delimiter */
3208cd3331d0Smrg		++next;
3209cd3331d0Smrg	    }
3210cd3331d0Smrg
3211cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3212cd3331d0Smrg		++repaint;
3213cd3331d0Smrg	    }
3214cd3331d0Smrg	    buf = next;
3215cd3331d0Smrg	}
3216cd3331d0Smrg    } else {
3217cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3218cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
3219cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3220cd3331d0Smrg		++repaint;
3221cd3331d0Smrg	    }
3222cd3331d0Smrg	}
3223cd3331d0Smrg    }
3224cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3225cd3331d0Smrg    return repaint;
3226cd3331d0Smrg}
3227d522f475Smrg#else
32283367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
32293367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
3230d522f475Smrg#endif /* OPT_ISO_COLORS */
3231d522f475Smrg
3232fa3f02f3SmrgBoolean
32339a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
3234fa3f02f3Smrg{
3235fa3f02f3Smrg    Colormap cmap = xw->core.colormap;
3236fa3f02f3Smrg
3237fa3f02f3Smrg    return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
3238fa3f02f3Smrg}
3239fa3f02f3Smrg
32403367019cSmrgstatic Boolean
32419a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
32423367019cSmrg{
32433367019cSmrg    Boolean result = False;
32443367019cSmrg    TScreen *screen = TScreenOf(xw);
32453367019cSmrg    Colormap cmap = xw->core.colormap;
32468f44fb3bSmrg    size_t have = strlen(spec);
32473367019cSmrg
32488f44fb3bSmrg    if (have == 0 || have > MAX_U_STRING) {
32498f44fb3bSmrg	if (resource.reportColors) {
32508f44fb3bSmrg	    printf("color  (ignored, length %lu)\n", have);
32518f44fb3bSmrg	}
32528f44fb3bSmrg    } else if (XParseColor(screen->display, cmap, spec, def)) {
3253fa3f02f3Smrg	XColor save_def = *def;
3254fa3f02f3Smrg	if (resource.reportColors) {
3255fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3256fa3f02f3Smrg		   def->red, def->green, def->blue,
3257fa3f02f3Smrg		   spec);
3258fa3f02f3Smrg	}
3259fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
3260fa3f02f3Smrg	    if (resource.reportColors) {
3261fa3f02f3Smrg		if (def->red != save_def.red ||
3262fa3f02f3Smrg		    def->green != save_def.green ||
3263fa3f02f3Smrg		    def->blue != save_def.blue) {
3264fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3265fa3f02f3Smrg			   def->red, def->green, def->blue,
3266fa3f02f3Smrg			   spec);
3267fa3f02f3Smrg		}
3268fa3f02f3Smrg	    }
3269fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3270fa3f02f3Smrg		   def->red, def->green, def->blue));
3271fa3f02f3Smrg	    result = True;
3272fa3f02f3Smrg	}
32733367019cSmrg    }
32743367019cSmrg    return result;
32753367019cSmrg}
32763367019cSmrg
32773367019cSmrg/*
32783367019cSmrg * This provides an approximation (the closest color from xterm's palette)
32793367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
32803367019cSmrg * because of the context in which it is used.
32813367019cSmrg */
32823367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
32833367019cSmrgint
32843367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
32853367019cSmrg{
32863367019cSmrg    int result = -1;
32873367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
32883367019cSmrg    int n;
32893367019cSmrg    int best_index = -1;
32903367019cSmrg    unsigned long best_value = 0;
32913367019cSmrg    unsigned long this_value;
32923367019cSmrg    long diff_red, diff_green, diff_blue;
32933367019cSmrg
32943367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
32953367019cSmrg
32963367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
32973367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
32983367019cSmrg
32993367019cSmrg	/* ensure that we have a value for each of the colors */
33003367019cSmrg	if (!res->mode) {
33013367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
33023367019cSmrg	}
33033367019cSmrg
33043367019cSmrg	/* find the closest match */
33053367019cSmrg	if (res->mode == True) {
33063367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
33073367019cSmrg		    res->value, res->red, res->green, res->blue));
33083367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
33093367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
33103367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
33113367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
33123367019cSmrg					  + (diff_green * diff_green)
33133367019cSmrg					  + (diff_blue * diff_blue));
33143367019cSmrg	    if (best_index < 0 || this_value < best_value) {
33153367019cSmrg		best_index = n;
33163367019cSmrg		best_value = this_value;
33173367019cSmrg	    }
33183367019cSmrg	}
33193367019cSmrg    }
33203367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
33213367019cSmrg    result = best_index;
33223367019cSmrg#else
33233367019cSmrg    (void) xw;
33243367019cSmrg    (void) find_red;
33253367019cSmrg    (void) find_green;
33263367019cSmrg    (void) find_blue;
33273367019cSmrg#endif
33283367019cSmrg    return result;
33293367019cSmrg}
33303367019cSmrg
3331d4fba8b9Smrg#if OPT_DIRECT_COLOR
3332d4fba8b9Smrgint
3333d4fba8b9SmrggetDirectColor(XtermWidget xw, int red, int green, int blue)
3334d4fba8b9Smrg{
3335d4fba8b9Smrg#define nRGB(name,shift) \
3336d4fba8b9Smrg	((unsigned long)(name << xw->rgb_shifts[shift]) \
3337d4fba8b9Smrg		         & xw->visInfo->name ##_mask)
3338d4fba8b9Smrg    MyPixel result = (MyPixel) (nRGB(red, 0) | nRGB(green, 1) | nRGB(blue, 2));
3339d4fba8b9Smrg    return (int) result;
3340d4fba8b9Smrg}
3341d4fba8b9Smrg
3342d4fba8b9Smrgstatic void
3343d4fba8b9SmrgformatDirectColor(char *target, XtermWidget xw, unsigned value)
3344d4fba8b9Smrg{
3345d4fba8b9Smrg#define fRGB(name, shift) \
3346d4fba8b9Smrg	(value & xw->visInfo->name ## _mask) >> xw->rgb_shifts[shift]
3347d4fba8b9Smrg    sprintf(target, "%lu:%lu:%lu", fRGB(red, 0), fRGB(green, 1), fRGB(blue, 2));
3348d4fba8b9Smrg}
3349d4fba8b9Smrg#endif /* OPT_DIRECT_COLOR */
3350d4fba8b9Smrg
3351d4fba8b9Smrg#define fg2SGR(n) \
3352d4fba8b9Smrg		(n) >= 8 ? 9 : 3, \
3353d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3354d4fba8b9Smrg#define bg2SGR(n) \
3355d4fba8b9Smrg		(n) >= 8 ? 10 : 4, \
3356d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3357d4fba8b9Smrg
3358d4fba8b9Smrg#define EndOf(s) (s) + strlen(s)
3359d4fba8b9Smrg
3360d4fba8b9Smrgchar *
3361d4fba8b9SmrgxtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3362d4fba8b9Smrg{
3363d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
3364d4fba8b9Smrg    char *msg = target;
3365d4fba8b9Smrg
3366d4fba8b9Smrg    strcpy(target, "0");
3367d4fba8b9Smrg    if (attr & BOLD)
3368d4fba8b9Smrg	strcat(msg, ";1");
3369d4fba8b9Smrg    if (attr & UNDERLINE)
3370d4fba8b9Smrg	strcat(msg, ";4");
3371d4fba8b9Smrg    if (attr & BLINK)
3372d4fba8b9Smrg	strcat(msg, ";5");
3373d4fba8b9Smrg    if (attr & INVERSE)
3374d4fba8b9Smrg	strcat(msg, ";7");
3375d4fba8b9Smrg    if (attr & INVISIBLE)
3376d4fba8b9Smrg	strcat(msg, ";8");
3377d4fba8b9Smrg#if OPT_WIDE_ATTRS
3378d4fba8b9Smrg    if (attr & ATR_FAINT)
3379d4fba8b9Smrg	strcat(msg, ";2");
3380d4fba8b9Smrg    if (attr & ATR_ITALIC)
3381d4fba8b9Smrg	strcat(msg, ";3");
3382d4fba8b9Smrg    if (attr & ATR_STRIKEOUT)
3383d4fba8b9Smrg	strcat(msg, ";9");
3384d4fba8b9Smrg    if (attr & ATR_DBL_UNDER)
3385d4fba8b9Smrg	strcat(msg, ";21");
3386d4fba8b9Smrg#endif
3387d4fba8b9Smrg#if OPT_256_COLORS || OPT_88_COLORS
3388d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3389d4fba8b9Smrg	if (attr & FG_COLOR) {
3390d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3391d4fba8b9Smrg		strcat(msg, ";38:2::");
3392d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3393d4fba8b9Smrg	    }) if (fg >= 16) {
3394d4fba8b9Smrg		sprintf(EndOf(msg), ";38:5:%d", fg);
3395d4fba8b9Smrg	    } else {
3396d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3397d4fba8b9Smrg	    }
3398d4fba8b9Smrg	}
3399d4fba8b9Smrg	if (attr & BG_COLOR) {
3400d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3401d4fba8b9Smrg		strcat(msg, ";48:2::");
3402d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3403d4fba8b9Smrg	    }) if (bg >= 16) {
3404d4fba8b9Smrg		sprintf(EndOf(msg), ";48:5:%d", bg);
3405d4fba8b9Smrg	    } else {
3406d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3407d4fba8b9Smrg	    }
3408d4fba8b9Smrg	}
3409d4fba8b9Smrg    });
3410d4fba8b9Smrg#elif OPT_ISO_COLORS
3411d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3412d4fba8b9Smrg	if (attr & FG_COLOR) {
3413d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3414d4fba8b9Smrg	}
3415d4fba8b9Smrg	if (attr & BG_COLOR) {
3416d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3417d4fba8b9Smrg	}
3418d4fba8b9Smrg    });
3419d4fba8b9Smrg#else
3420d4fba8b9Smrg    (void) screen;
3421d4fba8b9Smrg    (void) fg;
3422d4fba8b9Smrg    (void) bg;
3423d4fba8b9Smrg#endif
3424d4fba8b9Smrg    return target;
3425d4fba8b9Smrg}
3426d4fba8b9Smrg
3427d522f475Smrg#if OPT_PASTE64
3428d522f475Smrgstatic void
3429fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3430d522f475Smrg{
3431d522f475Smrg#define PDATA(a,b) { a, #b }
3432d522f475Smrg    static struct {
3433d522f475Smrg	char given;
3434cd3331d0Smrg	String result;
3435d522f475Smrg    } table[] = {
3436d522f475Smrg	PDATA('s', SELECT),
3437d522f475Smrg	    PDATA('p', PRIMARY),
3438d4fba8b9Smrg	    PDATA('q', SECONDARY),
3439d522f475Smrg	    PDATA('c', CLIPBOARD),
3440d522f475Smrg	    PDATA('0', CUT_BUFFER0),
3441d522f475Smrg	    PDATA('1', CUT_BUFFER1),
3442d522f475Smrg	    PDATA('2', CUT_BUFFER2),
3443d522f475Smrg	    PDATA('3', CUT_BUFFER3),
3444d522f475Smrg	    PDATA('4', CUT_BUFFER4),
3445d522f475Smrg	    PDATA('5', CUT_BUFFER5),
3446d522f475Smrg	    PDATA('6', CUT_BUFFER6),
3447d522f475Smrg	    PDATA('7', CUT_BUFFER7),
3448d522f475Smrg    };
3449d522f475Smrg
3450cd3331d0Smrg    const char *base = buf;
3451d522f475Smrg    Cardinal j, n = 0;
3452d522f475Smrg
3453d522f475Smrg    TRACE(("Manipulate selection data\n"));
3454d522f475Smrg
3455d522f475Smrg    while (*buf != ';' && *buf != '\0') {
3456d522f475Smrg	++buf;
3457d522f475Smrg    }
3458d522f475Smrg
3459d522f475Smrg    if (*buf == ';') {
3460037a25ddSmrg	char *used;
3461037a25ddSmrg
3462d522f475Smrg	*buf++ = '\0';
3463d522f475Smrg
3464d522f475Smrg	if (*base == '\0')
3465d522f475Smrg	    base = "s0";
3466d522f475Smrg
34673367019cSmrg	if ((used = x_strdup(base)) != 0) {
3468037a25ddSmrg	    String *select_args;
3469037a25ddSmrg
34703367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
34713367019cSmrg		while (*base != '\0') {
34723367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
34733367019cSmrg			if (*base == table[j].given) {
34743367019cSmrg			    used[n] = *base;
34753367019cSmrg			    select_args[n++] = table[j].result;
34763367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
34773367019cSmrg			    break;
34783367019cSmrg			}
34793367019cSmrg		    }
34803367019cSmrg		    ++base;
34813367019cSmrg		}
34823367019cSmrg		used[n] = 0;
34833367019cSmrg
34843367019cSmrg		if (!strcmp(buf, "?")) {
34853367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
34863367019cSmrg			TRACE(("Getting selection\n"));
34873367019cSmrg			unparseputc1(xw, ANSI_OSC);
34883367019cSmrg			unparseputs(xw, "52");
34893367019cSmrg			unparseputc(xw, ';');
34903367019cSmrg
34913367019cSmrg			unparseputs(xw, used);
34923367019cSmrg			unparseputc(xw, ';');
34933367019cSmrg
34943367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
34953367019cSmrg			screen->base64_paste = n;
34963367019cSmrg			screen->base64_final = final;
34973367019cSmrg
3498dfb07bc7Smrg			screen->selection_time =
3499dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3500dfb07bc7Smrg
35013367019cSmrg			/* terminator will be written in this call */
35023367019cSmrg			xtermGetSelection((Widget) xw,
3503dfb07bc7Smrg					  screen->selection_time,
35043367019cSmrg					  select_args, n,
35053367019cSmrg					  NULL);
350694644356Smrg			/*
350794644356Smrg			 * select_args is used via SelectionReceived, cannot
350894644356Smrg			 * free it here.
350994644356Smrg			 */
351094644356Smrg		    } else {
351194644356Smrg			free(select_args);
35123367019cSmrg		    }
35133367019cSmrg		} else {
35143367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
3515d4fba8b9Smrg			char *old = buf;
3516d4fba8b9Smrg
3517d4fba8b9Smrg			TRACE(("Setting selection(%s) with %s\n", used, buf));
3518dfb07bc7Smrg			screen->selection_time =
3519dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3520d4fba8b9Smrg
3521d4fba8b9Smrg			for (j = 0; j < n; ++j) {
3522d4fba8b9Smrg			    buf = old;
3523d4fba8b9Smrg			    ClearSelectionBuffer(screen, select_args[j]);
3524d4fba8b9Smrg			    while (*buf != '\0') {
3525d4fba8b9Smrg				AppendToSelectionBuffer(screen,
3526d4fba8b9Smrg							CharOf(*buf++),
3527d4fba8b9Smrg							select_args[j]);
3528d4fba8b9Smrg			    }
3529d4fba8b9Smrg			}
35303367019cSmrg			CompleteSelection(xw, select_args, n);
35313367019cSmrg		    }
353294644356Smrg		    free(select_args);
35333367019cSmrg		}
3534cd3331d0Smrg	    }
35353367019cSmrg	    free(used);
3536d522f475Smrg	}
3537d522f475Smrg    }
3538d522f475Smrg}
3539d522f475Smrg#endif /* OPT_PASTE64 */
3540d522f475Smrg
3541d522f475Smrg/***====================================================================***/
3542d522f475Smrg
3543d4fba8b9Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3544d4fba8b9Smrg			 || (xw->screen.utf8_title) \
3545d4fba8b9Smrg			 || (xw->screen.c1_printable))
3546cd3331d0Smrg
3547d522f475Smrgstatic Bool
3548fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3549d522f475Smrg{
3550cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3551d522f475Smrg    Bool result = False;
3552d522f475Smrg    Char *cp = *bufp;
3553d522f475Smrg    Char *next = cp;
3554d522f475Smrg
3555d522f475Smrg    (void) screen;
3556d522f475Smrg    (void) last;
3557d522f475Smrg
3558d522f475Smrg#if OPT_WIDE_CHARS
3559cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3560d522f475Smrg	PtyData data;
3561d522f475Smrg
35629a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3563d522f475Smrg	    if (data.utf_data != UCS_REPL
3564d522f475Smrg		&& (data.utf_data >= 128 ||
3565d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3566d522f475Smrg		next += (data.utf_size - 1);
3567d522f475Smrg		result = True;
3568d522f475Smrg	    } else {
3569d522f475Smrg		result = False;
3570d522f475Smrg	    }
3571d522f475Smrg	} else {
3572d522f475Smrg	    result = False;
3573d522f475Smrg	}
3574d522f475Smrg    } else
3575d522f475Smrg#endif
3576d522f475Smrg#if OPT_C1_PRINT
3577d522f475Smrg	if (screen->c1_printable
3578d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3579d522f475Smrg	result = True;
3580d522f475Smrg    } else
3581d522f475Smrg#endif
3582d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3583d522f475Smrg	result = True;
3584d522f475Smrg    }
3585d522f475Smrg    *bufp = next;
3586d522f475Smrg    return result;
3587d522f475Smrg}
3588d522f475Smrg
3589d522f475Smrg/***====================================================================***/
3590d522f475Smrg
3591d522f475Smrg/*
3592d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3593cd3331d0Smrg * array indices.  Compare with TermColors.
3594d522f475Smrg */
3595d522f475Smrgtypedef enum {
3596d522f475Smrg    OSC_TEXT_FG = 10
3597d522f475Smrg    ,OSC_TEXT_BG
3598d522f475Smrg    ,OSC_TEXT_CURSOR
3599d522f475Smrg    ,OSC_MOUSE_FG
3600d522f475Smrg    ,OSC_MOUSE_BG
3601d522f475Smrg#if OPT_TEK4014
3602d522f475Smrg    ,OSC_TEK_FG = 15
3603d522f475Smrg    ,OSC_TEK_BG
3604d522f475Smrg#endif
3605d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3606d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3607d522f475Smrg#endif
3608d522f475Smrg#if OPT_TEK4014
3609d522f475Smrg    ,OSC_TEK_CURSOR = 18
3610d522f475Smrg#endif
3611d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3612d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3613d522f475Smrg#endif
3614d522f475Smrg    ,OSC_NCOLORS
3615d522f475Smrg} OscTextColors;
3616d522f475Smrg
3617cd3331d0Smrg/*
3618cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3619cd3331d0Smrg */
3620cd3331d0Smrg#define OSC_RESET 100
3621cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3622cd3331d0Smrg
3623d522f475Smrgstatic Bool
3624d522f475SmrgGetOldColors(XtermWidget xw)
3625d522f475Smrg{
36269a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3627037a25ddSmrg	int i;
3628037a25ddSmrg
36299a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
36309a64e1c5Smrg	if (xw->work.oldColors == NULL) {
36313367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3632d522f475Smrg	    return (False);
3633d522f475Smrg	}
36349a64e1c5Smrg	xw->work.oldColors->which = 0;
3635d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
36369a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
36379a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3638d522f475Smrg	}
36399a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3640d522f475Smrg    }
3641d522f475Smrg    return (True);
3642d522f475Smrg}
3643d522f475Smrg
3644d522f475Smrgstatic int
3645d4fba8b9SmrgoppositeColor(XtermWidget xw, int n)
3646d522f475Smrg{
3647d4fba8b9Smrg    Boolean reversed = (xw->misc.re_verse);
3648d4fba8b9Smrg
3649d522f475Smrg    switch (n) {
3650d522f475Smrg    case TEXT_FG:
3651d4fba8b9Smrg	n = reversed ? TEXT_FG : TEXT_BG;
3652d522f475Smrg	break;
3653d522f475Smrg    case TEXT_BG:
3654d4fba8b9Smrg	n = reversed ? TEXT_BG : TEXT_FG;
3655d522f475Smrg	break;
3656d522f475Smrg    case MOUSE_FG:
3657d522f475Smrg	n = MOUSE_BG;
3658d522f475Smrg	break;
3659d522f475Smrg    case MOUSE_BG:
3660d522f475Smrg	n = MOUSE_FG;
3661d522f475Smrg	break;
3662d522f475Smrg#if OPT_TEK4014
3663d522f475Smrg    case TEK_FG:
3664d4fba8b9Smrg	n = reversed ? TEK_FG : TEK_BG;
3665d522f475Smrg	break;
3666d522f475Smrg    case TEK_BG:
3667d4fba8b9Smrg	n = reversed ? TEK_BG : TEK_FG;
3668d522f475Smrg	break;
3669d522f475Smrg#endif
3670d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3671d522f475Smrg    case HIGHLIGHT_FG:
3672d522f475Smrg	n = HIGHLIGHT_BG;
3673d522f475Smrg	break;
3674d522f475Smrg    case HIGHLIGHT_BG:
3675d522f475Smrg	n = HIGHLIGHT_FG;
3676d522f475Smrg	break;
3677d522f475Smrg#endif
3678d522f475Smrg    default:
3679d522f475Smrg	break;
3680d522f475Smrg    }
3681d522f475Smrg    return n;
3682d522f475Smrg}
3683d522f475Smrg
3684d4fba8b9Smrgstatic Bool
3685d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3686d522f475Smrg{
3687d4fba8b9Smrg    Bool result = False;
3688d4fba8b9Smrg
3689cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3690cd3331d0Smrg	XColor color;
3691cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3692cd3331d0Smrg	char buffer[80];
3693d522f475Smrg
3694cd3331d0Smrg	/*
3695cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3696cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3697cd3331d0Smrg	 * reporting the opposite color which would be used.
3698cd3331d0Smrg	 */
3699d4fba8b9Smrg	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3700cd3331d0Smrg
3701cd3331d0Smrg	GetOldColors(xw);
37029a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3703cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
3704cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3705cd3331d0Smrg		color.red,
3706cd3331d0Smrg		color.green,
3707cd3331d0Smrg		color.blue);
3708712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
37099a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3710cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3711cd3331d0Smrg	unparseputs(xw, buffer);
3712cd3331d0Smrg	unparseputc1(xw, final);
3713d4fba8b9Smrg	result = True;
3714cd3331d0Smrg    }
3715d4fba8b9Smrg    return result;
3716d522f475Smrg}
3717d522f475Smrg
3718d522f475Smrgstatic Bool
3719d4fba8b9SmrgUpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3720d522f475Smrg{
3721d522f475Smrg    int i;
3722d522f475Smrg
3723d522f475Smrg    /* if we were going to free old colors, this would be the place to
3724d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3725d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3726d522f475Smrg     * we could save some overhead this way.   The only case in which this
3727d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3728d522f475Smrg     * which case they can restart xterm
3729d522f475Smrg     */
3730d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3731d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
37329a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
37339a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
37349a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3735d522f475Smrg	    }
3736d522f475Smrg	    if (pNew->names[i]) {
37379a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3738d522f475Smrg	    }
37399a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3740d522f475Smrg	}
3741d522f475Smrg    }
3742d522f475Smrg    return (True);
3743d522f475Smrg}
3744d522f475Smrg
3745d522f475Smrg/*
3746d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3747d522f475Smrg * xterm is compiled.
3748d522f475Smrg */
3749d522f475Smrgstatic int
3750d522f475SmrgOscToColorIndex(OscTextColors mode)
3751d522f475Smrg{
3752d522f475Smrg    int result = 0;
3753d522f475Smrg
3754d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3755d522f475Smrg    switch (mode) {
3756d522f475Smrg	CASE(TEXT_FG);
3757d522f475Smrg	CASE(TEXT_BG);
3758d522f475Smrg	CASE(TEXT_CURSOR);
3759d522f475Smrg	CASE(MOUSE_FG);
3760d522f475Smrg	CASE(MOUSE_BG);
3761d522f475Smrg#if OPT_TEK4014
3762d522f475Smrg	CASE(TEK_FG);
3763d522f475Smrg	CASE(TEK_BG);
3764d522f475Smrg#endif
3765d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3766d522f475Smrg	CASE(HIGHLIGHT_BG);
3767d522f475Smrg	CASE(HIGHLIGHT_FG);
3768d522f475Smrg#endif
3769d522f475Smrg#if OPT_TEK4014
3770d522f475Smrg	CASE(TEK_CURSOR);
3771d522f475Smrg#endif
3772d522f475Smrg    case OSC_NCOLORS:
3773d522f475Smrg	break;
3774d522f475Smrg    }
3775d522f475Smrg    return result;
3776d522f475Smrg}
3777d522f475Smrg
3778d522f475Smrgstatic Bool
3779d522f475SmrgChangeColorsRequest(XtermWidget xw,
3780d522f475Smrg		    int start,
3781d522f475Smrg		    char *names,
3782d522f475Smrg		    int final)
3783d522f475Smrg{
3784d522f475Smrg    Bool result = False;
3785d522f475Smrg    ScrnColors newColors;
3786d522f475Smrg
3787d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3788d522f475Smrg
3789d522f475Smrg    if (GetOldColors(xw)) {
3790037a25ddSmrg	int i;
3791d4fba8b9Smrg	int queried = 0;
3792037a25ddSmrg
3793d522f475Smrg	newColors.which = 0;
3794d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3795d522f475Smrg	    newColors.names[i] = NULL;
3796d522f475Smrg	}
3797d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3798037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3799d522f475Smrg	    if (xw->misc.re_verse)
3800d4fba8b9Smrg		ndx = oppositeColor(xw, ndx);
3801d522f475Smrg
3802cd3331d0Smrg	    if (IsEmpty(names)) {
3803d522f475Smrg		newColors.names[ndx] = NULL;
3804d522f475Smrg	    } else {
3805037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3806037a25ddSmrg
3807d522f475Smrg		names = strchr(names, ';');
3808d522f475Smrg		if (names != NULL) {
3809d522f475Smrg		    *names++ = '\0';
3810d522f475Smrg		}
3811fa3f02f3Smrg		if (thisName != 0) {
3812fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3813d4fba8b9Smrg			if (ReportColorRequest(xw, ndx, final))
3814d4fba8b9Smrg			    ++queried;
38159a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
38169a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3817fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3818fa3f02f3Smrg		    }
3819d522f475Smrg		}
3820d522f475Smrg	    }
3821d522f475Smrg	}
3822d522f475Smrg
3823d522f475Smrg	if (newColors.which != 0) {
3824d522f475Smrg	    ChangeColors(xw, &newColors);
3825d522f475Smrg	    UpdateOldColors(xw, &newColors);
3826d4fba8b9Smrg	} else if (queried) {
3827d4fba8b9Smrg	    unparse_end(xw);
3828d522f475Smrg	}
3829d522f475Smrg	result = True;
3830d522f475Smrg    }
3831d522f475Smrg    return result;
3832d522f475Smrg}
3833d522f475Smrg
3834cd3331d0Smrgstatic Bool
3835cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3836cd3331d0Smrg		   int code)
3837cd3331d0Smrg{
3838cd3331d0Smrg    Bool result = False;
3839cd3331d0Smrg
3840dfb07bc7Smrg    (void) xw;
3841dfb07bc7Smrg    (void) code;
3842dfb07bc7Smrg
3843cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3844cd3331d0Smrg
3845cd3331d0Smrg#if OPT_COLOR_RES
3846cd3331d0Smrg    if (GetOldColors(xw)) {
3847037a25ddSmrg	ScrnColors newColors;
3848037a25ddSmrg	const char *thisName;
3849037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3850037a25ddSmrg
3851cd3331d0Smrg	if (xw->misc.re_verse)
3852d4fba8b9Smrg	    ndx = oppositeColor(xw, ndx);
3853cd3331d0Smrg
3854cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3855cd3331d0Smrg
3856cd3331d0Smrg	newColors.which = 0;
3857cd3331d0Smrg	newColors.names[ndx] = NULL;
3858cd3331d0Smrg
3859cd3331d0Smrg	if (thisName != 0
38609a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
38619a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3862cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3863cd3331d0Smrg
3864cd3331d0Smrg	    if (newColors.which != 0) {
3865cd3331d0Smrg		ChangeColors(xw, &newColors);
3866cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3867cd3331d0Smrg	    }
3868cd3331d0Smrg	}
3869cd3331d0Smrg	result = True;
3870cd3331d0Smrg    }
3871cd3331d0Smrg#endif
3872cd3331d0Smrg    return result;
3873cd3331d0Smrg}
3874cd3331d0Smrg
3875cd3331d0Smrg#if OPT_SHIFT_FONTS
3876cd3331d0Smrg/*
3877cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3878cd3331d0Smrg *
3879cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3880cd3331d0Smrg * the corresponding menu font entry.
3881cd3331d0Smrg */
3882cd3331d0Smrgstatic int
3883fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3884cd3331d0Smrg{
3885cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3886cd3331d0Smrg    int num = screen->menu_font_number;
3887cd3331d0Smrg    int rel = 0;
3888cd3331d0Smrg
3889cd3331d0Smrg    if (*++source == '+') {
3890cd3331d0Smrg	rel = 1;
3891cd3331d0Smrg	source++;
3892cd3331d0Smrg    } else if (*source == '-') {
3893cd3331d0Smrg	rel = -1;
3894cd3331d0Smrg	source++;
3895cd3331d0Smrg    }
3896cd3331d0Smrg
3897cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3898cd3331d0Smrg	int val = atoi(source);
3899cd3331d0Smrg	if (rel > 0)
3900cd3331d0Smrg	    rel = val;
3901cd3331d0Smrg	else if (rel < 0)
3902cd3331d0Smrg	    rel = -val;
3903cd3331d0Smrg	else
3904cd3331d0Smrg	    num = val;
3905cd3331d0Smrg    }
3906cd3331d0Smrg
3907cd3331d0Smrg    if (rel != 0) {
3908cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3909cd3331d0Smrg				     screen->menu_font_number, rel);
3910cd3331d0Smrg
3911cd3331d0Smrg    }
3912cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3913cd3331d0Smrg    *target = source;
3914cd3331d0Smrg    return num;
3915cd3331d0Smrg}
3916cd3331d0Smrg
3917cd3331d0Smrgstatic void
3918cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3919cd3331d0Smrg{
3920cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3921cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3922cd3331d0Smrg	Bool success = True;
3923cd3331d0Smrg	int num;
3924cb4a1343Smrg	String base = buf + 1;
3925cd3331d0Smrg	const char *name = 0;
3926cd3331d0Smrg
3927cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3928cd3331d0Smrg	if (num < 0
3929cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3930cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3931cd3331d0Smrg	    success = False;
3932cd3331d0Smrg	} else {
3933cd3331d0Smrg#if OPT_RENDERFONT
3934cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3935cd3331d0Smrg		name = getFaceName(xw, False);
3936cd3331d0Smrg	    } else
3937cd3331d0Smrg#endif
3938cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3939cd3331d0Smrg		success = False;
3940cd3331d0Smrg	    }
3941cd3331d0Smrg	}
3942cd3331d0Smrg
3943cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3944cd3331d0Smrg	unparseputs(xw, "50");
3945cd3331d0Smrg
3946cd3331d0Smrg	if (success) {
3947cd3331d0Smrg	    unparseputc(xw, ';');
3948cd3331d0Smrg	    if (buf >= base) {
3949cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3950cd3331d0Smrg		if (*buf != '\0') {
3951037a25ddSmrg		    char temp[10];
3952037a25ddSmrg
3953cd3331d0Smrg		    unparseputc(xw, '#');
3954cd3331d0Smrg		    sprintf(temp, "%d", num);
3955cd3331d0Smrg		    unparseputs(xw, temp);
3956cd3331d0Smrg		    if (*name != '\0')
3957cd3331d0Smrg			unparseputc(xw, ' ');
3958cd3331d0Smrg		}
3959cd3331d0Smrg	    }
3960cd3331d0Smrg	    unparseputs(xw, name);
3961cd3331d0Smrg	}
3962cd3331d0Smrg
3963cd3331d0Smrg	unparseputc1(xw, final);
3964cd3331d0Smrg	unparse_end(xw);
3965cd3331d0Smrg    }
3966cd3331d0Smrg}
3967cd3331d0Smrg
3968cd3331d0Smrgstatic void
3969cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3970cd3331d0Smrg{
3971cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3972cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3973cd3331d0Smrg	Bool success = True;
3974cd3331d0Smrg	int num;
3975cd3331d0Smrg	VTFontNames fonts;
3976cd3331d0Smrg	char *name;
3977cd3331d0Smrg
3978cd3331d0Smrg	/*
3979cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3980cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3981cd3331d0Smrg	 *
3982cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3983cd3331d0Smrg	 * to load the font entry.
3984cd3331d0Smrg	 */
3985cd3331d0Smrg	if (*buf == '#') {
3986cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3987cd3331d0Smrg
3988cd3331d0Smrg	    if (num < 0
3989cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3990cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3991cd3331d0Smrg		success = False;
3992cd3331d0Smrg	    } else {
3993cd3331d0Smrg		/*
3994cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3995cd3331d0Smrg		 * for a font specification within the control.
3996cd3331d0Smrg		 */
3997cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3998cd3331d0Smrg		    ++buf;
3999cd3331d0Smrg		}
4000cd3331d0Smrg		while (isspace(CharOf(*buf))) {
4001cd3331d0Smrg		    ++buf;
4002cd3331d0Smrg		}
4003cd3331d0Smrg#if OPT_RENDERFONT
4004cd3331d0Smrg		if (UsingRenderFont(xw)) {
4005c219fbebSmrg		    /* EMPTY */
4006c219fbebSmrg		    /* there is only one font entry to load */
4007c219fbebSmrg		    ;
4008cd3331d0Smrg		} else
4009cd3331d0Smrg#endif
4010cd3331d0Smrg		{
4011cd3331d0Smrg		    /*
4012cd3331d0Smrg		     * Normally there is no font specified in the control.
4013cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
4014cd3331d0Smrg		     */
4015cd3331d0Smrg		    if (*buf == '\0') {
4016cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
4017cd3331d0Smrg			    success = False;
4018cd3331d0Smrg			}
4019cd3331d0Smrg		    }
4020cd3331d0Smrg		}
4021cd3331d0Smrg	    }
4022cd3331d0Smrg	} else {
4023cd3331d0Smrg	    num = screen->menu_font_number;
4024cd3331d0Smrg	}
4025cd3331d0Smrg	name = x_strtrim(buf);
402694644356Smrg	if (screen->EscapeFontName()) {
402794644356Smrg	    FREE_STRING(screen->EscapeFontName());
402894644356Smrg	    screen->EscapeFontName() = 0;
402994644356Smrg	}
4030cd3331d0Smrg	if (success && !IsEmpty(name)) {
4031cd3331d0Smrg#if OPT_RENDERFONT
4032cd3331d0Smrg	    if (UsingRenderFont(xw)) {
4033cd3331d0Smrg		setFaceName(xw, name);
4034cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
4035cd3331d0Smrg	    } else
4036cd3331d0Smrg#endif
4037cd3331d0Smrg	    {
4038cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
4039cd3331d0Smrg		fonts.f_n = name;
4040cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
404194644356Smrg		if (num == screen->menu_font_number &&
404294644356Smrg		    num != fontMenu_fontescape) {
404394644356Smrg		    screen->EscapeFontName() = x_strdup(name);
404494644356Smrg		}
4045cd3331d0Smrg	    }
4046cd3331d0Smrg	} else {
4047cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
4048cd3331d0Smrg	}
404994644356Smrg	update_font_escape();
4050cd3331d0Smrg	free(name);
4051cd3331d0Smrg    }
4052cd3331d0Smrg}
4053cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
4054cd3331d0Smrg
4055d522f475Smrg/***====================================================================***/
4056d522f475Smrg
4057d522f475Smrgvoid
4058fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
4059d522f475Smrg{
4060cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4061d522f475Smrg    int mode;
4062d522f475Smrg    Char *cp;
4063d522f475Smrg    int state = 0;
4064d522f475Smrg    char *buf = 0;
4065cd3331d0Smrg    char temp[2];
4066cd3331d0Smrg#if OPT_ISO_COLORS
4067cd3331d0Smrg    int ansi_colors = 0;
4068cd3331d0Smrg#endif
4069cd3331d0Smrg    Bool need_data = True;
4070fa3f02f3Smrg    Bool optional_data = False;
4071d522f475Smrg
4072d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
4073d522f475Smrg
4074712a7ff4Smrg    (void) screen;
4075712a7ff4Smrg
4076d522f475Smrg    /*
4077d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
4078d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
4079d522f475Smrg     * with the same final character as the application sends to make this
4080d522f475Smrg     * work better with shell scripts, which may have trouble reading an
4081d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
4082d522f475Smrg     */
4083d522f475Smrg    mode = 0;
4084d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
4085d522f475Smrg	switch (state) {
4086d522f475Smrg	case 0:
4087d522f475Smrg	    if (isdigit(*cp)) {
4088d522f475Smrg		mode = 10 * mode + (*cp - '0');
4089d522f475Smrg		if (mode > 65535) {
4090d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
4091d522f475Smrg		    return;
4092d522f475Smrg		}
4093d522f475Smrg		break;
4094d4fba8b9Smrg	    } else {
4095d4fba8b9Smrg		switch (*cp) {
4096d4fba8b9Smrg		case 'I':
4097d4fba8b9Smrg		    xtermLoadIcon(xw, (char *) ++cp);
4098d4fba8b9Smrg		    return;
4099d4fba8b9Smrg		case 'l':
4100d4fba8b9Smrg		    ChangeTitle(xw, (char *) ++cp);
4101d4fba8b9Smrg		    return;
4102d4fba8b9Smrg		case 'L':
4103d4fba8b9Smrg		    ChangeIconName(xw, (char *) ++cp);
4104d4fba8b9Smrg		    return;
4105d4fba8b9Smrg		}
4106d522f475Smrg	    }
4107d522f475Smrg	    /* FALLTHRU */
4108d522f475Smrg	case 1:
4109d522f475Smrg	    if (*cp != ';') {
4110cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
4111cd3331d0Smrg		       (int) (cp - oscbuf)));
4112d522f475Smrg		return;
4113d522f475Smrg	    }
4114d522f475Smrg	    state = 2;
4115d522f475Smrg	    break;
4116d522f475Smrg	case 2:
4117d522f475Smrg	    buf = (char *) cp;
4118d522f475Smrg	    state = 3;
4119d522f475Smrg	    /* FALLTHRU */
4120d522f475Smrg	default:
4121cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4122d522f475Smrg		switch (mode) {
4123d522f475Smrg		case 0:
4124d522f475Smrg		case 1:
4125d522f475Smrg		case 2:
4126d522f475Smrg		    break;
4127d522f475Smrg		default:
4128d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
4129d522f475Smrg			   CharOf(*cp),
4130cd3331d0Smrg			   (int) (cp - oscbuf)));
4131d522f475Smrg		    return;
4132d522f475Smrg		}
4133d522f475Smrg	    }
4134d522f475Smrg	}
4135d522f475Smrg    }
4136cd3331d0Smrg
41373367019cSmrg    /*
41383367019cSmrg     * Check if the palette changed and there are no more immediate changes
41393367019cSmrg     * that could be deferred to the next repaint.
41403367019cSmrg     */
4141dfb07bc7Smrg    if (xw->work.palette_changed) {
41423367019cSmrg	switch (mode) {
4143d4fba8b9Smrg	case 03:		/* change X property */
41443367019cSmrg	case 30:		/* Konsole (unused) */
41453367019cSmrg	case 31:		/* Konsole (unused) */
41463367019cSmrg	case 50:		/* font operations */
41473367019cSmrg	case 51:		/* Emacs (unused) */
41483367019cSmrg#if OPT_PASTE64
41493367019cSmrg	case 52:		/* selection data */
41503367019cSmrg#endif
41513367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
4152dfb07bc7Smrg	    xw->work.palette_changed = False;
41533367019cSmrg	    xtermRepaint(xw);
41543367019cSmrg	    break;
4155d4fba8b9Smrg	default:
4156d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
4157d4fba8b9Smrg	    break;
41583367019cSmrg	}
41593367019cSmrg    }
41603367019cSmrg
4161cd3331d0Smrg    /*
4162cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
4163cd3331d0Smrg     * a special case.
4164cd3331d0Smrg     */
4165cd3331d0Smrg    switch (mode) {
416694644356Smrg    case 50:
4167cd3331d0Smrg#if OPT_ISO_COLORS
4168cd3331d0Smrg    case OSC_Reset(4):
4169cd3331d0Smrg    case OSC_Reset(5):
4170fa3f02f3Smrg	need_data = False;
4171fa3f02f3Smrg	optional_data = True;
4172fa3f02f3Smrg	break;
4173cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4174cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4175cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4176cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4177cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4178cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4179cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4180cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4181cd3331d0Smrg#endif
4182cd3331d0Smrg#if OPT_TEK4014
4183cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4184cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4185cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4186cd3331d0Smrg#endif
4187cd3331d0Smrg	need_data = False;
4188cd3331d0Smrg	break;
4189cd3331d0Smrg#endif
4190cd3331d0Smrg    default:
4191cd3331d0Smrg	break;
4192cd3331d0Smrg    }
4193cd3331d0Smrg
4194cd3331d0Smrg    /*
4195cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
4196cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
4197cd3331d0Smrg     */
4198cd3331d0Smrg    if (IsEmpty(buf)) {
4199cd3331d0Smrg	if (need_data) {
4200cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
4201cd3331d0Smrg	    return;
4202cd3331d0Smrg	}
4203cd3331d0Smrg	temp[0] = '\0';
4204cd3331d0Smrg	buf = temp;
4205fa3f02f3Smrg    } else if (!need_data && !optional_data) {
4206fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
4207d522f475Smrg	return;
42080d92cbfdSchristos    }
4209d522f475Smrg
4210d522f475Smrg    switch (mode) {
4211d522f475Smrg    case 0:			/* new icon name and title */
4212b7c89284Ssnj	ChangeIconName(xw, buf);
4213b7c89284Ssnj	ChangeTitle(xw, buf);
4214d522f475Smrg	break;
4215d522f475Smrg
4216d522f475Smrg    case 1:			/* new icon name only */
4217b7c89284Ssnj	ChangeIconName(xw, buf);
4218d522f475Smrg	break;
4219d522f475Smrg
4220d522f475Smrg    case 2:			/* new title only */
4221b7c89284Ssnj	ChangeTitle(xw, buf);
4222d522f475Smrg	break;
4223d522f475Smrg
422422d8e007Schristos#ifdef notdef
4225d522f475Smrg    case 3:			/* change X property */
4226cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
42270d92cbfdSchristos	    ChangeXprop(buf);
4228d522f475Smrg	break;
422922d8e007Schristos#endif
4230d522f475Smrg#if OPT_ISO_COLORS
4231cd3331d0Smrg    case 5:
4232cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4233cd3331d0Smrg	/* FALLTHRU */
4234d522f475Smrg    case 4:
4235d4fba8b9Smrg	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4236dfb07bc7Smrg	    xw->work.palette_changed = True;
4237cd3331d0Smrg	break;
423894644356Smrg    case 6:
423994644356Smrg	/* FALLTHRU */
424094644356Smrg    case OSC_Reset(6):
424194644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
424294644356Smrg	while (*buf != '\0') {
424394644356Smrg	    long which = 0;
424494644356Smrg	    long value = 0;
424594644356Smrg	    char *next;
424694644356Smrg	    if (*buf == ';') {
424794644356Smrg		++buf;
424894644356Smrg	    } else {
424994644356Smrg		which = strtol(buf, &next, 10);
4250037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
425194644356Smrg		    break;
425294644356Smrg		buf = next;
425394644356Smrg		if (*buf == ';')
425494644356Smrg		    ++buf;
425594644356Smrg	    }
425694644356Smrg	    if (*buf == ';') {
425794644356Smrg		++buf;
425894644356Smrg	    } else {
425994644356Smrg		value = strtol(buf, &next, 10);
4260dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
426194644356Smrg		    break;
426294644356Smrg		buf = next;
426394644356Smrg		if (*buf == ';')
426494644356Smrg		    ++buf;
426594644356Smrg	    }
426694644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
426794644356Smrg	    switch (which) {
426894644356Smrg	    case 0:
426994644356Smrg		screen->colorBDMode = (value != 0);
427094644356Smrg		break;
427194644356Smrg	    case 1:
427294644356Smrg		screen->colorULMode = (value != 0);
427394644356Smrg		break;
427494644356Smrg	    case 2:
427594644356Smrg		screen->colorBLMode = (value != 0);
427694644356Smrg		break;
427794644356Smrg	    case 3:
427894644356Smrg		screen->colorRVMode = (value != 0);
427994644356Smrg		break;
428094644356Smrg#if OPT_WIDE_ATTRS
428194644356Smrg	    case 4:
428294644356Smrg		screen->colorITMode = (value != 0);
428394644356Smrg		break;
428494644356Smrg#endif
428594644356Smrg	    default:
428694644356Smrg		TRACE(("...unknown colorXXMode\n"));
428794644356Smrg		break;
428894644356Smrg	    }
428994644356Smrg	}
429094644356Smrg	break;
4291cd3331d0Smrg    case OSC_Reset(5):
4292cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4293cd3331d0Smrg	/* FALLTHRU */
4294cd3331d0Smrg    case OSC_Reset(4):
4295cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4296dfb07bc7Smrg	    xw->work.palette_changed = True;
4297d522f475Smrg	break;
4298d522f475Smrg#endif
4299d522f475Smrg    case OSC_TEXT_FG:
4300d522f475Smrg    case OSC_TEXT_BG:
4301d522f475Smrg    case OSC_TEXT_CURSOR:
4302d522f475Smrg    case OSC_MOUSE_FG:
4303d522f475Smrg    case OSC_MOUSE_BG:
4304d522f475Smrg#if OPT_HIGHLIGHT_COLOR
4305d522f475Smrg    case OSC_HIGHLIGHT_BG:
4306cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
4307d522f475Smrg#endif
4308d522f475Smrg#if OPT_TEK4014
4309d522f475Smrg    case OSC_TEK_FG:
4310d522f475Smrg    case OSC_TEK_BG:
4311d522f475Smrg    case OSC_TEK_CURSOR:
4312d522f475Smrg#endif
4313cd3331d0Smrg	if (xw->misc.dynamicColors) {
4314d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
4315cd3331d0Smrg	}
4316cd3331d0Smrg	break;
4317cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4318cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4319cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4320cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4321cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4322cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4323cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4324cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4325cd3331d0Smrg#endif
4326cd3331d0Smrg#if OPT_TEK4014
4327cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4328cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4329cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4330cd3331d0Smrg#endif
4331cd3331d0Smrg	if (xw->misc.dynamicColors) {
4332cd3331d0Smrg	    ResetColorsRequest(xw, mode);
4333cd3331d0Smrg	}
4334d522f475Smrg	break;
4335d522f475Smrg
43368f44fb3bSmrg    case 22:
43378f44fb3bSmrg	xtermSetupPointer(xw, buf);
43388f44fb3bSmrg	break;
43398f44fb3bSmrg
4340d522f475Smrg    case 30:
4341d522f475Smrg    case 31:
4342d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
4343d522f475Smrg	break;
4344d522f475Smrg
4345d522f475Smrg#ifdef ALLOWLOGGING
4346d522f475Smrg    case 46:			/* new log file */
4347d522f475Smrg#ifdef ALLOWLOGFILECHANGES
4348d522f475Smrg	/*
4349d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
4350d522f475Smrg	 * arbitrary files accessible to the person running xterm.
4351d522f475Smrg	 */
4352037a25ddSmrg	if (strcmp(buf, "?")) {
4353037a25ddSmrg	    char *bp;
4354dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
4355d4fba8b9Smrg		free(screen->logfile);
4356037a25ddSmrg		screen->logfile = bp;
4357037a25ddSmrg		break;
4358037a25ddSmrg	    }
4359d522f475Smrg	}
4360d522f475Smrg#endif
4361cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4362cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4363d522f475Smrg	break;
4364d522f475Smrg#endif /* ALLOWLOGGING */
4365d522f475Smrg
4366d522f475Smrg    case 50:
4367d522f475Smrg#if OPT_SHIFT_FONTS
4368cd3331d0Smrg	if (*buf == '?') {
4369cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
4370cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
4371cd3331d0Smrg	    ChangeFontRequest(xw, buf);
4372d522f475Smrg	}
4373d522f475Smrg#endif /* OPT_SHIFT_FONTS */
4374d522f475Smrg	break;
4375d522f475Smrg    case 51:
4376d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
4377d522f475Smrg	break;
4378d522f475Smrg
4379d522f475Smrg#if OPT_PASTE64
4380d522f475Smrg    case 52:
4381cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
4382d522f475Smrg	break;
4383d522f475Smrg#endif
4384d522f475Smrg	/*
4385d522f475Smrg	 * One could write code to send back the display and host names,
4386d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
4387d522f475Smrg	 */
4388cd3331d0Smrg    default:
4389cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
4390cd3331d0Smrg	break;
4391d522f475Smrg    }
4392d522f475Smrg    unparse_end(xw);
4393d522f475Smrg}
4394d522f475Smrg
4395d522f475Smrg/*
4396d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
4397d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
4398d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4399d522f475Smrg * "real" terminals accept commas in the string definitions).
4400d522f475Smrg */
4401d522f475Smrgstatic int
4402cd3331d0Smrgudk_value(const char **cp)
4403d522f475Smrg{
4404cd3331d0Smrg    int result = -1;
4405d522f475Smrg
4406d522f475Smrg    for (;;) {
4407037a25ddSmrg	int c;
4408037a25ddSmrg
4409d522f475Smrg	if ((c = **cp) != '\0')
4410d522f475Smrg	    *cp = *cp + 1;
4411d522f475Smrg	if (c == ';' || c == '\0')
4412cd3331d0Smrg	    break;
4413cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
4414cd3331d0Smrg	    break;
4415d522f475Smrg    }
4416cd3331d0Smrg
4417cd3331d0Smrg    return result;
4418d522f475Smrg}
4419d522f475Smrg
4420d522f475Smrgvoid
44219a64e1c5Smrgreset_decudk(XtermWidget xw)
4422d522f475Smrg{
4423d522f475Smrg    int n;
4424d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
4425d4fba8b9Smrg	FreeAndNull(xw->work.user_keys[n].str);
4426d4fba8b9Smrg	xw->work.user_keys[n].len = 0;
4427d522f475Smrg    }
4428d522f475Smrg}
4429d522f475Smrg
4430d522f475Smrg/*
4431d522f475Smrg * Parse the data for DECUDK (user-defined keys).
4432d522f475Smrg */
4433d522f475Smrgstatic void
44349a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
4435d522f475Smrg{
4436d522f475Smrg    while (*cp) {
4437cd3331d0Smrg	const char *base = cp;
4438d4fba8b9Smrg	char *str = malloc(strlen(cp) + 3);
4439d522f475Smrg	unsigned key = 0;
4440d522f475Smrg	int len = 0;
4441d522f475Smrg
444294644356Smrg	if (str == NULL)
444394644356Smrg	    break;
444494644356Smrg
4445d522f475Smrg	while (isdigit(CharOf(*cp)))
44460d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
4447037a25ddSmrg
4448d522f475Smrg	if (*cp == '/') {
4449037a25ddSmrg	    int lo, hi;
4450037a25ddSmrg
4451d522f475Smrg	    cp++;
4452d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
4453d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
44540d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
4455d522f475Smrg	    }
4456d522f475Smrg	}
4457d522f475Smrg	if (len > 0 && key < MAX_UDK) {
44583367019cSmrg	    str[len] = '\0';
4459d4fba8b9Smrg	    free(xw->work.user_keys[key].str);
44609a64e1c5Smrg	    xw->work.user_keys[key].str = str;
44619a64e1c5Smrg	    xw->work.user_keys[key].len = len;
4462d4fba8b9Smrg	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4463d522f475Smrg	} else {
4464d522f475Smrg	    free(str);
4465d522f475Smrg	}
4466d522f475Smrg	if (*cp == ';')
4467d522f475Smrg	    cp++;
4468d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
4469d522f475Smrg	    break;
4470d522f475Smrg    }
4471d522f475Smrg}
4472d522f475Smrg
4473fa3f02f3Smrg/*
4474fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
4475fa3f02f3Smrg * interspersing with control characters, but have the string already.
4476fa3f02f3Smrg */
4477fa3f02f3Smrgstatic void
4478fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
4479fa3f02f3Smrg{
4480fa3f02f3Smrg    const char *cp = *string;
4481fa3f02f3Smrg    ParmType nparam = 0;
4482fa3f02f3Smrg    int last_empty = 1;
4483fa3f02f3Smrg
4484fa3f02f3Smrg    memset(params, 0, sizeof(*params));
4485fa3f02f3Smrg    while (*cp != '\0') {
4486fa3f02f3Smrg	Char ch = CharOf(*cp++);
4487fa3f02f3Smrg
4488fa3f02f3Smrg	if (isdigit(ch)) {
4489fa3f02f3Smrg	    last_empty = 0;
4490fa3f02f3Smrg	    if (nparam < NPARAM) {
4491fa3f02f3Smrg		params->a_param[nparam] =
4492fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
4493fa3f02f3Smrg				+ (ch - '0'));
4494fa3f02f3Smrg	    }
4495fa3f02f3Smrg	} else if (ch == ';') {
4496fa3f02f3Smrg	    last_empty = 1;
4497fa3f02f3Smrg	    nparam++;
4498fa3f02f3Smrg	} else if (ch < 32) {
4499fa3f02f3Smrg	    /* EMPTY */ ;
4500fa3f02f3Smrg	} else {
4501fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
4502fa3f02f3Smrg	    params->a_final = ch;
4503fa3f02f3Smrg	    break;
4504fa3f02f3Smrg	}
4505fa3f02f3Smrg    }
4506fa3f02f3Smrg
4507fa3f02f3Smrg    *string = cp;
4508fa3f02f3Smrg    if (!last_empty)
4509fa3f02f3Smrg	nparam++;
4510fa3f02f3Smrg    if (nparam > NPARAM)
4511fa3f02f3Smrg	params->a_nparam = NPARAM;
4512fa3f02f3Smrg    else
4513fa3f02f3Smrg	params->a_nparam = nparam;
4514fa3f02f3Smrg}
4515fa3f02f3Smrg
4516d522f475Smrg#if OPT_TRACE
4517d522f475Smrg#define SOFT_WIDE 10
4518d522f475Smrg#define SOFT_HIGH 20
4519d522f475Smrg
4520d522f475Smrgstatic void
4521fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
4522d522f475Smrg{
4523d522f475Smrg    char DscsName[8];
4524d522f475Smrg    int len;
4525d522f475Smrg    int Pfn = params->a_param[0];
4526d522f475Smrg    int Pcn = params->a_param[1];
4527d522f475Smrg    int Pe = params->a_param[2];
4528d522f475Smrg    int Pcmw = params->a_param[3];
4529d522f475Smrg    int Pw = params->a_param[4];
4530d522f475Smrg    int Pt = params->a_param[5];
4531d522f475Smrg    int Pcmh = params->a_param[6];
4532d522f475Smrg    int Pcss = params->a_param[7];
4533d522f475Smrg
4534d522f475Smrg    int start_char = Pcn + 0x20;
4535d522f475Smrg    int char_wide = ((Pcmw == 0)
4536d522f475Smrg		     ? (Pcss ? 6 : 10)
4537d522f475Smrg		     : (Pcmw > 4
4538d522f475Smrg			? Pcmw
4539d522f475Smrg			: (Pcmw + 3)));
4540d522f475Smrg    int char_high = ((Pcmh == 0)
45413367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
4542d522f475Smrg			? 10
4543d522f475Smrg			: 20)
4544d522f475Smrg		     : Pcmh);
4545d522f475Smrg    Char ch;
4546d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
4547d522f475Smrg    Bool first = True;
4548d522f475Smrg    Bool prior = False;
4549d522f475Smrg    int row = 0, col = 0;
4550d522f475Smrg
4551d522f475Smrg    TRACE(("Parsing DECDLD\n"));
4552d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
4553d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
4554d522f475Smrg    TRACE(("  erase control %d\n", Pe));
4555d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
4556d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
4557d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
4558d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
4559d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
4560d522f475Smrg
4561d522f475Smrg    if (Pfn > 1
4562d522f475Smrg	|| Pcn > 95
4563d522f475Smrg	|| Pe > 2
4564d522f475Smrg	|| Pcmw > 10
4565d522f475Smrg	|| Pcmw == 1
4566d522f475Smrg	|| Pt > 2
4567d522f475Smrg	|| Pcmh > 20
4568d522f475Smrg	|| Pcss > 1
4569d522f475Smrg	|| char_wide > SOFT_WIDE
4570d522f475Smrg	|| char_high > SOFT_HIGH) {
4571d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4572d522f475Smrg	return;
4573d522f475Smrg    }
4574d522f475Smrg
4575d522f475Smrg    len = 0;
4576d522f475Smrg    while (*string != '\0') {
4577d522f475Smrg	ch = CharOf(*string++);
4578d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4579d522f475Smrg	    if (len < 2)
4580b7c89284Ssnj		DscsName[len++] = (char) ch;
4581d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4582b7c89284Ssnj	    DscsName[len++] = (char) ch;
4583d522f475Smrg	    break;
4584d522f475Smrg	}
4585d522f475Smrg    }
4586d522f475Smrg    DscsName[len] = 0;
4587d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4588d522f475Smrg
4589d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4590d522f475Smrg    while (*string != '\0') {
4591d522f475Smrg	if (first) {
4592d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4593d522f475Smrg	    if (prior) {
4594d522f475Smrg		for (row = 0; row < char_high; ++row) {
4595d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4596d522f475Smrg		}
4597d522f475Smrg	    }
4598d522f475Smrg	    prior = False;
4599d522f475Smrg	    first = False;
4600d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4601d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4602d522f475Smrg		    bits[row][col] = '.';
4603d522f475Smrg		}
4604d522f475Smrg	    }
4605d522f475Smrg	    row = col = 0;
4606d522f475Smrg	}
4607d522f475Smrg	ch = CharOf(*string++);
4608d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4609d522f475Smrg	    int n;
4610d522f475Smrg
4611b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4612d522f475Smrg	    for (n = 0; n < 6; ++n) {
4613b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4614d522f475Smrg	    }
4615d522f475Smrg	    col += 1;
4616d522f475Smrg	    prior = True;
4617d522f475Smrg	} else if (ch == '/') {
4618d522f475Smrg	    row += 6;
4619d522f475Smrg	    col = 0;
4620d522f475Smrg	} else if (ch == ';') {
4621d522f475Smrg	    first = True;
4622d522f475Smrg	    ++start_char;
4623d522f475Smrg	}
4624d522f475Smrg    }
4625d522f475Smrg}
4626d522f475Smrg#else
4627d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4628d522f475Smrg#endif
4629d522f475Smrg
4630d4fba8b9Smrg#if OPT_DEC_RECTOPS
4631d4fba8b9Smrgstatic const char *
4632d4fba8b9Smrgskip_params(const char *cp)
4633d4fba8b9Smrg{
4634d4fba8b9Smrg    while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4635d4fba8b9Smrg	++cp;
4636d4fba8b9Smrg    return cp;
4637d4fba8b9Smrg}
4638d4fba8b9Smrg
4639d4fba8b9Smrgstatic int
4640d4fba8b9Smrgparse_int_param(const char **cp)
4641d4fba8b9Smrg{
4642d4fba8b9Smrg    int result = 0;
4643d4fba8b9Smrg    const char *s = *cp;
4644d4fba8b9Smrg    while (*s != '\0') {
4645d4fba8b9Smrg	if (*s == ';') {
4646d4fba8b9Smrg	    ++s;
4647d4fba8b9Smrg	    break;
4648d4fba8b9Smrg	} else if (*s >= '0' && *s <= '9') {
4649d4fba8b9Smrg	    result = (result * 10) + (*s++ - '0');
4650d4fba8b9Smrg	} else {
4651d4fba8b9Smrg	    s += strlen(s);
4652d4fba8b9Smrg	}
4653d4fba8b9Smrg    }
4654d4fba8b9Smrg    TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
4655d4fba8b9Smrg    *cp = s;
4656d4fba8b9Smrg    return result;
4657d4fba8b9Smrg}
4658d4fba8b9Smrg
4659d4fba8b9Smrgstatic int
4660d4fba8b9Smrgparse_chr_param(const char **cp)
4661d4fba8b9Smrg{
4662d4fba8b9Smrg    int result = 0;
4663d4fba8b9Smrg    const char *s = *cp;
4664d4fba8b9Smrg    if (*s != '\0') {
4665d4fba8b9Smrg	if ((result = CharOf(*s++)) != 0) {
4666d4fba8b9Smrg	    if (*s == ';') {
4667d4fba8b9Smrg		++s;
4668d4fba8b9Smrg	    } else if (*s != '\0') {
4669d4fba8b9Smrg		result = 0;
4670d4fba8b9Smrg	    }
4671d4fba8b9Smrg	}
4672d4fba8b9Smrg    }
4673d4fba8b9Smrg    TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
4674d4fba8b9Smrg    *cp = s;
4675d4fba8b9Smrg    return result;
4676d4fba8b9Smrg}
4677d4fba8b9Smrg
4678d4fba8b9Smrgstatic void
4679d4fba8b9Smrgrestore_DECCIR(XtermWidget xw, const char *cp)
4680d4fba8b9Smrg{
4681d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
4682d4fba8b9Smrg    int value;
4683d4fba8b9Smrg
4684d4fba8b9Smrg    /* row */
4685d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4686d4fba8b9Smrg	return;
4687d4fba8b9Smrg    screen->cur_row = (value - 1);
4688d4fba8b9Smrg
4689d4fba8b9Smrg    /* column */
4690d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4691d4fba8b9Smrg	return;
4692d4fba8b9Smrg    screen->cur_col = (value - 1);
4693d4fba8b9Smrg
4694d4fba8b9Smrg    /* page */
4695d4fba8b9Smrg    if (parse_int_param(&cp) != 1)
4696d4fba8b9Smrg	return;
4697d4fba8b9Smrg
4698d4fba8b9Smrg    /* rendition */
4699d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4700d4fba8b9Smrg	return;
4701d4fba8b9Smrg    UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4702d4fba8b9Smrg    xw->flags |= (value & 8) ? INVERSE : 0;
4703d4fba8b9Smrg    xw->flags |= (value & 4) ? BLINK : 0;
4704d4fba8b9Smrg    xw->flags |= (value & 2) ? UNDERLINE : 0;
4705d4fba8b9Smrg    xw->flags |= (value & 1) ? BOLD : 0;
4706d4fba8b9Smrg
4707d4fba8b9Smrg    /* attributes */
4708d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4709d4fba8b9Smrg	return;
4710d4fba8b9Smrg    screen->protected_mode &= ~DEC_PROTECT;
4711d4fba8b9Smrg    screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4712d4fba8b9Smrg
4713d4fba8b9Smrg    /* flags */
4714d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4715d4fba8b9Smrg	return;
4716d4fba8b9Smrg    screen->do_wrap = (value & 8) ? True : False;
4717d4fba8b9Smrg    screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4718d4fba8b9Smrg    UIntClr(xw->flags, ORIGIN);
4719d4fba8b9Smrg    xw->flags |= (value & 1) ? ORIGIN : 0;
4720d4fba8b9Smrg
4721d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4722d4fba8b9Smrg	return;
4723d4fba8b9Smrg    screen->curgl = (Char) value;
4724d4fba8b9Smrg
4725d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4726d4fba8b9Smrg	return;
4727d4fba8b9Smrg    screen->curgr = (Char) value;
4728d4fba8b9Smrg
4729d4fba8b9Smrg    /* character-set size */
4730d4fba8b9Smrg    if (parse_chr_param(&cp) != 0x4f)	/* works for xterm */
4731d4fba8b9Smrg	return;
4732d4fba8b9Smrg
4733d4fba8b9Smrg    /* SCS designators */
4734d4fba8b9Smrg    for (value = 0; value < NUM_GSETS; ++value) {
4735d4fba8b9Smrg	if (*cp == '%') {
4736d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '%', *++cp);
4737d4fba8b9Smrg	} else if (*cp != '\0') {
4738d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4739d4fba8b9Smrg	} else {
4740d4fba8b9Smrg	    return;
4741d4fba8b9Smrg	}
4742d4fba8b9Smrg	cp++;
4743d4fba8b9Smrg    }
4744d4fba8b9Smrg
4745d4fba8b9Smrg    TRACE(("...done DECCIR\n"));
4746d4fba8b9Smrg}
4747d4fba8b9Smrg
4748d4fba8b9Smrgstatic void
4749d4fba8b9Smrgrestore_DECTABSR(XtermWidget xw, const char *cp)
4750d4fba8b9Smrg{
4751d4fba8b9Smrg    int stop = 0;
4752d4fba8b9Smrg    Bool fail = False;
4753d4fba8b9Smrg
4754d4fba8b9Smrg    TabZonk(xw->tabs);
4755d4fba8b9Smrg    while (*cp != '\0' && !fail) {
4756d4fba8b9Smrg	if ((*cp) >= '0' && (*cp) <= '9') {
4757d4fba8b9Smrg	    stop = (stop * 10) + ((*cp) - '0');
4758d4fba8b9Smrg	} else if (*cp == '/') {
4759d4fba8b9Smrg	    --stop;
4760d4fba8b9Smrg	    if (OkTAB(stop)) {
4761d4fba8b9Smrg		TabSet(xw->tabs, stop);
4762d4fba8b9Smrg		stop = 0;
4763d4fba8b9Smrg	    } else {
4764d4fba8b9Smrg		fail = True;
4765d4fba8b9Smrg	    }
4766d4fba8b9Smrg	} else {
4767d4fba8b9Smrg	    fail = True;
4768d4fba8b9Smrg	}
4769d4fba8b9Smrg	++cp;
4770d4fba8b9Smrg    }
4771d4fba8b9Smrg    --stop;
4772d4fba8b9Smrg    if (OkTAB(stop))
4773d4fba8b9Smrg	TabSet(xw->tabs, stop);
4774d4fba8b9Smrg
4775d4fba8b9Smrg    TRACE(("...done DECTABSR\n"));
4776d4fba8b9Smrg}
4777d4fba8b9Smrg#endif
4778d4fba8b9Smrg
4779d522f475Smrgvoid
4780fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4781d522f475Smrg{
4782cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4783d522f475Smrg    char reply[BUFSIZ];
4784cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4785d522f475Smrg    Bool okay;
4786d522f475Smrg    ANSI params;
4787d4fba8b9Smrg#if OPT_DEC_RECTOPS
4788d4fba8b9Smrg    char psarg = '0';
4789d4fba8b9Smrg#endif
4790d522f475Smrg
4791cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4792d522f475Smrg
4793d522f475Smrg    if (dcslen != strlen(cp))
4794d522f475Smrg	/* shouldn't have nulls in the string */
4795d522f475Smrg	return;
4796d522f475Smrg
4797d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4798d522f475Smrg    case '$':			/* DECRQSS */
4799d522f475Smrg	okay = True;
4800d522f475Smrg
4801d522f475Smrg	cp++;
4802d4fba8b9Smrg	if (*cp == 'q') {
4803d4fba8b9Smrg	    *reply = '\0';
4804d4fba8b9Smrg	    cp++;
4805d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4806d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCA\n"));
4807d522f475Smrg		sprintf(reply, "%d%s",
4808d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4809d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4810d522f475Smrg			cp);
4811d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
48123367019cSmrg		if (screen->vtXX_level < 2) {
48133367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
48143367019cSmrg		    break;
48153367019cSmrg		}
4816d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCL\n"));
4817d522f475Smrg		sprintf(reply, "%d%s%s",
4818d522f475Smrg			(screen->vtXX_level ?
4819d522f475Smrg			 screen->vtXX_level : 1) + 60,
4820d522f475Smrg			(screen->vtXX_level >= 2)
4821d522f475Smrg			? (screen->control_eight_bits
4822d522f475Smrg			   ? ";0" : ";1")
4823d522f475Smrg			: "",
4824d522f475Smrg			cp);
4825d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4826d4fba8b9Smrg		TRACE(("DECRQSS -> DECSTBM\n"));
4827d522f475Smrg		sprintf(reply, "%d;%dr",
4828d522f475Smrg			screen->top_marg + 1,
4829d522f475Smrg			screen->bot_marg + 1);
48303367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
48313367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
4832d4fba8b9Smrg		    TRACE(("DECRQSS -> DECSLRM\n"));
48333367019cSmrg		    sprintf(reply, "%d;%ds",
48343367019cSmrg			    screen->lft_marg + 1,
48353367019cSmrg			    screen->rgt_marg + 1);
4836037a25ddSmrg		} else {
4837037a25ddSmrg		    okay = False;
48383367019cSmrg		}
4839d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4840d4fba8b9Smrg		TRACE(("DECRQSS -> SGR\n"));
4841d4fba8b9Smrg		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4842d522f475Smrg		strcat(reply, "m");
4843712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
48443367019cSmrg		int code = STEADY_BLOCK;
48453367019cSmrg		if (isCursorUnderline(screen))
48463367019cSmrg		    code = STEADY_UNDERLINE;
48473367019cSmrg		else if (isCursorBar(screen))
48483367019cSmrg		    code = STEADY_BAR;
48493367019cSmrg#if OPT_BLINK_CURS
485094644356Smrg		if (screen->cursor_blink_esc != 0)
48513367019cSmrg		    code -= 1;
48523367019cSmrg#endif
4853d4fba8b9Smrg		TRACE(("reply DECSCUSR\n"));
48543367019cSmrg		sprintf(reply, "%d%s", code, cp);
4855d4fba8b9Smrg	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4856d4fba8b9Smrg		sprintf(reply, "%d%s",
4857d4fba8b9Smrg			((screen->max_row > 24) ? screen->max_row : 24),
4858d4fba8b9Smrg			cp);
4859d4fba8b9Smrg		TRACE(("reply DECSLPP\n"));
4860d4fba8b9Smrg	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4861d4fba8b9Smrg		TRACE(("reply DECSCPP\n"));
4862d4fba8b9Smrg		sprintf(reply, "%d%s",
4863d4fba8b9Smrg			((xw->flags & IN132COLUMNS) ? 132 : 80),
4864d4fba8b9Smrg			cp);
4865d4fba8b9Smrg	    } else if (!strcmp(cp, "*|")) {	/* DECSNLS */
4866d4fba8b9Smrg		TRACE(("reply DECSNLS\n"));
4867d4fba8b9Smrg		sprintf(reply, "%d%s",
4868d4fba8b9Smrg			screen->max_row + 1,
4869d4fba8b9Smrg			cp);
48700d92cbfdSchristos	    } else {
4871d4fba8b9Smrg		okay = False;
487222d8e007Schristos	    }
4873d4fba8b9Smrg
4874d4fba8b9Smrg	    unparseputc1(xw, ANSI_DCS);
4875d4fba8b9Smrg	    unparseputc(xw, okay ? '1' : '0');
4876d4fba8b9Smrg	    unparseputc(xw, '$');
4877d4fba8b9Smrg	    unparseputc(xw, 'r');
4878d4fba8b9Smrg	    cp = reply;
4879d4fba8b9Smrg	    unparseputs(xw, cp);
4880d4fba8b9Smrg	    unparseputc1(xw, ANSI_ST);
4881d522f475Smrg	} else {
4882d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4883d522f475Smrg	}
4884d522f475Smrg	break;
4885d522f475Smrg    case '+':
4886d522f475Smrg	cp++;
4887cd3331d0Smrg	switch (*cp) {
4888d4fba8b9Smrg#if OPT_TCAP_QUERY
4889cd3331d0Smrg	case 'p':
4890cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4891cd3331d0Smrg		set_termcap(xw, cp + 1);
4892cd3331d0Smrg	    }
4893cd3331d0Smrg	    break;
4894cd3331d0Smrg	case 'q':
4895cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4896cd3331d0Smrg		Bool fkey;
4897cd3331d0Smrg		unsigned state;
4898cd3331d0Smrg		int code;
4899cd3331d0Smrg		const char *tmp;
4900cd3331d0Smrg		const char *parsed = ++cp;
4901d522f475Smrg
4902cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4903d522f475Smrg
4904cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4905b7c89284Ssnj
4906cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4907d522f475Smrg
4908cd3331d0Smrg		unparseputc(xw, '+');
4909cd3331d0Smrg		unparseputc(xw, 'r');
4910d522f475Smrg
4911cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4912cd3331d0Smrg		    if (cp == parsed)
4913cd3331d0Smrg			break;	/* no data found, error */
4914d522f475Smrg
4915cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4916cd3331d0Smrg			unparseputc(xw, *tmp);
4917d522f475Smrg
4918cd3331d0Smrg		    if (code >= 0) {
4919cd3331d0Smrg			unparseputc(xw, '=');
4920cd3331d0Smrg			screen->tc_query_code = code;
4921cd3331d0Smrg			screen->tc_query_fkey = fkey;
4922d522f475Smrg#if OPT_ISO_COLORS
4923cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4924cd3331d0Smrg			 * number of colors) */
4925cd3331d0Smrg			if (code == XK_COLORS) {
4926d4fba8b9Smrg			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
4927d4fba8b9Smrg			} else
4928d4fba8b9Smrg#if OPT_DIRECT_COLOR
4929d4fba8b9Smrg			if (code == XK_RGB) {
4930d4fba8b9Smrg			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
4931d4fba8b9Smrg				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
4932d4fba8b9Smrg				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
4933d4fba8b9Smrg				    unparseputn(xw, xw->rgb_widths[0]);
4934d4fba8b9Smrg				} else {
4935d4fba8b9Smrg				    char temp[1024];
4936d4fba8b9Smrg				    sprintf(temp, "%d/%d/%d",
4937d4fba8b9Smrg					    xw->rgb_widths[0],
4938d4fba8b9Smrg					    xw->rgb_widths[1],
4939d4fba8b9Smrg					    xw->rgb_widths[2]);
4940d4fba8b9Smrg				    unparseputs(xw, temp);
4941d4fba8b9Smrg				}
4942d4fba8b9Smrg			    } else {
4943d4fba8b9Smrg				unparseputs(xw, "-1");
4944d4fba8b9Smrg			    }
4945cd3331d0Smrg			} else
4946d4fba8b9Smrg#endif
4947cd3331d0Smrg#endif
4948cd3331d0Smrg			if (code == XK_TCAPNAME) {
4949c219fbebSmrg			    unparseputs(xw, resource.term_name);
4950cd3331d0Smrg			} else {
4951cd3331d0Smrg			    XKeyEvent event;
4952d4fba8b9Smrg			    memset(&event, 0, sizeof(event));
4953cd3331d0Smrg			    event.state = state;
4954cd3331d0Smrg			    Input(xw, &event, False);
4955cd3331d0Smrg			}
4956cd3331d0Smrg			screen->tc_query_code = -1;
4957cd3331d0Smrg		    } else {
4958cd3331d0Smrg			break;	/* no match found, error */
4959d522f475Smrg		    }
4960d522f475Smrg
4961d522f475Smrg		    cp = parsed;
4962cd3331d0Smrg		    if (*parsed == ';') {
4963cd3331d0Smrg			unparseputc(xw, *parsed++);
4964cd3331d0Smrg			cp = parsed;
4965cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4966cd3331d0Smrg		    }
4967d522f475Smrg		}
4968cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4969d522f475Smrg	    }
4970cd3331d0Smrg	    break;
4971d4fba8b9Smrg#endif
4972d4fba8b9Smrg#if OPT_XRES_QUERY
4973d4fba8b9Smrg	case 'Q':
4974d4fba8b9Smrg	    ++cp;
4975d4fba8b9Smrg	    if (AllowXResOps(xw)) {
4976d4fba8b9Smrg		Boolean first = True;
4977d4fba8b9Smrg		while (*cp != '\0') {
4978d4fba8b9Smrg		    const char *parsed = 0;
4979d4fba8b9Smrg		    const char *tmp;
4980d4fba8b9Smrg		    char *name = x_decode_hex(cp, &parsed);
4981d4fba8b9Smrg		    char *value;
4982d4fba8b9Smrg		    char *result;
4983d4fba8b9Smrg		    if (cp == parsed || name == NULL) {
4984d4fba8b9Smrg			free(name);
4985d4fba8b9Smrg			break;	/* no data found, error */
4986d4fba8b9Smrg		    }
4987d4fba8b9Smrg		    TRACE(("query-feature '%s'\n", name));
4988d4fba8b9Smrg		    if ((value = vt100ResourceToString(xw, name)) != 0) {
4989d4fba8b9Smrg			okay = True;	/* valid */
4990d4fba8b9Smrg		    } else {
4991d4fba8b9Smrg			okay = False;	/* invalid */
4992d4fba8b9Smrg		    }
4993d4fba8b9Smrg		    if (first) {
4994d4fba8b9Smrg			unparseputc1(xw, ANSI_DCS);
4995d4fba8b9Smrg			unparseputc(xw, okay ? '1' : '0');
4996d4fba8b9Smrg			unparseputc(xw, '+');
4997d4fba8b9Smrg			unparseputc(xw, 'R');
4998d4fba8b9Smrg			first = False;
4999d4fba8b9Smrg		    }
5000d4fba8b9Smrg
5001d4fba8b9Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
5002d4fba8b9Smrg			unparseputc(xw, *tmp);
5003d4fba8b9Smrg
5004d4fba8b9Smrg		    if (value != 0) {
5005d4fba8b9Smrg			unparseputc1(xw, '=');
5006d4fba8b9Smrg			result = x_encode_hex(value);
5007d4fba8b9Smrg			unparseputs(xw, result);
5008d4fba8b9Smrg		    } else {
5009d4fba8b9Smrg			result = NULL;
5010d4fba8b9Smrg		    }
5011d4fba8b9Smrg
5012d4fba8b9Smrg		    free(name);
5013d4fba8b9Smrg		    free(value);
5014d4fba8b9Smrg		    free(result);
5015d4fba8b9Smrg
5016d4fba8b9Smrg		    cp = parsed;
5017d4fba8b9Smrg		    if (*parsed == ';') {
5018d4fba8b9Smrg			unparseputc(xw, *parsed++);
5019d4fba8b9Smrg			cp = parsed;
5020d4fba8b9Smrg		    }
5021d4fba8b9Smrg		}
5022d4fba8b9Smrg		if (!first)
5023d4fba8b9Smrg		    unparseputc1(xw, ANSI_ST);
5024d4fba8b9Smrg	    }
5025d4fba8b9Smrg	    break;
5026d4fba8b9Smrg#endif
5027d522f475Smrg	}
5028d522f475Smrg	break;
5029d4fba8b9Smrg#if OPT_DEC_RECTOPS
5030d4fba8b9Smrg    case '1':
5031d4fba8b9Smrg	/* FALLTHRU */
5032d4fba8b9Smrg    case '2':
5033d4fba8b9Smrg	if (*skip_params(cp) == '$') {
5034d4fba8b9Smrg	    psarg = *cp++;
5035d4fba8b9Smrg	    if ((*cp++ == '$')
5036d4fba8b9Smrg		&& (*cp++ == 't')
5037d4fba8b9Smrg		&& (screen->vtXX_level >= 3)) {
5038d4fba8b9Smrg		switch (psarg) {
5039d4fba8b9Smrg		case '1':
5040d4fba8b9Smrg		    TRACE(("DECRSPS (DECCIR)\n"));
5041d4fba8b9Smrg		    restore_DECCIR(xw, cp);
5042d4fba8b9Smrg		    break;
5043d4fba8b9Smrg		case '2':
5044d4fba8b9Smrg		    TRACE(("DECRSPS (DECTABSR)\n"));
5045d4fba8b9Smrg		    restore_DECTABSR(xw, cp);
5046d4fba8b9Smrg		    break;
5047d4fba8b9Smrg		}
5048d4fba8b9Smrg	    }
5049d4fba8b9Smrg	    break;
5050d4fba8b9Smrg	}
5051d522f475Smrg#endif
5052d4fba8b9Smrg	/* FALLTHRU */
5053d522f475Smrg    default:
5054d4fba8b9Smrg	if (optRegisGraphics(screen) ||
5055d4fba8b9Smrg	    optSixelGraphics(screen) ||
5056fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
50570d92cbfdSchristos	    parse_ansi_params(&params, &cp);
50580d92cbfdSchristos	    switch (params.a_final) {
5059d4fba8b9Smrg	    case 'p':		/* ReGIS */
50609a64e1c5Smrg#if OPT_REGIS_GRAPHICS
5061d4fba8b9Smrg		if (optRegisGraphics(screen)) {
5062fa3f02f3Smrg		    parse_regis(xw, &params, cp);
5063fa3f02f3Smrg		}
50649a64e1c5Smrg#else
50659a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
50669a64e1c5Smrg#endif
5067fa3f02f3Smrg		break;
5068d4fba8b9Smrg	    case 'q':		/* sixel */
50699a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
5070d4fba8b9Smrg		if (optSixelGraphics(screen)) {
5071037a25ddSmrg		    (void) parse_sixel(xw, &params, cp);
5072fa3f02f3Smrg		}
50739a64e1c5Smrg#else
50749a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
5075fa3f02f3Smrg#endif
50769a64e1c5Smrg		break;
50770d92cbfdSchristos	    case '|':		/* DECUDK */
50789a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
50799a64e1c5Smrg		    if (params.a_param[0] == 0)
50809a64e1c5Smrg			reset_decudk(xw);
50819a64e1c5Smrg		    parse_decudk(xw, cp);
50829a64e1c5Smrg		}
50830d92cbfdSchristos		break;
508494644356Smrg	    case L_CURL:	/* DECDLD */
50859a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
50869a64e1c5Smrg		    parse_decdld(&params, cp);
50879a64e1c5Smrg		}
50880d92cbfdSchristos		break;
50890d92cbfdSchristos	    }
5090d522f475Smrg	}
5091d522f475Smrg	break;
5092d522f475Smrg    }
5093d522f475Smrg    unparse_end(xw);
5094d522f475Smrg}
5095d522f475Smrg
5096cb4a1343Smrg#if OPT_DEC_RECTOPS
5097cb4a1343Smrgenum {
5098cb4a1343Smrg    mdUnknown = 0,
5099cb4a1343Smrg    mdMaybeSet = 1,
5100cb4a1343Smrg    mdMaybeReset = 2,
5101cb4a1343Smrg    mdAlwaysSet = 3,
5102cb4a1343Smrg    mdAlwaysReset = 4
5103cb4a1343Smrg};
5104cb4a1343Smrg
5105cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
51063367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
5107cb4a1343Smrg
5108cb4a1343Smrg/*
5109cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
5110cb4a1343Smrg * 0 - not recognized
5111cb4a1343Smrg * 1 - set
5112cb4a1343Smrg * 2 - reset
5113cb4a1343Smrg * 3 - permanently set
5114cb4a1343Smrg * 4 - permanently reset
5115cb4a1343Smrg * Only one mode can be reported at a time.
5116cb4a1343Smrg */
5117cb4a1343Smrgvoid
5118d4fba8b9Smrgdo_ansi_rqm(XtermWidget xw, int nparams, int *params)
5119cb4a1343Smrg{
5120cb4a1343Smrg    ANSI reply;
5121cb4a1343Smrg    int count = 0;
5122cb4a1343Smrg
5123d4fba8b9Smrg    TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5124cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5125037a25ddSmrg
5126cb4a1343Smrg    if (nparams >= 1) {
5127d4fba8b9Smrg	int result = mdUnknown;
5128037a25ddSmrg
5129d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5130cb4a1343Smrg	switch (params[0]) {
5131cb4a1343Smrg	case 1:		/* GATM */
5132cb4a1343Smrg	    result = mdAlwaysReset;
5133cb4a1343Smrg	    break;
5134cb4a1343Smrg	case 2:
5135cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
5136cb4a1343Smrg	    break;
5137cb4a1343Smrg	case 3:		/* CRM */
5138cb4a1343Smrg	    result = mdMaybeReset;
5139cb4a1343Smrg	    break;
5140cb4a1343Smrg	case 4:
5141cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
5142cb4a1343Smrg	    break;
5143cb4a1343Smrg	case 5:		/* SRTM */
5144cb4a1343Smrg	case 7:		/* VEM */
5145cb4a1343Smrg	case 10:		/* HEM */
5146cb4a1343Smrg	case 11:		/* PUM */
5147cb4a1343Smrg	    result = mdAlwaysReset;
5148cb4a1343Smrg	    break;
5149cb4a1343Smrg	case 12:
5150cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
5151cb4a1343Smrg	    break;
5152cb4a1343Smrg	case 13:		/* FEAM */
5153cb4a1343Smrg	case 14:		/* FETM */
5154cb4a1343Smrg	case 15:		/* MATM */
5155cb4a1343Smrg	case 16:		/* TTM */
5156cb4a1343Smrg	case 17:		/* SATM */
5157cb4a1343Smrg	case 18:		/* TSM */
5158cb4a1343Smrg	case 19:		/* EBM */
5159cb4a1343Smrg	    result = mdAlwaysReset;
5160cb4a1343Smrg	    break;
5161cb4a1343Smrg	case 20:
5162cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
5163cb4a1343Smrg	    break;
5164cb4a1343Smrg	}
5165cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5166cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5167cb4a1343Smrg    }
5168cb4a1343Smrg    reply.a_type = ANSI_CSI;
5169cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5170cb4a1343Smrg    reply.a_inters = '$';
5171cb4a1343Smrg    reply.a_final = 'y';
5172cb4a1343Smrg    unparseseq(xw, &reply);
5173cb4a1343Smrg}
5174cb4a1343Smrg
5175cb4a1343Smrgvoid
5176d4fba8b9Smrgdo_dec_rqm(XtermWidget xw, int nparams, int *params)
5177cb4a1343Smrg{
5178cb4a1343Smrg    ANSI reply;
5179cb4a1343Smrg    int count = 0;
5180cb4a1343Smrg
5181d4fba8b9Smrg    TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5182cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5183037a25ddSmrg
5184cb4a1343Smrg    if (nparams >= 1) {
5185cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
5186d4fba8b9Smrg	int result = mdUnknown;
5187cb4a1343Smrg
5188d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5189d4fba8b9Smrg	switch ((DECSET_codes) params[0]) {
5190fa3f02f3Smrg	case srm_DECCKM:
5191cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5192cb4a1343Smrg	    break;
5193fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
5194cb4a1343Smrg#if OPT_VT52_MODE
51953367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
5196cb4a1343Smrg#else
5197cb4a1343Smrg	    result = mdMaybeSet;
5198cb4a1343Smrg#endif
5199cb4a1343Smrg	    break;
5200fa3f02f3Smrg	case srm_DECCOLM:
5201cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
5202cb4a1343Smrg	    break;
5203fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
5204cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5205cb4a1343Smrg	    break;
5206fa3f02f3Smrg	case srm_DECSCNM:
5207cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5208cb4a1343Smrg	    break;
5209fa3f02f3Smrg	case srm_DECOM:
5210cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
5211cb4a1343Smrg	    break;
5212fa3f02f3Smrg	case srm_DECAWM:
5213cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
5214cb4a1343Smrg	    break;
5215fa3f02f3Smrg	case srm_DECARM:
5216cb4a1343Smrg	    result = mdAlwaysReset;
5217cb4a1343Smrg	    break;
5218fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
5219cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5220cb4a1343Smrg	    break;
5221cb4a1343Smrg#if OPT_TOOLBAR
5222fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
5223cb4a1343Smrg	    result = MdBool(resource.toolBar);
5224cb4a1343Smrg	    break;
5225cb4a1343Smrg#endif
5226cb4a1343Smrg#if OPT_BLINK_CURS
5227d4fba8b9Smrg	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5228d4fba8b9Smrg	    result = MdBool(screen->cursor_blink_esc);
5229d4fba8b9Smrg	    break;
5230d4fba8b9Smrg	case srm_CURSOR_BLINK_OPS:
5231d4fba8b9Smrg	    switch (screen->cursor_blink) {
5232d4fba8b9Smrg	    case cbTrue:
5233d4fba8b9Smrg		result = mdMaybeSet;
5234d4fba8b9Smrg		break;
5235d4fba8b9Smrg	    case cbFalse:
5236d4fba8b9Smrg		result = mdMaybeReset;
5237d4fba8b9Smrg		break;
5238d4fba8b9Smrg	    case cbAlways:
5239d4fba8b9Smrg		result = mdAlwaysSet;
5240d4fba8b9Smrg		break;
5241d4fba8b9Smrg	    case cbLAST:
5242d4fba8b9Smrg		/* FALLTHRU */
5243d4fba8b9Smrg	    case cbNever:
5244d4fba8b9Smrg		result = mdAlwaysReset;
5245d4fba8b9Smrg		break;
5246d4fba8b9Smrg	    }
5247d4fba8b9Smrg	    break;
5248d4fba8b9Smrg	case srm_XOR_CURSOR_BLINKS:
5249d4fba8b9Smrg	    result = (screen->cursor_blink_xor
5250d4fba8b9Smrg		      ? mdAlwaysSet
5251d4fba8b9Smrg		      : mdAlwaysReset);
5252cb4a1343Smrg	    break;
5253cb4a1343Smrg#endif
5254fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
5255712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
5256cb4a1343Smrg	    break;
5257fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
5258712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
5259cb4a1343Smrg	    break;
5260fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5261cb4a1343Smrg	    result = MdBool(screen->cursor_set);
5262cb4a1343Smrg	    break;
5263fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
5264cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5265cb4a1343Smrg	    break;
5266cb4a1343Smrg#if OPT_SHIFT_FONTS
5267fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
5268cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
5269cb4a1343Smrg	    break;
5270cb4a1343Smrg#endif
5271cb4a1343Smrg#if OPT_TEK4014
5272fa3f02f3Smrg	case srm_DECTEK:
5273cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
5274cb4a1343Smrg	    break;
5275cb4a1343Smrg#endif
5276fa3f02f3Smrg	case srm_132COLS:
5277cb4a1343Smrg	    result = MdBool(screen->c132);
5278cb4a1343Smrg	    break;
5279fa3f02f3Smrg	case srm_CURSES_HACK:
5280cb4a1343Smrg	    result = MdBool(screen->curses);
5281cb4a1343Smrg	    break;
5282fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
5283d4fba8b9Smrg	    if (screen->vtXX_level >= 2) {
5284d4fba8b9Smrg		result = MdFlag(xw->flags, NATIONAL);
5285d4fba8b9Smrg	    } else {
5286d4fba8b9Smrg		result = 0;
5287d4fba8b9Smrg	    }
5288cb4a1343Smrg	    break;
5289fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
5290cb4a1343Smrg	    result = MdBool(screen->marginbell);
5291cb4a1343Smrg	    break;
5292d4fba8b9Smrg#if OPT_PRINT_GRAPHICS
5293d4fba8b9Smrg	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5294d4fba8b9Smrg	    result = MdBool(screen->graphics_expanded_print_mode);
5295d4fba8b9Smrg	    break;
5296d4fba8b9Smrg#endif
5297fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
5298d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5299d4fba8b9Smrg		result = MdFlag(xw->flags, REVERSEWRAP);
5300cb4a1343Smrg	    break;
5301d4fba8b9Smrg#if defined(ALLOWLOGGING)
5302fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
5303d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5304d4fba8b9Smrg#if defined(ALLOWLOGFILEONOFF)
5305d4fba8b9Smrg		result = MdBool(screen->logging);
5306d4fba8b9Smrg#else
5307d4fba8b9Smrg		result = ((MdBool(screen->logging) == mdMaybeSet)
5308d4fba8b9Smrg			  ? mdAlwaysSet
5309d4fba8b9Smrg			  : mdAlwaysReset);
5310d4fba8b9Smrg#endif
5311d4fba8b9Smrg	    break;
5312d4fba8b9Smrg#elif OPT_PRINT_GRAPHICS
5313d4fba8b9Smrg	case srm_DECGPBM:	/* Graphics Print Background Mode */
5314d4fba8b9Smrg	    result = MdBool(screen->graphics_print_background_mode);
5315cb4a1343Smrg	    break;
5316cb4a1343Smrg#endif
5317fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5318cb4a1343Smrg	    /* FALLTHRU */
5319fa3f02f3Smrg	case srm_OPT_ALTBUF:
5320cb4a1343Smrg	    result = MdBool(screen->whichBuf);
5321cb4a1343Smrg	    break;
5322d4fba8b9Smrg	case srm_ALTBUF:
5323d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5324d4fba8b9Smrg		result = MdBool(screen->whichBuf);
5325d4fba8b9Smrg	    break;
5326fa3f02f3Smrg	case srm_DECNKM:
5327cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5328cb4a1343Smrg	    break;
5329fa3f02f3Smrg	case srm_DECBKM:
5330cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5331cb4a1343Smrg	    break;
5332fa3f02f3Smrg	case srm_DECLRMM:
5333d4fba8b9Smrg	    if (screen->vtXX_level >= 4) {	/* VT420 */
5334d4fba8b9Smrg		result = MdFlag(xw->flags, LEFT_RIGHT);
5335d4fba8b9Smrg	    } else {
5336d4fba8b9Smrg		result = 0;
5337d4fba8b9Smrg	    }
53383367019cSmrg	    break;
5339fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
5340fa3f02f3Smrg	case srm_DECSDM:
5341fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5342fa3f02f3Smrg	    break;
5343fa3f02f3Smrg#endif
5344fa3f02f3Smrg	case srm_DECNCSM:
5345d4fba8b9Smrg	    if (screen->vtXX_level >= 5) {	/* VT510 */
5346d4fba8b9Smrg		result = MdFlag(xw->flags, NOCLEAR_COLM);
5347d4fba8b9Smrg	    } else {
5348d4fba8b9Smrg		result = 0;
5349d4fba8b9Smrg	    }
53503367019cSmrg	    break;
5351d4fba8b9Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5352cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5353cb4a1343Smrg	    break;
5354fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5355cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5356cb4a1343Smrg	    break;
5357fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
5358cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5359cb4a1343Smrg	    break;
5360fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
5361cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5362cb4a1343Smrg	    break;
5363cb4a1343Smrg#if OPT_FOCUS_EVENT
5364fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
5365cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
5366cb4a1343Smrg	    break;
5367cb4a1343Smrg#endif
5368fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
53693367019cSmrg	    /* FALLTHRU */
5370fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
53713367019cSmrg	    /* FALLTHRU */
5372fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
5373d4fba8b9Smrg	    /* FALLTHRU */
5374d4fba8b9Smrg	case srm_PIXEL_POSITION_MOUSE:
53753367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
53763367019cSmrg	    break;
5377fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
53783367019cSmrg	    result = MdBool(screen->alternateScroll);
5379cb4a1343Smrg	    break;
5380fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
5381cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
5382cb4a1343Smrg	    break;
5383fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5384cb4a1343Smrg	    result = MdBool(screen->scrollkey);
5385cb4a1343Smrg	    break;
5386fa3f02f3Smrg	case srm_EIGHT_BIT_META:
53873367019cSmrg	    result = MdBool(screen->eight_bit_meta);
5388cb4a1343Smrg	    break;
5389cb4a1343Smrg#if OPT_NUM_LOCK
5390fa3f02f3Smrg	case srm_REAL_NUMLOCK:
5391cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
5392cb4a1343Smrg	    break;
5393fa3f02f3Smrg	case srm_META_SENDS_ESC:
5394cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
5395cb4a1343Smrg	    break;
5396cb4a1343Smrg#endif
5397fa3f02f3Smrg	case srm_DELETE_IS_DEL:
5398d4fba8b9Smrg	    result = MdBool(xtermDeleteIsDEL(xw));
5399cb4a1343Smrg	    break;
5400cb4a1343Smrg#if OPT_NUM_LOCK
5401fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
5402cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
5403cb4a1343Smrg	    break;
5404cb4a1343Smrg#endif
5405fa3f02f3Smrg	case srm_KEEP_SELECTION:
5406cb4a1343Smrg	    result = MdBool(screen->keepSelection);
5407cb4a1343Smrg	    break;
5408fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
5409cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
5410cb4a1343Smrg	    break;
5411fa3f02f3Smrg	case srm_BELL_IS_URGENT:
5412cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
5413cb4a1343Smrg	    break;
5414fa3f02f3Smrg	case srm_POP_ON_BELL:
5415cb4a1343Smrg	    result = MdBool(screen->poponbell);
5416cb4a1343Smrg	    break;
5417d4fba8b9Smrg	case srm_KEEP_CLIPBOARD:
5418d4fba8b9Smrg	    result = MdBool(screen->keepClipboard);
5419d4fba8b9Smrg	    break;
5420d4fba8b9Smrg	case srm_ALLOW_ALTBUF:
5421d4fba8b9Smrg	    result = MdBool(xw->misc.titeInhibit);
5422d4fba8b9Smrg	    break;
5423d4fba8b9Smrg	case srm_SAVE_CURSOR:
5424cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
5425cb4a1343Smrg	    break;
5426cb4a1343Smrg#if OPT_TCAP_FKEYS
5427fa3f02f3Smrg	case srm_TCAP_FKEYS:
5428cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5429cb4a1343Smrg	    break;
5430cb4a1343Smrg#endif
5431cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
5432fa3f02f3Smrg	case srm_SUN_FKEYS:
5433cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5434cb4a1343Smrg	    break;
5435cb4a1343Smrg#endif
5436cb4a1343Smrg#if OPT_HP_FUNC_KEYS
5437fa3f02f3Smrg	case srm_HP_FKEYS:
5438cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5439cb4a1343Smrg	    break;
5440cb4a1343Smrg#endif
5441cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
5442fa3f02f3Smrg	case srm_SCO_FKEYS:
5443cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5444cb4a1343Smrg	    break;
5445cb4a1343Smrg#endif
5446fa3f02f3Smrg	case srm_LEGACY_FKEYS:
5447cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5448cb4a1343Smrg	    break;
5449cb4a1343Smrg#if OPT_SUNPC_KBD
5450fa3f02f3Smrg	case srm_VT220_FKEYS:
5451cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5452cb4a1343Smrg	    break;
5453cb4a1343Smrg#endif
5454d4fba8b9Smrg#if OPT_PASTE64 || OPT_READLINE
5455d4fba8b9Smrg	case srm_PASTE_IN_BRACKET:
5456d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5457d4fba8b9Smrg	    break;
5458d4fba8b9Smrg#endif
5459cb4a1343Smrg#if OPT_READLINE
5460fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
5461d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5462cb4a1343Smrg	    break;
5463fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
5464d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5465cb4a1343Smrg	    break;
5466fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
5467d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5468cb4a1343Smrg	    break;
5469fa3f02f3Smrg	case srm_PASTE_QUOTE:
5470d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5471cb4a1343Smrg	    break;
5472fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
5473d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5474cb4a1343Smrg	    break;
5475cb4a1343Smrg#endif /* OPT_READLINE */
5476d4fba8b9Smrg#if OPT_GRAPHICS
54779a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
54789a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
54799a64e1c5Smrg	    break;
54809a64e1c5Smrg#endif
54819a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
54829a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
54839a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
54849a64e1c5Smrg	    break;
54859a64e1c5Smrg#endif
54869a64e1c5Smrg	default:
54879a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
54889a64e1c5Smrg		   params[0]));
5489cb4a1343Smrg	}
5490cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5491cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5492d4fba8b9Smrg	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5493cb4a1343Smrg    }
5494cb4a1343Smrg    reply.a_type = ANSI_CSI;
5495cb4a1343Smrg    reply.a_pintro = '?';
5496cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5497cb4a1343Smrg    reply.a_inters = '$';
5498cb4a1343Smrg    reply.a_final = 'y';
5499cb4a1343Smrg    unparseseq(xw, &reply);
5500cb4a1343Smrg}
5501cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
5502cb4a1343Smrg
5503d522f475Smrgchar *
55049a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
5505d522f475Smrg{
5506d4fba8b9Smrg    char *result = NULL;
5507d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
55089a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
5509d4fba8b9Smrg	result = xw->work.user_keys[keycode].str;
5510d4fba8b9Smrg	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5511d4fba8b9Smrg    } else {
5512d4fba8b9Smrg	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5513d4fba8b9Smrg    }
5514d4fba8b9Smrg    return result;
5515d4fba8b9Smrg}
5516d4fba8b9Smrg
5517d4fba8b9Smrg#if OPT_REPORT_ICONS
5518d4fba8b9Smrgvoid
5519d4fba8b9Smrgreport_icons(const char *fmt, ...)
5520d4fba8b9Smrg{
5521d4fba8b9Smrg    if (resource.reportIcons) {
5522d4fba8b9Smrg	va_list ap;
5523d4fba8b9Smrg	va_start(ap, fmt);
5524d4fba8b9Smrg	vfprintf(stdout, fmt, ap);
5525d4fba8b9Smrg	va_end(ap);
5526d4fba8b9Smrg#if OPT_TRACE
5527d4fba8b9Smrg	va_start(ap, fmt);
5528d4fba8b9Smrg	TraceVA(fmt, ap);
5529d4fba8b9Smrg	va_end(ap);
5530d4fba8b9Smrg#endif
5531d522f475Smrg    }
5532d522f475Smrg}
5533d4fba8b9Smrg#endif
5534d522f475Smrg
55353367019cSmrg#ifdef HAVE_LIBXPM
55363367019cSmrg
55373367019cSmrg#ifndef PIXMAP_ROOTDIR
55383367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
55393367019cSmrg#endif
55403367019cSmrg
55413367019cSmrgtypedef struct {
55423367019cSmrg    const char *name;
55433367019cSmrg    const char *const *data;
55443367019cSmrg} XPM_DATA;
55453367019cSmrg
55463367019cSmrgstatic char *
5547d4fba8b9Smrgx_find_icon(char **work, int *state, const char *filename, const char *suffix)
55483367019cSmrg{
55493367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
55503367019cSmrg    const char *larger = "_48x48";
55513367019cSmrg    char *result = 0;
55523367019cSmrg
55533367019cSmrg    if (*state >= 0) {
55543367019cSmrg	if ((*state & 1) == 0)
55553367019cSmrg	    suffix = "";
55563367019cSmrg	if ((*state & 2) == 0)
55573367019cSmrg	    larger = "";
55583367019cSmrg	if ((*state & 4) == 0) {
55593367019cSmrg	    prefix = "";
55603367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
55613367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
55623367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
55633367019cSmrg	    *state = -1;
55643367019cSmrg	} else if (*state >= 8) {
55653367019cSmrg	    *state = -1;
55663367019cSmrg	}
55673367019cSmrg    }
55683367019cSmrg
55693367019cSmrg    if (*state >= 0) {
5570037a25ddSmrg	size_t length;
5571037a25ddSmrg
5572d4fba8b9Smrg	FreeAndNull(*work);
55733367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
55743367019cSmrg	    strlen(suffix);
55753367019cSmrg	if ((result = malloc(length)) != 0) {
55763367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
55773367019cSmrg	    *work = result;
55783367019cSmrg	}
55793367019cSmrg	*state += 1;
55803367019cSmrg    }
5581d4fba8b9Smrg    TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
55823367019cSmrg    return result;
55833367019cSmrg}
55843367019cSmrg
55853367019cSmrg#if OPT_BUILTIN_XPMS
5586d4fba8b9Smrg
55873367019cSmrgstatic const XPM_DATA *
5588d4fba8b9Smrgbuilt_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
55893367019cSmrg{
55903367019cSmrg    const XPM_DATA *result = 0;
55913367019cSmrg    if (!IsEmpty(find)) {
55923367019cSmrg	Cardinal n;
55933367019cSmrg	for (n = 0; n < length; ++n) {
55943367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
55953367019cSmrg		result = table + n;
5596d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[n].name));
55973367019cSmrg		break;
55983367019cSmrg	    }
55993367019cSmrg	}
56003367019cSmrg
56013367019cSmrg	/*
56023367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
56033367019cSmrg	 * which are all _HHxWW format.
56043367019cSmrg	 */
56053367019cSmrg	if (result == 0) {
56063367019cSmrg	    const char *base = table[0].name;
56073367019cSmrg	    const char *last = strchr(base, '_');
56083367019cSmrg	    if (last != 0
56093367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
56103367019cSmrg		result = table + length - 1;
5611d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[0].name));
56123367019cSmrg	    }
56133367019cSmrg	}
56143367019cSmrg    }
56153367019cSmrg    return result;
56163367019cSmrg}
5617d4fba8b9Smrg#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
56183367019cSmrg#endif /* OPT_BUILTIN_XPMS */
56193367019cSmrg
56203367019cSmrgtypedef enum {
56213367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
56223367019cSmrg    ,eHintNone
56233367019cSmrg    ,eHintSearch
56243367019cSmrg} ICON_HINT;
56253367019cSmrg#endif /* HAVE_LIBXPM */
56263367019cSmrg
56273367019cSmrgint
56283367019cSmrggetVisualDepth(XtermWidget xw)
56293367019cSmrg{
56303367019cSmrg    int result = 0;
56313367019cSmrg
5632fa3f02f3Smrg    if (getVisualInfo(xw)) {
5633fa3f02f3Smrg	result = xw->visInfo->depth;
56343367019cSmrg    }
56353367019cSmrg    return result;
56363367019cSmrg}
56373367019cSmrg
56383367019cSmrg/*
56393367019cSmrg * WM_ICON_SIZE should be honored if possible.
56403367019cSmrg */
56413367019cSmrgvoid
5642d4fba8b9SmrgxtermLoadIcon(XtermWidget xw, const char *icon_hint)
56433367019cSmrg{
56443367019cSmrg#ifdef HAVE_LIBXPM
56453367019cSmrg    Display *dpy = XtDisplay(xw);
56463367019cSmrg    Pixmap myIcon = 0;
56473367019cSmrg    Pixmap myMask = 0;
56483367019cSmrg    char *workname = 0;
5649d4fba8b9Smrg    ICON_HINT hint = eHintDefault;
5650fa3f02f3Smrg#include <builtin_icons.h>
56513367019cSmrg
5652d4fba8b9Smrg    ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5653d4fba8b9Smrg    if (!IsEmpty(icon_hint)) {
5654d4fba8b9Smrg	if (!x_strcasecmp(icon_hint, "none")) {
5655d4fba8b9Smrg	    hint = eHintNone;
5656d4fba8b9Smrg	} else {
5657d4fba8b9Smrg	    hint = eHintSearch;
5658d4fba8b9Smrg	}
5659d4fba8b9Smrg    }
56603367019cSmrg
56613367019cSmrg    if (hint == eHintSearch) {
56623367019cSmrg	int state = 0;
5663d4fba8b9Smrg	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) {
56643367019cSmrg	    Pixmap resIcon = 0;
56653367019cSmrg	    Pixmap shapemask = 0;
56663367019cSmrg	    XpmAttributes attributes;
5667d4fba8b9Smrg	    struct stat sb;
56683367019cSmrg
56693367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
56703367019cSmrg	    attributes.valuemask = XpmDepth;
56713367019cSmrg
5672d4fba8b9Smrg	    if (IsEmpty(workname)
5673d4fba8b9Smrg		|| lstat(workname, &sb) != 0
5674d4fba8b9Smrg		|| !S_ISREG(sb.st_mode)) {
5675d4fba8b9Smrg		TRACE(("...failure (no such file)\n"));
5676d4fba8b9Smrg	    } else {
5677d4fba8b9Smrg		int rc = XpmReadFileToPixmap(dpy,
5678d4fba8b9Smrg					     DefaultRootWindow(dpy),
5679d4fba8b9Smrg					     workname,
5680d4fba8b9Smrg					     &resIcon,
5681d4fba8b9Smrg					     &shapemask,
5682d4fba8b9Smrg					     &attributes);
5683d4fba8b9Smrg		if (rc == XpmSuccess) {
5684d4fba8b9Smrg		    myIcon = resIcon;
5685d4fba8b9Smrg		    myMask = shapemask;
5686d4fba8b9Smrg		    TRACE(("...success\n"));
5687d4fba8b9Smrg		    ReportIcons(("found/loaded icon-file %s\n", workname));
5688d4fba8b9Smrg		    break;
5689d4fba8b9Smrg		} else {
5690d4fba8b9Smrg		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5691d4fba8b9Smrg		}
56923367019cSmrg	    }
56933367019cSmrg	}
56943367019cSmrg    }
56953367019cSmrg
56963367019cSmrg    /*
56973367019cSmrg     * If no external file was found, look for the name in the built-in table.
56983367019cSmrg     * If that fails, just use the biggest mini-icon.
56993367019cSmrg     */
57003367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
57013367019cSmrg	char **data;
57023367019cSmrg#if OPT_BUILTIN_XPMS
57033367019cSmrg	const XPM_DATA *myData = 0;
5704d4fba8b9Smrg	myData = BuiltInXPM(mini_xterm_xpms);
57053367019cSmrg	if (myData == 0)
5706d4fba8b9Smrg	    myData = BuiltInXPM(filled_xterm_xpms);
57073367019cSmrg	if (myData == 0)
5708d4fba8b9Smrg	    myData = BuiltInXPM(xterm_color_xpms);
57093367019cSmrg	if (myData == 0)
5710d4fba8b9Smrg	    myData = BuiltInXPM(xterm_xpms);
57113367019cSmrg	if (myData == 0)
57123367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
571394644356Smrg	data = (char **) myData->data;
57143367019cSmrg#else
57153367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
57163367019cSmrg#endif
57173367019cSmrg	if (XpmCreatePixmapFromData(dpy,
57183367019cSmrg				    DefaultRootWindow(dpy),
57193367019cSmrg				    data,
5720d4fba8b9Smrg				    &myIcon, &myMask, 0) == 0) {
5721d4fba8b9Smrg	    ReportIcons(("loaded built-in pixmap icon\n"));
5722d4fba8b9Smrg	} else {
57233367019cSmrg	    myIcon = 0;
57243367019cSmrg	    myMask = 0;
57253367019cSmrg	}
57263367019cSmrg    }
57273367019cSmrg
57283367019cSmrg    if (myIcon != 0) {
57293367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
57303367019cSmrg	if (!hints)
57313367019cSmrg	    hints = XAllocWMHints();
57323367019cSmrg
57333367019cSmrg	if (hints) {
57343367019cSmrg	    hints->flags |= IconPixmapHint;
57353367019cSmrg	    hints->icon_pixmap = myIcon;
57363367019cSmrg	    if (myMask) {
57373367019cSmrg		hints->flags |= IconMaskHint;
57383367019cSmrg		hints->icon_mask = myMask;
57393367019cSmrg	    }
57403367019cSmrg
57413367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
57423367019cSmrg	    XFree(hints);
5743d4fba8b9Smrg	    ReportIcons(("updated window-manager hints\n"));
57443367019cSmrg	}
57453367019cSmrg    }
57463367019cSmrg
5747d4fba8b9Smrg    free(workname);
57483367019cSmrg
57493367019cSmrg#else
57503367019cSmrg    (void) xw;
5751d4fba8b9Smrg    (void) icon_hint;
57523367019cSmrg#endif
57533367019cSmrg}
57543367019cSmrg
57553367019cSmrgvoid
5756cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
5757d522f475Smrg{
5758d522f475Smrg    Arg args[1];
5759cd3331d0Smrg    Boolean changed = True;
5760d522f475Smrg    Widget w = CURRENT_EMU();
5761d522f475Smrg    Widget top = SHELL_OF(w);
5762d522f475Smrg
5763d4fba8b9Smrg    char *my_attr = NULL;
5764d4fba8b9Smrg    char *old_value = value;
5765d4fba8b9Smrg#if OPT_WIDE_CHARS
5766d4fba8b9Smrg    Boolean titleIsUTF8;
5767d4fba8b9Smrg#endif
5768d522f475Smrg
5769b7c89284Ssnj    if (!AllowTitleOps(xw))
5770d522f475Smrg	return;
5771d522f475Smrg
5772d4fba8b9Smrg    /*
5773d4fba8b9Smrg     * Ignore empty or too-long requests.
5774d4fba8b9Smrg     */
5775d4fba8b9Smrg    if (value == 0 || strlen(value) > 1000)
5776d4fba8b9Smrg	return;
5777d4fba8b9Smrg
5778cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
5779cd3331d0Smrg	const char *temp;
5780cd3331d0Smrg	char *test;
5781cd3331d0Smrg
5782d4fba8b9Smrg	/* this allocates a new string, if no error is detected */
5783cd3331d0Smrg	value = x_decode_hex(value, &temp);
5784d4fba8b9Smrg	if (value == 0 || *temp != '\0') {
57853367019cSmrg	    free(value);
5786cd3331d0Smrg	    return;
57873367019cSmrg	}
5788cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
5789cd3331d0Smrg	    if (CharOf(*test) < 32) {
5790cd3331d0Smrg		*test = '\0';
5791cd3331d0Smrg		break;
5792cd3331d0Smrg	    }
5793cd3331d0Smrg	}
5794cd3331d0Smrg    }
5795d4fba8b9Smrg#if OPT_WIDE_CHARS
5796d522f475Smrg    /*
5797d4fba8b9Smrg     * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
5798d4fba8b9Smrg     * the WM_NAME property, rather than doing this directly.  That relies on
5799d4fba8b9Smrg     * the application to tell it if the format should be something other than
5800d4fba8b9Smrg     * STRING, i.e., by setting the XtNtitleEncoding resource.
5801d4fba8b9Smrg     *
5802d4fba8b9Smrg     * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
5803d4fba8b9Smrg     * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
5804d4fba8b9Smrg     * added UTF8_STRING (the documentation for that was discarded by an Xorg
5805d4fba8b9Smrg     * developer, although the source-code provides this feature).
5806d4fba8b9Smrg     *
5807d4fba8b9Smrg     * Since X11R5, if the X11 library fails to store a text property as
5808d4fba8b9Smrg     * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
5809d4fba8b9Smrg     * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
5810d4fba8b9Smrg     * case, limit the resulting characters to the printable ISO-8859-1 set.
5811d522f475Smrg     */
5812d4fba8b9Smrg    titleIsUTF8 = isValidUTF8((Char *) value);
5813d4fba8b9Smrg    if (IsSetUtf8Title(xw) && titleIsUTF8) {
5814d4fba8b9Smrg	char *testc = malloc(strlen(value) + 1);
5815d4fba8b9Smrg	Char *nextc = (Char *) value;
5816d4fba8b9Smrg	Boolean ok8bit = True;
5817d522f475Smrg
5818d4fba8b9Smrg	if (testc != NULL) {
5819d4fba8b9Smrg	    /*
5820d4fba8b9Smrg	     * Check if the data fits in STRING.  Along the way, replace
5821d4fba8b9Smrg	     * control characters.
5822d4fba8b9Smrg	     */
5823d4fba8b9Smrg	    Char *lastc = (Char *) testc;
5824d4fba8b9Smrg	    while (*nextc != '\0') {
5825d4fba8b9Smrg		unsigned ch;
5826d4fba8b9Smrg		nextc = convertFromUTF8(nextc, &ch);
5827d4fba8b9Smrg		if (ch > 255) {
5828d4fba8b9Smrg		    ok8bit = False;
5829d4fba8b9Smrg		} else if (!IsLatin1(ch)) {
5830d4fba8b9Smrg		    ch = OnlyLatin1(ch);
5831d4fba8b9Smrg		}
5832d4fba8b9Smrg		*lastc++ = (Char) ch;
5833d4fba8b9Smrg	    }
5834d4fba8b9Smrg	    *lastc = '\0';
5835d4fba8b9Smrg	    if (ok8bit) {
5836d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
5837d4fba8b9Smrg		if (value != old_value)
5838d4fba8b9Smrg		    free(value);
5839d4fba8b9Smrg		value = testc;
5840d4fba8b9Smrg		titleIsUTF8 = False;
5841d4fba8b9Smrg	    } else {
5842d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
5843d4fba8b9Smrg		       "\t%s\n", value));
5844d4fba8b9Smrg		free(testc);
5845d4fba8b9Smrg		nextc = (Char *) value;
5846d4fba8b9Smrg		while (*nextc != '\0') {
5847d4fba8b9Smrg		    unsigned ch;
5848d4fba8b9Smrg		    Char *skip = convertFromUTF8(nextc, &ch);
5849d4fba8b9Smrg		    if (iswcntrl((wint_t) ch)) {
5850d4fba8b9Smrg			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
5851d4fba8b9Smrg		    }
5852d4fba8b9Smrg		    nextc = skip;
5853d4fba8b9Smrg		}
5854cd3331d0Smrg	    }
5855d522f475Smrg	}
5856d4fba8b9Smrg    } else
5857d4fba8b9Smrg#endif
5858d4fba8b9Smrg    {
5859d4fba8b9Smrg	Char *c1 = (Char *) value;
5860d4fba8b9Smrg
5861d4fba8b9Smrg	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
5862d4fba8b9Smrg	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
5863d4fba8b9Smrg	    *c1 = (Char) OnlyLatin1(*c1);
5864d4fba8b9Smrg	}
5865d4fba8b9Smrg    }
5866d4fba8b9Smrg
5867d4fba8b9Smrg    my_attr = x_strdup(attribute);
5868d4fba8b9Smrg
5869d4fba8b9Smrg    ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
5870d522f475Smrg
5871d522f475Smrg#if OPT_WIDE_CHARS
5872d4fba8b9Smrg    /*
5873d4fba8b9Smrg     * If we're running in UTF-8 mode, and have not been told that the
5874d4fba8b9Smrg     * title string is in UTF-8, it is likely that non-ASCII text in the
5875d4fba8b9Smrg     * string will be rejected because it is not printable in the current
5876d4fba8b9Smrg     * locale.  So we convert it to UTF-8, allowing the X library to
5877d4fba8b9Smrg     * convert it back.
5878d4fba8b9Smrg     */
5879d4fba8b9Smrg    TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
5880d4fba8b9Smrg    if (xtermEnvUTF8() && !titleIsUTF8) {
5881d4fba8b9Smrg	size_t limit = strlen(value);
5882d4fba8b9Smrg	Char *c1 = (Char *) value;
5883d4fba8b9Smrg	int n;
5884cd3331d0Smrg
5885d4fba8b9Smrg	for (n = 0; c1[n] != '\0'; ++n) {
5886d4fba8b9Smrg	    if (c1[n] > 127) {
5887d4fba8b9Smrg		Char *converted;
5888d4fba8b9Smrg		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
5889d4fba8b9Smrg		    Char *temp = converted;
5890d4fba8b9Smrg		    while (*c1 != 0) {
5891d4fba8b9Smrg			temp = convertToUTF8(temp, *c1++);
5892d522f475Smrg		    }
5893d4fba8b9Smrg		    *temp = 0;
5894d4fba8b9Smrg		    if (value != old_value)
5895d4fba8b9Smrg			free(value);
5896d4fba8b9Smrg		    value = (char *) converted;
5897d4fba8b9Smrg		    ReportIcons(("...converted{%s}\n", value));
5898d522f475Smrg		}
5899d4fba8b9Smrg		break;
5900d522f475Smrg	    }
5901d522f475Smrg	}
5902d4fba8b9Smrg    }
5903d522f475Smrg#endif
5904d522f475Smrg
5905d522f475Smrg#if OPT_SAME_NAME
5906d4fba8b9Smrg    /* If the attribute isn't going to change, then don't bother... */
5907d4fba8b9Smrg    if (resource.sameName) {
5908d4fba8b9Smrg	char *buf = 0;
5909d4fba8b9Smrg	XtSetArg(args[0], my_attr, &buf);
5910d4fba8b9Smrg	XtGetValues(top, args, 1);
5911d4fba8b9Smrg	TRACE(("...comparing{%s}\n", NonNull(buf)));
5912d4fba8b9Smrg	if (buf != 0 && strcmp(value, buf) == 0)
5913d4fba8b9Smrg	    changed = False;
5914d4fba8b9Smrg    }
5915d522f475Smrg#endif /* OPT_SAME_NAME */
5916d522f475Smrg
5917d4fba8b9Smrg    if (changed) {
5918d4fba8b9Smrg	ReportIcons(("...updating %s\n", my_attr));
5919d4fba8b9Smrg	ReportIcons(("...value is %s\n", value));
5920d4fba8b9Smrg	XtSetArg(args[0], my_attr, value);
5921d4fba8b9Smrg	XtSetValues(top, args, 1);
5922d4fba8b9Smrg    }
5923d522f475Smrg#if OPT_WIDE_CHARS
5924d4fba8b9Smrg    if (xtermEnvUTF8()) {
5925d4fba8b9Smrg	Display *dpy = XtDisplay(xw);
5926d4fba8b9Smrg	const char *propname = (!strcmp(my_attr, XtNtitle)
5927d4fba8b9Smrg				? "_NET_WM_NAME"
5928d4fba8b9Smrg				: "_NET_WM_ICON_NAME");
5929d4fba8b9Smrg	Atom my_atom = XInternAtom(dpy, propname, False);
5930d4fba8b9Smrg
5931d4fba8b9Smrg	if (my_atom != None) {
5932d4fba8b9Smrg	    changed = True;
5933d4fba8b9Smrg
5934d4fba8b9Smrg	    if (IsSetUtf8Title(xw)) {
5935d4fba8b9Smrg#if OPT_SAME_NAME
5936d4fba8b9Smrg		if (resource.sameName) {
5937d4fba8b9Smrg		    Atom actual_type;
5938d4fba8b9Smrg		    Atom requested_type = XA_UTF8_STRING(dpy);
5939d4fba8b9Smrg		    int actual_format = 0;
5940d4fba8b9Smrg		    long long_length = 1024;
5941d4fba8b9Smrg		    unsigned long nitems = 0;
5942d4fba8b9Smrg		    unsigned long bytes_after = 0;
5943d4fba8b9Smrg		    unsigned char *prop = 0;
5944d4fba8b9Smrg
5945d4fba8b9Smrg		    if (xtermGetWinProp(dpy,
5946d4fba8b9Smrg					VShellWindow(xw),
5947d4fba8b9Smrg					my_atom,
5948d4fba8b9Smrg					0L,
5949d4fba8b9Smrg					long_length,
5950d4fba8b9Smrg					requested_type,
5951d4fba8b9Smrg					&actual_type,
5952d4fba8b9Smrg					&actual_format,
5953d4fba8b9Smrg					&nitems,
5954d4fba8b9Smrg					&bytes_after,
5955d4fba8b9Smrg					&prop)
5956d4fba8b9Smrg			&& actual_type == requested_type
5957d4fba8b9Smrg			&& actual_format == 8
5958d4fba8b9Smrg			&& prop != 0
5959d4fba8b9Smrg			&& nitems == strlen(value)
5960d4fba8b9Smrg			&& memcmp(value, prop, nitems) == 0) {
5961d4fba8b9Smrg			changed = False;
5962cd3331d0Smrg		    }
5963cd3331d0Smrg		}
5964d4fba8b9Smrg#endif /* OPT_SAME_NAME */
5965d4fba8b9Smrg		if (changed) {
5966d4fba8b9Smrg		    ReportIcons(("...updating %s\n", propname));
5967d4fba8b9Smrg		    ReportIcons(("...value is %s\n", value));
5968d4fba8b9Smrg		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
5969d4fba8b9Smrg				    XA_UTF8_STRING(dpy), 8,
5970d4fba8b9Smrg				    PropModeReplace,
5971d4fba8b9Smrg				    (Char *) value,
5972d4fba8b9Smrg				    (int) strlen(value));
5973d4fba8b9Smrg		}
5974d4fba8b9Smrg	    } else {
5975d4fba8b9Smrg		ReportIcons(("...deleting %s\n", propname));
5976d4fba8b9Smrg		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5977d522f475Smrg	    }
5978d522f475Smrg	}
5979d522f475Smrg    }
5980d4fba8b9Smrg#endif
5981d4fba8b9Smrg    if (value != old_value) {
59823367019cSmrg	free(value);
59833367019cSmrg    }
59843367019cSmrg    free(my_attr);
59853367019cSmrg
5986cd3331d0Smrg    return;
5987d522f475Smrg}
5988d522f475Smrg
5989d522f475Smrgvoid
5990b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
5991d522f475Smrg{
5992cd3331d0Smrg    if (name == 0) {
59933367019cSmrg	name = emptyString;
59943367019cSmrg    }
59953367019cSmrg    if (!showZIconBeep(xw, name))
5996b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
5997d522f475Smrg}
5998d522f475Smrg
5999d522f475Smrgvoid
6000b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
6001d522f475Smrg{
6002b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
6003d522f475Smrg}
6004d522f475Smrg
6005712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
6006d522f475Smrg
6007d522f475Smrgvoid
6008d522f475SmrgChangeXprop(char *buf)
6009d522f475Smrg{
6010d522f475Smrg    Display *dpy = XtDisplay(toplevel);
6011d522f475Smrg    Window w = XtWindow(toplevel);
6012d522f475Smrg    XTextProperty text_prop;
6013d522f475Smrg    Atom aprop;
6014d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
6015d522f475Smrg
6016d522f475Smrg    if (pchEndPropName)
6017d522f475Smrg	*pchEndPropName = '\0';
6018d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
6019d522f475Smrg    if (pchEndPropName == NULL) {
6020d522f475Smrg	/* no "=value" given, so delete the property */
6021d522f475Smrg	XDeleteProperty(dpy, w, aprop);
6022d522f475Smrg    } else {
6023d522f475Smrg	text_prop.value = pchEndPropName + 1;
6024d522f475Smrg	text_prop.encoding = XA_STRING;
6025d522f475Smrg	text_prop.format = 8;
6026d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
6027d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
6028d522f475Smrg    }
6029d522f475Smrg}
6030d522f475Smrg
6031d522f475Smrg/***====================================================================***/
6032d522f475Smrg
6033d522f475Smrg/*
6034d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
6035d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
6036d522f475Smrg */
6037d522f475Smrgvoid
60389a64e1c5SmrgReverseOldColors(XtermWidget xw)
6039d522f475Smrg{
60409a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
6041d522f475Smrg    Pixel tmpPix;
6042d522f475Smrg    char *tmpName;
6043d522f475Smrg
6044d522f475Smrg    if (pOld) {
6045d4fba8b9Smrg	/* change text cursor, if necessary */
6046d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
6047d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
6048d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
60499a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
6050d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
6051d522f475Smrg	    }
6052d522f475Smrg	    if (pOld->names[TEXT_BG]) {
6053d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
6054d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
6055d522f475Smrg		}
6056d522f475Smrg	    }
6057d522f475Smrg	}
6058d522f475Smrg
6059d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
6060d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
6061d522f475Smrg
6062d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
6063d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
6064d522f475Smrg
6065d522f475Smrg#if OPT_TEK4014
6066d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
6067d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
6068d522f475Smrg#endif
6069d4fba8b9Smrg	FreeMarkGCs(xw);
6070d522f475Smrg    }
6071d522f475Smrg    return;
6072d522f475Smrg}
6073d522f475Smrg
6074d522f475SmrgBool
6075d522f475SmrgAllocateTermColor(XtermWidget xw,
6076d522f475Smrg		  ScrnColors * pNew,
6077d522f475Smrg		  int ndx,
6078cd3331d0Smrg		  const char *name,
6079cd3331d0Smrg		  Bool always)
6080d522f475Smrg{
6081cd3331d0Smrg    Bool result = False;
6082d522f475Smrg
6083cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
6084cd3331d0Smrg	XColor def;
6085cd3331d0Smrg	char *newName;
6086cd3331d0Smrg
6087712a7ff4Smrg	result = True;
6088712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
6089712a7ff4Smrg	    def.pixel = xw->old_foreground;
6090712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
6091712a7ff4Smrg	    def.pixel = xw->old_background;
60923367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
6093712a7ff4Smrg	    result = False;
6094712a7ff4Smrg	}
6095712a7ff4Smrg
6096712a7ff4Smrg	if (result
6097cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
6098712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
6099cd3331d0Smrg		free(pNew->names[ndx]);
6100712a7ff4Smrg	    }
6101cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
6102cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
6103712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
6104712a7ff4Smrg		   ndx, newName, def.pixel));
6105cd3331d0Smrg	} else {
6106cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
6107712a7ff4Smrg	    result = False;
6108cd3331d0Smrg	}
6109cd3331d0Smrg    }
6110cd3331d0Smrg    return result;
6111d522f475Smrg}
6112d522f475Smrg/***====================================================================***/
6113d522f475Smrg
6114d522f475Smrg/* ARGSUSED */
6115d522f475Smrgvoid
6116cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
6117d522f475Smrg{
61183367019cSmrg    if_DEBUG({
61193367019cSmrg	xtermWarning(s, a);
61203367019cSmrg    });
6121d522f475Smrg}
6122d522f475Smrg
6123d522f475Smrgconst char *
6124d522f475SmrgSysErrorMsg(int code)
6125d522f475Smrg{
612694644356Smrg    static const char unknown[] = "unknown error";
6127d4fba8b9Smrg    const char *s = strerror(code);
6128d522f475Smrg    return s ? s : unknown;
6129d522f475Smrg}
6130d522f475Smrg
6131d522f475Smrgconst char *
6132d522f475SmrgSysReasonMsg(int code)
6133d522f475Smrg{
6134d522f475Smrg    /* *INDENT-OFF* */
6135d522f475Smrg    static const struct {
6136d522f475Smrg	int code;
6137d522f475Smrg	const char *name;
6138d522f475Smrg    } table[] = {
6139d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
6140d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
6141d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
6142d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
6143d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
6144d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
6145d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
6146d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
6147d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
6148d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
6149d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
6150d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
6151d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
6152d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
6153d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
6154d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
6155d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
6156d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
6157d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
6158d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
6159d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6160d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
6161d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
6162d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6163d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6164d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6165d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
6166d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6167d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6168d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
6169d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6170d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6171d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6172d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
6173d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6174d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6175d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6176d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6177d522f475Smrg    };
6178d522f475Smrg    /* *INDENT-ON* */
6179d522f475Smrg
6180d522f475Smrg    Cardinal n;
6181d522f475Smrg    const char *result = "?";
6182d522f475Smrg
6183d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
6184d522f475Smrg	if (code == table[n].code) {
6185d522f475Smrg	    result = table[n].name;
6186d522f475Smrg	    break;
6187d522f475Smrg	}
6188d522f475Smrg    }
6189d522f475Smrg    return result;
6190d522f475Smrg}
6191d522f475Smrg
6192d522f475Smrgvoid
6193d522f475SmrgSysError(int code)
6194d522f475Smrg{
6195d522f475Smrg    int oerrno = errno;
6196d522f475Smrg
6197c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6198d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6199d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6200d522f475Smrg
6201d522f475Smrg    Cleanup(code);
6202d522f475Smrg}
6203d522f475Smrg
6204d522f475Smrgvoid
62053367019cSmrgNormalExit(void)
6206d522f475Smrg{
6207d522f475Smrg    static Bool cleaning;
6208d522f475Smrg
6209d522f475Smrg    /*
6210d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
6211d522f475Smrg     */
62123367019cSmrg    if (cleaning) {
62133367019cSmrg	hold_screen = 0;
62143367019cSmrg	return;
62153367019cSmrg    }
6216d522f475Smrg
62173367019cSmrg    cleaning = True;
62183367019cSmrg    need_cleanup = False;
6219d522f475Smrg
62203367019cSmrg    if (hold_screen) {
62213367019cSmrg	hold_screen = 2;
62223367019cSmrg	while (hold_screen) {
6223d4fba8b9Smrg	    xtermFlushDbe(term);
6224d4fba8b9Smrg	    xevents(term);
6225d4fba8b9Smrg	    Sleep(EVENT_DELAY);
6226d522f475Smrg	}
62273367019cSmrg    }
6228d522f475Smrg#if OPT_SESSION_MGT
62293367019cSmrg    if (resource.sessionMgt) {
62303367019cSmrg	XtVaSetValues(toplevel,
62313367019cSmrg		      XtNjoinSession, False,
62323367019cSmrg		      (void *) 0);
6233d522f475Smrg    }
62343367019cSmrg#endif
62353367019cSmrg    Cleanup(0);
62363367019cSmrg}
62373367019cSmrg
6238d4fba8b9Smrg#if USE_DOUBLE_BUFFER
6239d4fba8b9Smrgvoid
6240d4fba8b9SmrgxtermFlushDbe(XtermWidget xw)
6241d4fba8b9Smrg{
6242d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
6243d4fba8b9Smrg    if (resource.buffered && screen->needSwap) {
6244d4fba8b9Smrg	XdbeSwapInfo swap;
6245d4fba8b9Smrg	swap.swap_window = VWindow(screen);
6246d4fba8b9Smrg	swap.swap_action = XdbeCopied;
6247d4fba8b9Smrg	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6248d4fba8b9Smrg	XFlush(XtDisplay(xw));
6249d4fba8b9Smrg	screen->needSwap = 0;
6250d4fba8b9Smrg	ScrollBarDrawThumb(xw, 2);
6251d4fba8b9Smrg	X_GETTIMEOFDAY(&screen->buffered_at);
6252d4fba8b9Smrg    }
6253d4fba8b9Smrg}
6254d4fba8b9Smrg
6255d4fba8b9Smrgvoid
6256d4fba8b9SmrgxtermTimedDbe(XtermWidget xw)
6257d4fba8b9Smrg{
6258d4fba8b9Smrg    if (resource.buffered) {
6259d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
6260d4fba8b9Smrg	struct timeval now;
6261d4fba8b9Smrg	long elapsed;
6262d4fba8b9Smrg	long limit = DbeMsecs(xw);
6263d4fba8b9Smrg
6264d4fba8b9Smrg	X_GETTIMEOFDAY(&now);
6265d4fba8b9Smrg	if (screen->buffered_at.tv_sec) {
6266d4fba8b9Smrg	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6267d4fba8b9Smrg		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6268d4fba8b9Smrg	} else {
6269d4fba8b9Smrg	    elapsed = limit;
6270d4fba8b9Smrg	}
6271d4fba8b9Smrg	if (elapsed >= limit) {
6272d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
6273d4fba8b9Smrg	    xtermFlushDbe(xw);
6274d4fba8b9Smrg	}
6275d4fba8b9Smrg    }
6276d4fba8b9Smrg}
6277d4fba8b9Smrg#endif
6278d4fba8b9Smrg
62793367019cSmrg/*
62803367019cSmrg * cleanup by sending SIGHUP to client processes
62813367019cSmrg */
62823367019cSmrgvoid
62833367019cSmrgCleanup(int code)
62843367019cSmrg{
62853367019cSmrg    TScreen *screen = TScreenOf(term);
62863367019cSmrg
62873367019cSmrg    TRACE(("Cleanup %d\n", code));
6288d522f475Smrg
6289d522f475Smrg    if (screen->pid > 1) {
6290d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
6291d522f475Smrg    }
6292d522f475Smrg    Exit(code);
6293d522f475Smrg}
6294d522f475Smrg
6295fa3f02f3Smrg#ifndef S_IXOTH
6296fa3f02f3Smrg#define S_IXOTH 1
6297fa3f02f3Smrg#endif
6298fa3f02f3Smrg
6299fa3f02f3SmrgBoolean
6300fa3f02f3SmrgvalidProgram(const char *pathname)
6301fa3f02f3Smrg{
6302fa3f02f3Smrg    Boolean result = False;
6303fa3f02f3Smrg    struct stat sb;
6304fa3f02f3Smrg
6305fa3f02f3Smrg    if (!IsEmpty(pathname)
6306fa3f02f3Smrg	&& *pathname == '/'
6307fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
6308fa3f02f3Smrg	&& stat(pathname, &sb) == 0
6309fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
6310fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
6311fa3f02f3Smrg	result = True;
6312fa3f02f3Smrg    }
6313fa3f02f3Smrg    return result;
6314fa3f02f3Smrg}
6315fa3f02f3Smrg
6316d522f475Smrg#ifndef VMS
63173367019cSmrg#ifndef PATH_MAX
63183367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
63193367019cSmrg#endif
6320d522f475Smrgchar *
6321d522f475SmrgxtermFindShell(char *leaf, Bool warning)
6322d522f475Smrg{
63233367019cSmrg    char *s0;
6324d522f475Smrg    char *s;
6325d522f475Smrg    char *d;
6326d522f475Smrg    char *tmp;
6327d522f475Smrg    char *result = leaf;
63283367019cSmrg    Bool allocated = False;
6329d522f475Smrg
6330d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
63313367019cSmrg
63323367019cSmrg    if (!strncmp("./", result, (size_t) 2)
63333367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
63343367019cSmrg	size_t need = PATH_MAX;
63353367019cSmrg	size_t used = strlen(result) + 2;
63363367019cSmrg	char *buffer = malloc(used + need);
63373367019cSmrg	if (buffer != 0) {
63383367019cSmrg	    if (getcwd(buffer, need) != 0) {
63393367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
63403367019cSmrg		result = buffer;
63413367019cSmrg		allocated = True;
63423367019cSmrg	    } else {
63433367019cSmrg		free(buffer);
63443367019cSmrg	    }
63453367019cSmrg	}
63463367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
6347d522f475Smrg	/* find it in $PATH */
63483367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
63490d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
6350d522f475Smrg		Bool found = False;
6351d522f475Smrg		while (*s != '\0') {
6352d522f475Smrg		    strcpy(tmp, s);
6353d522f475Smrg		    for (d = tmp;; ++d) {
6354d522f475Smrg			if (*d == ':' || *d == '\0') {
6355d522f475Smrg			    int skip = (*d != '\0');
6356d522f475Smrg			    *d = '/';
6357d522f475Smrg			    strcpy(d + 1, leaf);
6358d522f475Smrg			    if (skip)
6359d522f475Smrg				++d;
6360d522f475Smrg			    s += (d - tmp);
6361fa3f02f3Smrg			    if (validProgram(tmp)) {
6362d522f475Smrg				result = x_strdup(tmp);
6363d522f475Smrg				found = True;
63643367019cSmrg				allocated = True;
6365d522f475Smrg			    }
6366d522f475Smrg			    break;
6367d522f475Smrg			}
6368d522f475Smrg		    }
6369d522f475Smrg		    if (found)
6370d522f475Smrg			break;
6371d522f475Smrg		}
6372d522f475Smrg		free(tmp);
6373d522f475Smrg	    }
63743367019cSmrg	    free(s0);
6375d522f475Smrg	}
6376d522f475Smrg    }
6377d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
6378fa3f02f3Smrg    if (!validProgram(result)) {
6379d522f475Smrg	if (warning)
63803367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
63813367019cSmrg	if (allocated)
63823367019cSmrg	    free(result);
6383d522f475Smrg	result = 0;
6384d522f475Smrg    }
63853367019cSmrg    /* be consistent, so that caller can always free the result */
63863367019cSmrg    if (result != 0 && !allocated)
63873367019cSmrg	result = x_strdup(result);
6388d522f475Smrg    return result;
6389d522f475Smrg}
6390d522f475Smrg#endif /* VMS */
6391d522f475Smrg
63920d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6393d522f475Smrg
63943367019cSmrg/*
63953367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
63963367019cSmrg * This could happen on some older machines due to the uneven standardization
63973367019cSmrg * process for the two functions.
63983367019cSmrg *
63993367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
64003367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
64013367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
64023367019cSmrg * could copy environ.
64033367019cSmrg */
64043367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
64053367019cSmrg#undef HAVE_PUTENV
64063367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
64073367019cSmrg#undef HAVE_UNSETENV
64083367019cSmrg#endif
64093367019cSmrg
6410d522f475Smrg/*
6411d522f475Smrg * copy the environment before Setenv'ing.
6412d522f475Smrg */
6413d522f475Smrgvoid
6414d522f475SmrgxtermCopyEnv(char **oldenv)
6415d522f475Smrg{
64163367019cSmrg#ifdef HAVE_PUTENV
64173367019cSmrg    (void) oldenv;
64183367019cSmrg#else
6419d522f475Smrg    unsigned size;
6420d522f475Smrg    char **newenv;
6421d522f475Smrg
6422d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
6423d522f475Smrg	;
6424d522f475Smrg    }
6425d522f475Smrg
6426d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
6427d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
6428d522f475Smrg    environ = newenv;
64293367019cSmrg#endif
64303367019cSmrg}
64313367019cSmrg
64323367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
64333367019cSmrgstatic int
64343367019cSmrgfindEnv(const char *var, int *lengthp)
64353367019cSmrg{
64363367019cSmrg    char *test;
64373367019cSmrg    int envindex = 0;
64383367019cSmrg    size_t len = strlen(var);
64393367019cSmrg    int found = -1;
64403367019cSmrg
64413367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
64423367019cSmrg
64433367019cSmrg    while ((test = environ[envindex]) != NULL) {
64443367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
64453367019cSmrg	    found = envindex;
64463367019cSmrg	    break;
64473367019cSmrg	}
64483367019cSmrg	envindex++;
64493367019cSmrg    }
64503367019cSmrg    *lengthp = envindex;
64513367019cSmrg    return found;
6452d522f475Smrg}
64533367019cSmrg#endif
6454d522f475Smrg
6455d522f475Smrg/*
6456d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6457d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
6458d522f475Smrg * This procedure assumes the memory for the first level of environ
6459d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
6460d522f475Smrg * to have to do a realloc().
6461d522f475Smrg */
6462d522f475Smrgvoid
6463cd3331d0SmrgxtermSetenv(const char *var, const char *value)
6464d522f475Smrg{
6465d522f475Smrg    if (value != 0) {
64663367019cSmrg#ifdef HAVE_PUTENV
64673367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
64683367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
64693367019cSmrg	if (both) {
64703367019cSmrg	    sprintf(both, "%s=%s", var, value);
64713367019cSmrg	    putenv(both);
64723367019cSmrg	}
64733367019cSmrg#else
6474d522f475Smrg	size_t len = strlen(var);
64753367019cSmrg	int envindex;
64763367019cSmrg	int found = findEnv(var, &envindex);
6477d522f475Smrg
6478d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6479d522f475Smrg
6480d522f475Smrg	if (found < 0) {
6481d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
6482d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
6483d522f475Smrg
6484d522f475Smrg	    if (need > have) {
6485d522f475Smrg		char **newenv;
6486d522f475Smrg		newenv = TypeMallocN(char *, need);
6487d522f475Smrg		if (newenv == 0) {
64883367019cSmrg		    xtermWarning("Cannot increase environment\n");
6489d522f475Smrg		    return;
6490d522f475Smrg		}
6491d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
6492d522f475Smrg		free(environ);
6493d522f475Smrg		environ = newenv;
6494d522f475Smrg	    }
6495d522f475Smrg
6496d522f475Smrg	    found = envindex;
6497d522f475Smrg	    environ[found + 1] = NULL;
6498d522f475Smrg	    environ = environ;
6499d522f475Smrg	}
6500d522f475Smrg
6501d4fba8b9Smrg	environ[found] = malloc(2 + len + strlen(value));
6502d522f475Smrg	if (environ[found] == 0) {
65033367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
6504d522f475Smrg	    return;
6505d522f475Smrg	}
6506d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
65073367019cSmrg#endif
6508d522f475Smrg    }
6509d522f475Smrg}
6510d522f475Smrg
65113367019cSmrgvoid
65123367019cSmrgxtermUnsetenv(const char *var)
65133367019cSmrg{
65143367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
65153367019cSmrg#ifdef HAVE_UNSETENV
65163367019cSmrg    unsetenv(var);
65173367019cSmrg#else
65183367019cSmrg    {
65193367019cSmrg	int ignore;
65203367019cSmrg	int item = findEnv(var, &ignore);
65213367019cSmrg	if (item >= 0) {
65223367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
65233367019cSmrg		++item;
65243367019cSmrg	    }
65253367019cSmrg	}
65263367019cSmrg    }
65273367019cSmrg#endif
65283367019cSmrg}
65293367019cSmrg
6530d522f475Smrg/*ARGSUSED*/
6531d522f475Smrgint
65329a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
6533d522f475Smrg{
65343367019cSmrg    xtermWarning("warning, error event received:\n");
6535d4fba8b9Smrg    TRACE_X_ERR(d, ev);
6536d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6537d522f475Smrg    Exit(ERROR_XERROR);
6538d522f475Smrg    return 0;			/* appease the compiler */
6539d522f475Smrg}
6540d522f475Smrg
6541712a7ff4Smrgvoid
6542712a7ff4Smrgice_error(IceConn iceConn)
6543712a7ff4Smrg{
6544712a7ff4Smrg    (void) iceConn;
6545712a7ff4Smrg
65463367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
65473367019cSmrg		 (long) getpid(), errno);
6548712a7ff4Smrg
6549712a7ff4Smrg    Exit(ERROR_ICEERROR);
6550712a7ff4Smrg}
6551712a7ff4Smrg
6552d522f475Smrg/*ARGSUSED*/
6553d522f475Smrgint
6554fa3f02f3Smrgxioerror(Display *dpy)
6555d522f475Smrg{
6556d522f475Smrg    int the_error = errno;
6557d522f475Smrg
65583367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
65593367019cSmrg		 the_error, SysErrorMsg(the_error),
65603367019cSmrg		 DisplayString(dpy));
6561d522f475Smrg
6562d522f475Smrg    Exit(ERROR_XIOERROR);
6563d522f475Smrg    return 0;			/* appease the compiler */
6564d522f475Smrg}
6565d522f475Smrg
6566728ff447Schristosvoid
6567d522f475Smrgxt_error(String message)
6568d522f475Smrg{
65693367019cSmrg    xtermWarning("Xt error: %s\n", message);
6570d522f475Smrg
6571d522f475Smrg    /*
6572d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
6573d522f475Smrg     */
6574d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
65753367019cSmrg	xtermWarning("DISPLAY is not set\n");
6576d522f475Smrg    }
6577d522f475Smrg    exit(1);
6578d522f475Smrg}
6579d522f475Smrg
6580d522f475Smrgint
6581d522f475SmrgXStrCmp(char *s1, char *s2)
6582d522f475Smrg{
6583d522f475Smrg    if (s1 && s2)
6584d522f475Smrg	return (strcmp(s1, s2));
6585d522f475Smrg    if (s1 && *s1)
6586d522f475Smrg	return (1);
6587d522f475Smrg    if (s2 && *s2)
6588d522f475Smrg	return (-1);
6589d522f475Smrg    return (0);
6590d522f475Smrg}
6591d522f475Smrg
6592d522f475Smrg#if OPT_TEK4014
6593d522f475Smrgstatic void
6594fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
6595d522f475Smrg{
6596d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
6597d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
6598d522f475Smrg    XWithdrawWindow(dpy, w, scr);
6599d522f475Smrg    return;
6600d522f475Smrg}
6601d522f475Smrg#endif
6602d522f475Smrg
6603d522f475Smrgvoid
6604d522f475Smrgset_vt_visibility(Bool on)
6605d522f475Smrg{
6606c219fbebSmrg    XtermWidget xw = term;
6607c219fbebSmrg    TScreen *screen = TScreenOf(xw);
6608d522f475Smrg
6609d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
6610d522f475Smrg    if (on) {
6611c219fbebSmrg	if (!screen->Vshow && xw) {
6612c219fbebSmrg	    VTInit(xw);
6613c219fbebSmrg	    XtMapWidget(XtParent(xw));
6614d522f475Smrg#if OPT_TOOLBAR
6615d522f475Smrg	    /* we need both of these during initialization */
6616c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
6617d522f475Smrg	    ShowToolbar(resource.toolBar);
6618d522f475Smrg#endif
6619d522f475Smrg	    screen->Vshow = True;
6620d522f475Smrg	}
6621d522f475Smrg    }
6622d522f475Smrg#if OPT_TEK4014
6623d522f475Smrg    else {
6624c219fbebSmrg	if (screen->Vshow && xw) {
6625c219fbebSmrg	    withdraw_window(XtDisplay(xw),
6626c219fbebSmrg			    VShellWindow(xw),
6627c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
6628d522f475Smrg	    screen->Vshow = False;
6629d522f475Smrg	}
6630d522f475Smrg    }
6631d522f475Smrg    set_vthide_sensitivity();
6632d522f475Smrg    set_tekhide_sensitivity();
6633d522f475Smrg    update_vttekmode();
6634d522f475Smrg    update_tekshow();
6635d522f475Smrg    update_vtshow();
6636d522f475Smrg#endif
6637d522f475Smrg    return;
6638d522f475Smrg}
6639d522f475Smrg
6640d522f475Smrg#if OPT_TEK4014
6641d522f475Smrgvoid
6642d522f475Smrgset_tek_visibility(Bool on)
6643d522f475Smrg{
6644d4fba8b9Smrg    XtermWidget xw = term;
6645d4fba8b9Smrg
6646d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
6647d522f475Smrg
6648d522f475Smrg    if (on) {
6649d4fba8b9Smrg	if (!TEK4014_SHOWN(xw)) {
6650cd3331d0Smrg	    if (tekWidget == 0) {
6651cd3331d0Smrg		TekInit();	/* will exit on failure */
6652cd3331d0Smrg	    }
6653cd3331d0Smrg	    if (tekWidget != 0) {
6654cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
6655cd3331d0Smrg		XtRealizeWidget(tekParent);
6656cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
6657d522f475Smrg#if OPT_TOOLBAR
6658cd3331d0Smrg		/* we need both of these during initialization */
6659cd3331d0Smrg		XtMapWidget(tekParent);
6660cd3331d0Smrg		XtMapWidget(tekWidget);
6661d522f475Smrg#endif
6662cd3331d0Smrg		XtOverrideTranslations(tekParent,
6663cd3331d0Smrg				       XtParseTranslationTable
6664cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6665cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
6666cd3331d0Smrg				       XtWindow(tekParent),
6667cd3331d0Smrg				       &wm_delete_window, 1);
6668d4fba8b9Smrg		TEK4014_SHOWN(xw) = True;
6669cd3331d0Smrg	    }
6670d522f475Smrg	}
6671d522f475Smrg    } else {
6672d4fba8b9Smrg	if (TEK4014_SHOWN(xw) && tekWidget) {
6673d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
6674d522f475Smrg			    TShellWindow,
6675d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6676d4fba8b9Smrg	    TEK4014_SHOWN(xw) = False;
6677d522f475Smrg	}
6678d522f475Smrg    }
6679d522f475Smrg    set_tekhide_sensitivity();
6680d522f475Smrg    set_vthide_sensitivity();
6681d522f475Smrg    update_vtshow();
6682d522f475Smrg    update_tekshow();
6683d522f475Smrg    update_vttekmode();
6684d522f475Smrg    return;
6685d522f475Smrg}
6686d522f475Smrg
6687d522f475Smrgvoid
6688d522f475Smrgend_tek_mode(void)
6689d522f475Smrg{
6690cd3331d0Smrg    XtermWidget xw = term;
6691cd3331d0Smrg
6692cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
6693cd3331d0Smrg	FlushLog(xw);
6694dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
6695dfb07bc7Smrg	xtermSetWinSize(xw);
6696d522f475Smrg	longjmp(Tekend, 1);
6697d522f475Smrg    }
6698d522f475Smrg    return;
6699d522f475Smrg}
6700d522f475Smrg
6701d522f475Smrgvoid
6702d522f475Smrgend_vt_mode(void)
6703d522f475Smrg{
6704cd3331d0Smrg    XtermWidget xw = term;
6705cd3331d0Smrg
6706cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
6707cd3331d0Smrg	FlushLog(xw);
6708d4fba8b9Smrg	set_tek_visibility(True);
6709cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
6710dfb07bc7Smrg	TekSetWinSize(tekWidget);
6711d522f475Smrg	longjmp(VTend, 1);
6712d522f475Smrg    }
6713d522f475Smrg    return;
6714d522f475Smrg}
6715d522f475Smrg
6716d522f475Smrgvoid
6717d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
6718d522f475Smrg{
6719d522f475Smrg    if (tovt) {
6720d522f475Smrg	if (tekRefreshList)
6721d522f475Smrg	    TekRefresh(tekWidget);
6722d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
6723d522f475Smrg    } else {
6724d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
6725d522f475Smrg    }
6726d522f475Smrg}
6727d522f475Smrg
6728d522f475Smrgvoid
6729d522f475Smrghide_vt_window(void)
6730d522f475Smrg{
6731d522f475Smrg    set_vt_visibility(False);
6732d522f475Smrg    if (!TEK4014_ACTIVE(term))
6733d522f475Smrg	switch_modes(False);	/* switch to tek mode */
6734d522f475Smrg}
6735d522f475Smrg
6736d522f475Smrgvoid
6737d522f475Smrghide_tek_window(void)
6738d522f475Smrg{
6739d522f475Smrg    set_tek_visibility(False);
6740d522f475Smrg    tekRefreshList = (TekLink *) 0;
6741d522f475Smrg    if (TEK4014_ACTIVE(term))
6742d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
6743d522f475Smrg}
6744d522f475Smrg#endif /* OPT_TEK4014 */
6745d522f475Smrg
6746d522f475Smrgstatic const char *
6747d522f475Smrgskip_punct(const char *s)
6748d522f475Smrg{
6749d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
6750d522f475Smrg	++s;
6751d522f475Smrg    }
6752d522f475Smrg    return s;
6753d522f475Smrg}
6754d522f475Smrg
6755d522f475Smrgstatic int
6756d522f475Smrgcmp_options(const void *a, const void *b)
6757d522f475Smrg{
6758d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
6759d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
6760d522f475Smrg    return strcmp(s1, s2);
6761d522f475Smrg}
6762d522f475Smrg
6763d522f475Smrgstatic int
6764d522f475Smrgcmp_resources(const void *a, const void *b)
6765d522f475Smrg{
6766d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
6767d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
6768d522f475Smrg}
6769d522f475Smrg
6770d522f475SmrgXrmOptionDescRec *
6771d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
6772d522f475Smrg{
6773d522f475Smrg    static XrmOptionDescRec *res_array = 0;
6774d522f475Smrg
6775d522f475Smrg#ifdef NO_LEAKS
6776cd3331d0Smrg    if (descs == 0) {
6777d4fba8b9Smrg	FreeAndNull(res_array);
6778d522f475Smrg    } else
6779d522f475Smrg#endif
6780d522f475Smrg    if (res_array == 0) {
6781d522f475Smrg	Cardinal j;
6782d522f475Smrg
6783d522f475Smrg	/* make a sorted index to 'resources' */
6784d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
6785cd3331d0Smrg	if (res_array != 0) {
6786cd3331d0Smrg	    for (j = 0; j < res_count; j++)
6787cd3331d0Smrg		res_array[j] = descs[j];
6788cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
6789cd3331d0Smrg	}
6790d522f475Smrg    }
6791d522f475Smrg    return res_array;
6792d522f475Smrg}
6793d522f475Smrg
6794d522f475Smrg/*
6795d522f475Smrg * The first time this is called, construct sorted index to the main program's
6796d522f475Smrg * list of options, taking into account the on/off options which will be
6797d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
6798d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
6799d522f475Smrg */
6800d522f475SmrgOptionHelp *
6801d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
6802d522f475Smrg{
6803d522f475Smrg    static OptionHelp *opt_array = 0;
6804d522f475Smrg
6805d522f475Smrg#ifdef NO_LEAKS
6806d522f475Smrg    if (descs == 0 && opt_array != 0) {
6807d522f475Smrg	sortedOptDescs(descs, numDescs);
6808d4fba8b9Smrg	FreeAndNull(opt_array);
6809d522f475Smrg	return 0;
6810d522f475Smrg    } else if (options == 0 || descs == 0) {
6811d522f475Smrg	return 0;
6812d522f475Smrg    }
6813d522f475Smrg#endif
6814d522f475Smrg
6815d522f475Smrg    if (opt_array == 0) {
6816cd3331d0Smrg	size_t opt_count, j;
6817d522f475Smrg#if OPT_TRACE
6818d522f475Smrg	Cardinal k;
6819d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
6820d522f475Smrg	int code;
6821cd3331d0Smrg	const char *mesg;
6822d522f475Smrg#else
6823d522f475Smrg	(void) descs;
6824d522f475Smrg	(void) numDescs;
6825d522f475Smrg#endif
6826d522f475Smrg
6827d522f475Smrg	/* count 'options' and make a sorted index to it */
6828d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
6829d522f475Smrg	    ;
6830d522f475Smrg	}
6831d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
6832d522f475Smrg	for (j = 0; j < opt_count; j++)
6833d522f475Smrg	    opt_array[j] = options[j];
6834d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
6835d522f475Smrg
6836d522f475Smrg	/* supply the "turn on/off" strings if needed */
6837d522f475Smrg#if OPT_TRACE
6838d522f475Smrg	for (j = 0; j < opt_count; j++) {
6839712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
6840c219fbebSmrg		char temp[80];
6841cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
6842d522f475Smrg		for (k = 0; k < numDescs; ++k) {
6843cd3331d0Smrg		    const char *value = res_array[k].value;
6844d522f475Smrg		    if (res_array[k].option[0] == '-') {
6845d522f475Smrg			code = -1;
6846d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
6847d522f475Smrg			code = 1;
6848d522f475Smrg		    } else {
6849d522f475Smrg			code = 0;
6850d522f475Smrg		    }
68513367019cSmrg		    sprintf(temp, "%.*s",
68523367019cSmrg			    (int) sizeof(temp) - 2,
68533367019cSmrg			    opt_array[j].desc);
6854c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
6855d522f475Smrg			code = -code;
6856d522f475Smrg		    if (code != 0
6857d522f475Smrg			&& res_array[k].value != 0
6858d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
6859d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
6860d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
6861d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
6862d522f475Smrg			    mesg = "turn on/off";
6863d522f475Smrg			} else {
6864d522f475Smrg			    mesg = "turn off/on";
6865d522f475Smrg			}
6866d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
6867712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
6868d4fba8b9Smrg				char *s = malloc(strlen(mesg)
6869d4fba8b9Smrg						 + strlen(opt_array[j].desc)
6870d4fba8b9Smrg						 + 2);
6871d522f475Smrg				if (s != 0) {
6872d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
6873d522f475Smrg				    opt_array[j].desc = s;
6874d522f475Smrg				}
6875d522f475Smrg			    } else {
6876d522f475Smrg				TRACE(("OOPS "));
6877d522f475Smrg			    }
6878d522f475Smrg			}
6879d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
6880d522f475Smrg			       mesg,
6881d522f475Smrg			       res_array[k].option,
6882d522f475Smrg			       res_array[k].value,
6883d522f475Smrg			       opt_array[j].opt,
6884d522f475Smrg			       opt_array[j].desc));
6885d522f475Smrg			break;
6886d522f475Smrg		    }
6887d522f475Smrg		}
6888d522f475Smrg	    }
6889d522f475Smrg	}
6890d522f475Smrg#endif
6891d522f475Smrg    }
6892d522f475Smrg    return opt_array;
6893d522f475Smrg}
6894d522f475Smrg
6895d522f475Smrg/*
6896d522f475Smrg * Report the character-type locale that xterm was started in.
6897d522f475Smrg */
68983367019cSmrgString
6899d522f475SmrgxtermEnvLocale(void)
6900d522f475Smrg{
69013367019cSmrg    static String result;
6902d522f475Smrg
6903d522f475Smrg    if (result == 0) {
6904d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
6905cd3331d0Smrg	    result = x_strdup("C");
6906cd3331d0Smrg	} else {
6907cd3331d0Smrg	    result = x_strdup(result);
6908d522f475Smrg	}
6909d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
6910d522f475Smrg    }
6911d522f475Smrg    return result;
6912d522f475Smrg}
6913d522f475Smrg
6914d522f475Smrgchar *
6915d522f475SmrgxtermEnvEncoding(void)
6916d522f475Smrg{
6917d522f475Smrg    static char *result;
6918d522f475Smrg
6919d522f475Smrg    if (result == 0) {
6920d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6921d522f475Smrg	result = nl_langinfo(CODESET);
6922d522f475Smrg#else
6923d4fba8b9Smrg	const char *locale = xtermEnvLocale();
6924d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
6925d4fba8b9Smrg	    result = x_strdup("ASCII");
6926d522f475Smrg	} else {
6927d4fba8b9Smrg	    result = x_strdup("ISO-8859-1");
6928d522f475Smrg	}
6929d522f475Smrg#endif
6930d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
6931d522f475Smrg    }
6932d522f475Smrg    return result;
6933d522f475Smrg}
6934d522f475Smrg
6935d522f475Smrg#if OPT_WIDE_CHARS
6936d522f475Smrg/*
6937d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
6938d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
6939d522f475Smrg * various library calls.
6940d522f475Smrg */
6941d522f475SmrgBool
6942d522f475SmrgxtermEnvUTF8(void)
6943d522f475Smrg{
6944d522f475Smrg    static Bool init = False;
6945d522f475Smrg    static Bool result = False;
6946d522f475Smrg
6947d522f475Smrg    if (!init) {
6948d522f475Smrg	init = True;
6949d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6950d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
6951d522f475Smrg#else
6952fa3f02f3Smrg	{
6953fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
6954fa3f02f3Smrg	    int n;
6955fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
6956fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
6957fa3f02f3Smrg	    }
6958fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
6959fa3f02f3Smrg		result = True;
6960fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
6961fa3f02f3Smrg		result = True;
6962fa3f02f3Smrg	    free(locale);
6963fa3f02f3Smrg	}
6964d522f475Smrg#endif
6965d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
6966d522f475Smrg    }
6967d522f475Smrg    return result;
6968d522f475Smrg}
6969d522f475Smrg#endif /* OPT_WIDE_CHARS */
6970d522f475Smrg
6971b7c89284Ssnj/*
6972b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
6973b7c89284Ssnj */
6974b7c89284SsnjXtermWidget
6975b7c89284SsnjgetXtermWidget(Widget w)
6976b7c89284Ssnj{
6977b7c89284Ssnj    XtermWidget xw;
6978b7c89284Ssnj
6979b7c89284Ssnj    if (w == 0) {
6980b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
6981b7c89284Ssnj	if (!IsXtermWidget(xw)) {
6982b7c89284Ssnj	    xw = 0;
6983b7c89284Ssnj	}
6984b7c89284Ssnj    } else if (IsXtermWidget(w)) {
6985b7c89284Ssnj	xw = (XtermWidget) w;
6986b7c89284Ssnj    } else {
6987b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
6988b7c89284Ssnj    }
6989b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
6990b7c89284Ssnj    return xw;
6991b7c89284Ssnj}
69923367019cSmrg
69933367019cSmrg#if OPT_SESSION_MGT
69943367019cSmrgstatic void
69953367019cSmrgdie_callback(Widget w GCC_UNUSED,
69963367019cSmrg	     XtPointer client_data GCC_UNUSED,
69973367019cSmrg	     XtPointer call_data GCC_UNUSED)
69983367019cSmrg{
69993367019cSmrg    NormalExit();
70003367019cSmrg}
70013367019cSmrg
70023367019cSmrgstatic void
70033367019cSmrgsave_callback(Widget w GCC_UNUSED,
70043367019cSmrg	      XtPointer client_data GCC_UNUSED,
70053367019cSmrg	      XtPointer call_data)
70063367019cSmrg{
70073367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
70083367019cSmrg    /* we have nothing to save */
70093367019cSmrg    token->save_success = True;
70103367019cSmrg}
70113367019cSmrg
70123367019cSmrgstatic void
70133367019cSmrgicewatch(IceConn iceConn,
70143367019cSmrg	 IcePointer clientData GCC_UNUSED,
70153367019cSmrg	 Bool opening,
70163367019cSmrg	 IcePointer * watchData GCC_UNUSED)
70173367019cSmrg{
70183367019cSmrg    if (opening) {
70193367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
70203367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
70213367019cSmrg    } else {
70223367019cSmrg	ice_fd = -1;
70233367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
70243367019cSmrg    }
70253367019cSmrg}
70263367019cSmrg
70273367019cSmrgvoid
70283367019cSmrgxtermOpenSession(void)
70293367019cSmrg{
70303367019cSmrg    if (resource.sessionMgt) {
70313367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
70323367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
70333367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
70343367019cSmrg    }
70353367019cSmrg}
70363367019cSmrg
70373367019cSmrgvoid
70383367019cSmrgxtermCloseSession(void)
70393367019cSmrg{
70403367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
70413367019cSmrg}
70423367019cSmrg#endif /* OPT_SESSION_MGT */
70433367019cSmrg
70443367019cSmrgWidget
70453367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
70463367019cSmrg		     String my_class,
70473367019cSmrg		     XrmOptionDescRec * options,
70483367019cSmrg		     Cardinal num_options,
70493367019cSmrg		     int *argc_in_out,
7050d4fba8b9Smrg		     char **argv_in_out,
7051fa3f02f3Smrg		     String *fallback_resources,
70523367019cSmrg		     WidgetClass widget_class,
70533367019cSmrg		     ArgList args,
70543367019cSmrg		     Cardinal num_args)
70553367019cSmrg{
70563367019cSmrg    Widget result;
70573367019cSmrg
70583367019cSmrg    XtSetErrorHandler(xt_error);
70593367019cSmrg#if OPT_SESSION_MGT
70603367019cSmrg    result = XtOpenApplication(app_context_return,
70613367019cSmrg			       my_class,
70623367019cSmrg			       options,
70633367019cSmrg			       num_options,
70643367019cSmrg			       argc_in_out,
70653367019cSmrg			       argv_in_out,
70663367019cSmrg			       fallback_resources,
70673367019cSmrg			       widget_class,
70683367019cSmrg			       args,
70693367019cSmrg			       num_args);
70703367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
70713367019cSmrg#else
70729a64e1c5Smrg    (void) widget_class;
70739a64e1c5Smrg    (void) args;
70749a64e1c5Smrg    (void) num_args;
70753367019cSmrg    result = XtAppInitialize(app_context_return,
70763367019cSmrg			     my_class,
70773367019cSmrg			     options,
70783367019cSmrg			     num_options,
70793367019cSmrg			     argc_in_out,
70803367019cSmrg			     argv_in_out,
70813367019cSmrg			     fallback_resources,
70823367019cSmrg			     NULL, 0);
70833367019cSmrg#endif /* OPT_SESSION_MGT */
7084e8264990Smrg    XtSetErrorHandler(NULL);
70853367019cSmrg
70863367019cSmrg    return result;
70873367019cSmrg}
70883367019cSmrg
7089d4fba8b9Smrg/*
7090d4fba8b9Smrg * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
7091d4fba8b9Smrg * our own error-handler.
7092d4fba8b9Smrg */
7093d4fba8b9Smrg/* ARGSUSED */
7094d4fba8b9Smrgint
7095d4fba8b9Smrgignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
7096d4fba8b9Smrg{
7097d4fba8b9Smrg    return 1;
7098d4fba8b9Smrg}
7099d4fba8b9Smrg
71003367019cSmrgstatic int x11_errors;
71013367019cSmrg
71023367019cSmrgstatic int
71039a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
71043367019cSmrg{
71053367019cSmrg    (void) display;
71063367019cSmrg    (void) error_event;
71073367019cSmrg    ++x11_errors;
71083367019cSmrg    return 0;
71093367019cSmrg}
71103367019cSmrg
71113367019cSmrgBoolean
7112fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
71133367019cSmrg{
71143367019cSmrg    Boolean result = False;
71153367019cSmrg    Status code;
71163367019cSmrg
71173367019cSmrg    memset(attrs, 0, sizeof(*attrs));
71183367019cSmrg    if (win != None) {
71193367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
71203367019cSmrg	x11_errors = 0;
71213367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
71223367019cSmrg	XSetErrorHandler(save);
71233367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
71243367019cSmrg	if (result) {
71253367019cSmrg	    TRACE_WIN_ATTRS(attrs);
71263367019cSmrg	} else {
71273367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
71283367019cSmrg	}
71293367019cSmrg    }
71303367019cSmrg    return result;
71313367019cSmrg}
71323367019cSmrg
71333367019cSmrgBoolean
7134fa3f02f3SmrgxtermGetWinProp(Display *display,
71353367019cSmrg		Window win,
71363367019cSmrg		Atom property,
71373367019cSmrg		long long_offset,
71383367019cSmrg		long long_length,
71393367019cSmrg		Atom req_type,
71409a64e1c5Smrg		Atom *actual_type_return,
71413367019cSmrg		int *actual_format_return,
71423367019cSmrg		unsigned long *nitems_return,
71433367019cSmrg		unsigned long *bytes_after_return,
71443367019cSmrg		unsigned char **prop_return)
71453367019cSmrg{
7146d4fba8b9Smrg    Boolean result = False;
71473367019cSmrg
71483367019cSmrg    if (win != None) {
71493367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
71503367019cSmrg	x11_errors = 0;
71513367019cSmrg	if (XGetWindowProperty(display,
71523367019cSmrg			       win,
71533367019cSmrg			       property,
71543367019cSmrg			       long_offset,
71553367019cSmrg			       long_length,
71563367019cSmrg			       False,
71573367019cSmrg			       req_type,
71583367019cSmrg			       actual_type_return,
71593367019cSmrg			       actual_format_return,
71603367019cSmrg			       nitems_return,
71613367019cSmrg			       bytes_after_return,
71623367019cSmrg			       prop_return) == Success
71633367019cSmrg	    && x11_errors == 0) {
71643367019cSmrg	    result = True;
71653367019cSmrg	}
71663367019cSmrg	XSetErrorHandler(save);
71673367019cSmrg    }
71683367019cSmrg    return result;
71693367019cSmrg}
71703367019cSmrg
71713367019cSmrgvoid
71723367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
71733367019cSmrg{
71743367019cSmrg    Display *dpy = XtDisplay(toplevel);
71753367019cSmrg    XWindowAttributes attrs;
71763367019cSmrg
71773367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
71783367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
71793367019cSmrg	XtermWidget xw = term;
71803367019cSmrg	TScreen *screen = TScreenOf(xw);
71813367019cSmrg
71823367019cSmrg	XtRealizeWidget(toplevel);
71833367019cSmrg
71843367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
71853367019cSmrg	       XtWindow(toplevel),
71863367019cSmrg	       winToEmbedInto));
71873367019cSmrg	XReparentWindow(dpy,
71883367019cSmrg			XtWindow(toplevel),
71893367019cSmrg			winToEmbedInto, 0, 0);
71903367019cSmrg
71913367019cSmrg	screen->embed_high = (Dimension) attrs.height;
71923367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
71933367019cSmrg    }
71943367019cSmrg}
719594644356Smrg
719694644356Smrgvoid
719794644356Smrgfree_string(String value)
719894644356Smrg{
719994644356Smrg    free((void *) value);
720094644356Smrg}
7201dfb07bc7Smrg
7202dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
7203d4fba8b9Smrgint
7204dfb07bc7Smrgupdate_winsize(int fd, int rows, int cols, int height, int width)
7205dfb07bc7Smrg{
7206d4fba8b9Smrg    int code = -1;
7207dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
7208d4fba8b9Smrg    static int last_rows = -1;
7209d4fba8b9Smrg    static int last_cols = -1;
7210dfb07bc7Smrg
7211d4fba8b9Smrg    if (rows != last_rows || cols != last_cols) {
7212d4fba8b9Smrg	TTYSIZE_STRUCT ts;
7213d4fba8b9Smrg
7214d4fba8b9Smrg	last_rows = rows;
7215d4fba8b9Smrg	last_cols = cols;
7216d4fba8b9Smrg	setup_winsize(ts, rows, cols, height, width);
7217d4fba8b9Smrg	TRACE_RC(code, SET_TTYSIZE(fd, ts));
7218d4fba8b9Smrg	trace_winsize(ts, "from SET_TTYSIZE");
7219d4fba8b9Smrg    }
7220dfb07bc7Smrg#endif
7221dfb07bc7Smrg
7222dfb07bc7Smrg    (void) rows;
7223dfb07bc7Smrg    (void) cols;
7224dfb07bc7Smrg    (void) height;
7225dfb07bc7Smrg    (void) width;
7226d4fba8b9Smrg
7227d4fba8b9Smrg    return code;
7228dfb07bc7Smrg}
7229dfb07bc7Smrg
7230dfb07bc7Smrg/*
7231dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
7232dfb07bc7Smrg * manipulation 18 and 19.
7233dfb07bc7Smrg */
7234dfb07bc7Smrgvoid
7235dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
7236dfb07bc7Smrg{
7237dfb07bc7Smrg#if OPT_TEK4014
7238dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
7239dfb07bc7Smrg#endif
7240dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
7241dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
7242dfb07bc7Smrg
7243dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
7244dfb07bc7Smrg	    update_winsize(screen->respond,
7245dfb07bc7Smrg			   MaxRows(screen),
7246dfb07bc7Smrg			   MaxCols(screen),
7247dfb07bc7Smrg			   Height(screen),
7248dfb07bc7Smrg			   Width(screen));
7249dfb07bc7Smrg	}
7250dfb07bc7Smrg}
7251d4fba8b9Smrg
7252d4fba8b9Smrg#if OPT_XTERM_SGR
7253d4fba8b9Smrg
7254d4fba8b9Smrg#if OPT_TRACE
7255d4fba8b9Smrgstatic char *
7256d4fba8b9SmrgtraceIFlags(IFlags flags)
7257d4fba8b9Smrg{
7258d4fba8b9Smrg    static char result[1000];
7259d4fba8b9Smrg    result[0] = '\0';
7260d4fba8b9Smrg#define DATA(name) if (flags & name) { strcat(result, " " #name); }
7261d4fba8b9Smrg    DATA(INVERSE);
7262d4fba8b9Smrg    DATA(UNDERLINE);
7263d4fba8b9Smrg    DATA(BOLD);
7264d4fba8b9Smrg    DATA(BLINK);
7265d4fba8b9Smrg    DATA(INVISIBLE);
7266d4fba8b9Smrg    DATA(BG_COLOR);
7267d4fba8b9Smrg    DATA(FG_COLOR);
7268d4fba8b9Smrg
7269d4fba8b9Smrg#if OPT_WIDE_ATTRS
7270d4fba8b9Smrg    DATA(ATR_FAINT);
7271d4fba8b9Smrg    DATA(ATR_ITALIC);
7272d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7273d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7274d4fba8b9Smrg    DATA(ATR_DIRECT_FG);
7275d4fba8b9Smrg    DATA(ATR_DIRECT_BG);
7276d4fba8b9Smrg#endif
7277d4fba8b9Smrg#undef DATA
7278d4fba8b9Smrg    return result;
7279d4fba8b9Smrg}
7280d4fba8b9Smrg
7281d4fba8b9Smrgstatic char *
7282d4fba8b9SmrgtraceIStack(unsigned flags)
7283d4fba8b9Smrg{
7284d4fba8b9Smrg    static char result[1000];
7285d4fba8b9Smrg    result[0] = '\0';
7286d4fba8b9Smrg#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7287d4fba8b9Smrg    DATA(INVERSE);
7288d4fba8b9Smrg    DATA(UNDERLINE);
7289d4fba8b9Smrg    DATA(BOLD);
7290d4fba8b9Smrg    DATA(BLINK);
7291d4fba8b9Smrg    DATA(INVISIBLE);
7292d4fba8b9Smrg#if OPT_ISO_COLORS
7293d4fba8b9Smrg    DATA(BG_COLOR);
7294d4fba8b9Smrg    DATA(FG_COLOR);
7295d4fba8b9Smrg#endif
7296d4fba8b9Smrg
7297d4fba8b9Smrg#if OPT_WIDE_ATTRS
7298d4fba8b9Smrg    DATA(ATR_FAINT);
7299d4fba8b9Smrg    DATA(ATR_ITALIC);
7300d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7301d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7302d4fba8b9Smrg    /* direct-colors are a special case of ISO-colors (see above) */
7303d4fba8b9Smrg#endif
7304d4fba8b9Smrg#undef DATA
7305d4fba8b9Smrg    return result;
7306d4fba8b9Smrg}
7307d4fba8b9Smrg#endif
7308d4fba8b9Smrg
7309d4fba8b9Smrgvoid
7310d4fba8b9SmrgxtermPushSGR(XtermWidget xw, int value)
7311d4fba8b9Smrg{
7312d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7313d4fba8b9Smrg
7314d4fba8b9Smrg    TRACE(("xtermPushSGR %d mask %#x %s\n",
7315d4fba8b9Smrg	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
7316d4fba8b9Smrg
7317d4fba8b9Smrg    if (s->used < MAX_SAVED_SGR) {
7318d4fba8b9Smrg	s->stack[s->used].mask = (IFlags) value;
7319d4fba8b9Smrg#define PUSH_FLAG(name) \
7320d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7321d4fba8b9Smrg	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
7322d4fba8b9Smrg#define PUSH_DATA(name) \
7323d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7324d4fba8b9Smrg	    TRACE(("...may pop %s %d\n", #name, xw->name))
7325d4fba8b9Smrg	PUSH_FLAG(flags);
7326d4fba8b9Smrg#if OPT_ISO_COLORS
7327d4fba8b9Smrg	PUSH_DATA(sgr_foreground);
7328d4fba8b9Smrg	PUSH_DATA(sgr_background);
7329d4fba8b9Smrg	PUSH_DATA(sgr_38_xcolors);
7330d4fba8b9Smrg#endif
7331d4fba8b9Smrg    }
7332d4fba8b9Smrg    s->used++;
7333d4fba8b9Smrg}
7334d4fba8b9Smrg
7335d4fba8b9Smrg#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
7336d4fba8b9Smrg
7337d4fba8b9Smrgvoid
7338d4fba8b9SmrgxtermReportSGR(XtermWidget xw, XTermRect *value)
7339d4fba8b9Smrg{
7340d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
7341d4fba8b9Smrg    char reply[BUFSIZ];
7342d4fba8b9Smrg    CellData working;
7343d4fba8b9Smrg    int row, col;
7344d4fba8b9Smrg    Boolean first = True;
7345d4fba8b9Smrg
7346d4fba8b9Smrg    TRACE(("xtermReportSGR %d,%d - %d,%d\n",
7347d4fba8b9Smrg	   value->top, value->left,
7348d4fba8b9Smrg	   value->bottom, value->right));
7349d4fba8b9Smrg
7350d4fba8b9Smrg    memset(&working, 0, sizeof(working));
7351d4fba8b9Smrg    for (row = value->top - 1; row < value->bottom; ++row) {
7352d4fba8b9Smrg	LineData *ld = getLineData(screen, row);
7353d4fba8b9Smrg	if (ld == 0)
7354d4fba8b9Smrg	    continue;
7355d4fba8b9Smrg	for (col = value->left - 1; col < value->right; ++col) {
7356d4fba8b9Smrg	    if (first) {
7357d4fba8b9Smrg		first = False;
7358d4fba8b9Smrg		saveCellData(screen, &working, 0, ld, NULL, col);
7359d4fba8b9Smrg	    }
7360d4fba8b9Smrg	    working.attribs &= ld->attribs[col];
7361d4fba8b9Smrg#if OPT_ISO_COLORS
7362d4fba8b9Smrg	    if (working.attribs & FG_COLOR
7363d4fba8b9Smrg		&& GetCellColorFG(working.color)
7364d4fba8b9Smrg		!= GetCellColorFG(ld->color[col])) {
7365d4fba8b9Smrg		IAttrClr(working.attribs, FG_COLOR);
7366d4fba8b9Smrg	    }
7367d4fba8b9Smrg	    if (working.attribs & BG_COLOR
7368d4fba8b9Smrg		&& GetCellColorBG(working.color)
7369d4fba8b9Smrg		!= GetCellColorBG(ld->color[col])) {
7370d4fba8b9Smrg		IAttrClr(working.attribs, BG_COLOR);
7371d4fba8b9Smrg	    }
7372d4fba8b9Smrg#endif
7373d4fba8b9Smrg	}
7374d4fba8b9Smrg    }
7375d4fba8b9Smrg    xtermFormatSGR(xw, reply,
7376d4fba8b9Smrg		   working.attribs,
7377d4fba8b9Smrg		   GetCellColorFG(working.color),
7378d4fba8b9Smrg		   GetCellColorBG(working.color));
7379d4fba8b9Smrg    unparseputc1(xw, ANSI_CSI);
7380d4fba8b9Smrg    unparseputs(xw, reply);
7381d4fba8b9Smrg    unparseputc(xw, 'm');
7382d4fba8b9Smrg    unparse_end(xw);
7383d4fba8b9Smrg}
7384d4fba8b9Smrg
7385d4fba8b9Smrgvoid
7386d4fba8b9SmrgxtermPopSGR(XtermWidget xw)
7387d4fba8b9Smrg{
7388d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7389d4fba8b9Smrg
7390d4fba8b9Smrg    TRACE(("xtermPopSGR %d\n", s->used));
7391d4fba8b9Smrg
7392d4fba8b9Smrg    if (s->used > 0) {
7393d4fba8b9Smrg	if (s->used-- <= MAX_SAVED_SGR) {
7394d4fba8b9Smrg	    IFlags mask = s->stack[s->used].mask;
7395d4fba8b9Smrg	    Boolean changed = False;
7396d4fba8b9Smrg
7397d4fba8b9Smrg	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
7398d4fba8b9Smrg	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
7399d4fba8b9Smrg	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
7400d4fba8b9Smrg#define POP_FLAG(name) \
7401d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7402d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7403d4fba8b9Smrg		    changed = True; \
7404d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7405d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7406d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7407d4fba8b9Smrg		} \
7408d4fba8b9Smrg	    }
7409d4fba8b9Smrg#define POP_FLAG2(name,part) \
7410d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7411d4fba8b9Smrg	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
7412d4fba8b9Smrg		    changed = True; \
7413d4fba8b9Smrg		    UIntClr(xw->flags, part); \
7414d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
7415d4fba8b9Smrg		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
7416d4fba8b9Smrg		} \
7417d4fba8b9Smrg	    }
7418d4fba8b9Smrg#define POP_DATA(name,value) \
7419d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7420d4fba8b9Smrg	        Bool always = False; \
7421d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7422d4fba8b9Smrg		    always = changed = True; \
7423d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7424d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7425d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7426d4fba8b9Smrg		} \
7427d4fba8b9Smrg		if (always || (xw->value != s->stack[s->used].value)) { \
7428d4fba8b9Smrg		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
7429d4fba8b9Smrg		    xw->value = s->stack[s->used].value; \
7430d4fba8b9Smrg		    changed = True; \
7431d4fba8b9Smrg		} \
7432d4fba8b9Smrg	    }
7433d4fba8b9Smrg	    POP_FLAG(BOLD);
7434d4fba8b9Smrg	    POP_FLAG(UNDERLINE);
7435d4fba8b9Smrg	    POP_FLAG(BLINK);
7436d4fba8b9Smrg	    POP_FLAG(INVERSE);
7437d4fba8b9Smrg	    POP_FLAG(INVISIBLE);
7438d4fba8b9Smrg#if OPT_WIDE_ATTRS
7439d4fba8b9Smrg	    if (xBIT(psATR_ITALIC - 1) & mask) {
7440d4fba8b9Smrg		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
7441d4fba8b9Smrg	    }
7442d4fba8b9Smrg	    POP_FLAG(ATR_ITALIC);
7443d4fba8b9Smrg	    POP_FLAG(ATR_FAINT);
7444d4fba8b9Smrg	    POP_FLAG(ATR_STRIKEOUT);
7445d4fba8b9Smrg	    POP_FLAG(ATR_DBL_UNDER);
7446d4fba8b9Smrg#endif
7447d4fba8b9Smrg#if OPT_ISO_COLORS
7448d4fba8b9Smrg	    POP_DATA(FG_COLOR, sgr_foreground);
7449d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_background);
7450d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_38_xcolors);
7451d4fba8b9Smrg#if OPT_DIRECT_COLOR
7452d4fba8b9Smrg	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
7453d4fba8b9Smrg	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
7454d4fba8b9Smrg#endif
7455d4fba8b9Smrg	    if (changed) {
7456d4fba8b9Smrg		setExtendedColors(xw);
7457d4fba8b9Smrg	    }
7458d4fba8b9Smrg#else
7459d4fba8b9Smrg	    (void) changed;
7460d4fba8b9Smrg#endif
7461d4fba8b9Smrg	}
7462d4fba8b9Smrg#if OPT_ISO_COLORS
7463d4fba8b9Smrg	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
7464d4fba8b9Smrg	       traceIFlags(xw->flags),
7465d4fba8b9Smrg	       xw->sgr_foreground,
7466d4fba8b9Smrg	       xw->sgr_background,
7467d4fba8b9Smrg	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
7468d4fba8b9Smrg#else
7469d4fba8b9Smrg	TRACE(("xtermP -> flags%s\n",
7470d4fba8b9Smrg	       traceIFlags(xw->flags)));
7471d4fba8b9Smrg#endif
7472d4fba8b9Smrg    }
7473d4fba8b9Smrg}
7474d4fba8b9Smrg
7475d4fba8b9Smrg#if OPT_ISO_COLORS
7476d4fba8b9Smrgstatic ColorSlot *
7477d4fba8b9SmrgallocColorSlot(XtermWidget xw, int slot)
7478d4fba8b9Smrg{
7479d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7480d4fba8b9Smrg    ColorSlot *result = NULL;
7481d4fba8b9Smrg
7482d4fba8b9Smrg    if (slot >= 0 && slot < MAX_SAVED_SGR) {
7483d4fba8b9Smrg	if (s->palettes[slot] == NULL) {
7484d4fba8b9Smrg	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
7485d4fba8b9Smrg						     sizeof(ColorSlot)
7486d4fba8b9Smrg						     + (sizeof(ColorRes)
7487d4fba8b9Smrg							* MAXCOLORS));
7488d4fba8b9Smrg	}
7489d4fba8b9Smrg	result = s->palettes[slot];
7490d4fba8b9Smrg    }
7491d4fba8b9Smrg    return result;
7492d4fba8b9Smrg}
7493d4fba8b9Smrg
7494d4fba8b9Smrgstatic void
7495d4fba8b9SmrgpopOldColors(XtermWidget xw, ScrnColors * source)
7496d4fba8b9Smrg{
7497d4fba8b9Smrg    Boolean changed = False;
7498d4fba8b9Smrg    ScrnColors *target = xw->work.oldColors;
7499d4fba8b9Smrg
7500d4fba8b9Smrg    if (source->which != target->which) {
7501d4fba8b9Smrg	changed = True;
7502d4fba8b9Smrg    } else {
7503d4fba8b9Smrg	int n;
7504d4fba8b9Smrg	for (n = 0; n < NCOLORS; ++n) {
7505d4fba8b9Smrg	    if (COLOR_DEFINED(source, n)) {
7506d4fba8b9Smrg		if (COLOR_DEFINED(target, n)) {
7507d4fba8b9Smrg		    if (source->colors[n] != target->colors[n]) {
7508d4fba8b9Smrg			changed = True;
7509d4fba8b9Smrg			break;
7510d4fba8b9Smrg		    }
7511d4fba8b9Smrg		} else {
7512d4fba8b9Smrg		    changed = True;
7513d4fba8b9Smrg		    break;
7514d4fba8b9Smrg		}
7515d4fba8b9Smrg	    } else if (COLOR_DEFINED(target, n)) {
7516d4fba8b9Smrg		changed = True;
7517d4fba8b9Smrg		break;
7518d4fba8b9Smrg	    }
7519d4fba8b9Smrg	}
7520d4fba8b9Smrg    }
7521d4fba8b9Smrg    if (changed) {
7522d4fba8b9Smrg	ChangeColors(xw, source);
7523d4fba8b9Smrg	UpdateOldColors(xw, source);
7524d4fba8b9Smrg    }
7525d4fba8b9Smrg}
7526d4fba8b9Smrg#endif /* OPT_ISO_COLORS */
7527d4fba8b9Smrg
7528d4fba8b9Smrg#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
7529d4fba8b9Smrg#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
7530d4fba8b9Smrg
7531d4fba8b9Smrg/*
7532d4fba8b9Smrg * By default, a "push" increments the stack after copying to the current
7533d4fba8b9Smrg * slot.  But a specific target allows one to copy into a specific slot.
7534d4fba8b9Smrg */
7535d4fba8b9Smrgvoid
7536d4fba8b9SmrgxtermPushColors(XtermWidget xw, int value)
7537d4fba8b9Smrg{
7538d4fba8b9Smrg#if OPT_ISO_COLORS
7539d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7540d4fba8b9Smrg    int pushed = s->used;
7541d4fba8b9Smrg    int actual = (value <= 0) ? pushed : (value - 1);
7542d4fba8b9Smrg
7543d4fba8b9Smrg    TRACE(("xtermPushColors %d:%d\n", actual, pushed));
7544d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7545d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7546d4fba8b9Smrg	ColorSlot *palette;
7547d4fba8b9Smrg
7548d4fba8b9Smrg	if ((palette = allocColorSlot(xw, actual)) != NULL) {
7549d4fba8b9Smrg	    GetColors(xw, &(palette->base));
7550d4fba8b9Smrg	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
7551d4fba8b9Smrg	    if (value < 0) {
7552d4fba8b9Smrg		s->used++;
7553d4fba8b9Smrg		if (s->last < s->used)
7554d4fba8b9Smrg		    s->last = s->used;
7555d4fba8b9Smrg	    } else {
7556d4fba8b9Smrg		s->used = value;
7557d4fba8b9Smrg	    }
7558d4fba8b9Smrg	}
7559d4fba8b9Smrg    }
7560d4fba8b9Smrg#else
7561d4fba8b9Smrg    (void) xw;
7562d4fba8b9Smrg    (void) value;
7563d4fba8b9Smrg#endif
7564d4fba8b9Smrg}
7565d4fba8b9Smrg
7566d4fba8b9Smrgvoid
7567d4fba8b9SmrgxtermPopColors(XtermWidget xw, int value)
7568d4fba8b9Smrg{
7569d4fba8b9Smrg#if OPT_ISO_COLORS
7570d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7571d4fba8b9Smrg    int popped = (s->used - 1);
7572d4fba8b9Smrg    int actual = (value <= 0) ? popped : (value - 1);
7573d4fba8b9Smrg
7574d4fba8b9Smrg    TRACE(("xtermPopColors %d:%d\n", actual, popped));
7575d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7576d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7577d4fba8b9Smrg	ColorSlot *palette;
7578d4fba8b9Smrg
7579d4fba8b9Smrg	if ((palette = s->palettes[actual]) != NULL) {
7580d4fba8b9Smrg	    Boolean changed = DiffColorSlot(screen->Acolors,
7581d4fba8b9Smrg					    palette->ansi,
7582d4fba8b9Smrg					    MAXCOLORS);
7583d4fba8b9Smrg
7584d4fba8b9Smrg	    GetOldColors(xw);
7585d4fba8b9Smrg	    popOldColors(xw, &(palette->base));
7586d4fba8b9Smrg	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
7587d4fba8b9Smrg	    s->used = actual;
7588d4fba8b9Smrg	    if (changed)
7589d4fba8b9Smrg		xtermRepaint(xw);
7590d4fba8b9Smrg	}
7591d4fba8b9Smrg    }
7592d4fba8b9Smrg#else
7593d4fba8b9Smrg    (void) xw;
7594d4fba8b9Smrg    (void) value;
7595d4fba8b9Smrg#endif
7596d4fba8b9Smrg}
7597d4fba8b9Smrg
7598d4fba8b9Smrgvoid
7599d4fba8b9SmrgxtermReportColors(XtermWidget xw)
7600d4fba8b9Smrg{
7601d4fba8b9Smrg    ANSI reply;
7602d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7603d4fba8b9Smrg
7604d4fba8b9Smrg    memset(&reply, 0, sizeof(reply));
7605d4fba8b9Smrg    reply.a_type = ANSI_CSI;
7606d4fba8b9Smrg    reply.a_pintro = '?';
7607d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->used;
7608d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->last;
7609d4fba8b9Smrg    reply.a_inters = '#';
7610d4fba8b9Smrg    reply.a_final = 'Q';
7611d4fba8b9Smrg    unparseseq(xw, &reply);
7612d4fba8b9Smrg}
7613d4fba8b9Smrg#endif /* OPT_XTERM_SGR */
7614