misc.c revision c48a5815
1c48a5815Smrg/* $XTermId: misc.c,v 1.1007 2021/11/12 09:28:19 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
2596c48a5815SmrgXVisualInfo *
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) &&
2637c48a5815Smrg			   ((vi->blue_mask & vi->red_mask) == 0) &&
2638c48a5815Smrg			   (vi->class == TrueColor
2639c48a5815Smrg			    || vi->class == DirectColor));
2640d4fba8b9Smrg
2641fa3f02f3Smrg	    if (resource.reportColors) {
2642fa3f02f3Smrg		printf(MYFMT, MYARG);
2643fa3f02f3Smrg	    }
2644fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2645d4fba8b9Smrg	    TRACE(("...shifts %u/%u/%u\n",
2646d4fba8b9Smrg		   xw->rgb_shifts[0],
2647d4fba8b9Smrg		   xw->rgb_shifts[1],
2648d4fba8b9Smrg		   xw->rgb_shifts[2]));
2649c48a5815Smrg	    TRACE(("...widths %u/%u/%u\n",
2650c48a5815Smrg		   xw->rgb_widths[0],
2651c48a5815Smrg		   xw->rgb_widths[1],
2652c48a5815Smrg		   xw->rgb_widths[2]));
2653fa3f02f3Smrg	}
2654fa3f02f3Smrg    }
2655c48a5815Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
2656fa3f02f3Smrg#undef MYFMT
2657fa3f02f3Smrg#undef MYARG
2658fa3f02f3Smrg}
26593367019cSmrg
26609a64e1c5Smrg#if OPT_ISO_COLORS
2661d4fba8b9Smrgstatic Bool
2662d4fba8b9SmrgReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
26639a64e1c5Smrg{
2664d4fba8b9Smrg    Bool result = False;
2665d4fba8b9Smrg
26669a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
26679a64e1c5Smrg	XColor color;
26689a64e1c5Smrg	char buffer[80];
26699a64e1c5Smrg
26709a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
26719a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2672c48a5815Smrg	(void) QueryOneColor(xw, &color);
2673d4fba8b9Smrg	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2674d4fba8b9Smrg		opcode,
2675d4fba8b9Smrg		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
26769a64e1c5Smrg		color.red,
26779a64e1c5Smrg		color.green,
26789a64e1c5Smrg		color.blue);
26799a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
26809a64e1c5Smrg	unparseputs(xw, buffer);
26819a64e1c5Smrg	unparseputc1(xw, final);
2682d4fba8b9Smrg	result = True;
26839a64e1c5Smrg    }
2684d4fba8b9Smrg    return result;
26859a64e1c5Smrg}
26869a64e1c5Smrg
2687fa3f02f3Smrgstatic void
2688fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2689fa3f02f3Smrg{
2690fa3f02f3Smrg    if (getVisualInfo(xw)) {
2691fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2692fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2693fa3f02f3Smrg    } else {
2694fa3f02f3Smrg	*typep = 0;
2695fa3f02f3Smrg	*sizep = 0;
2696fa3f02f3Smrg    }
26973367019cSmrg}
26983367019cSmrg
26993367019cSmrg#define MAX_COLORTABLE 4096
27003367019cSmrg
27013367019cSmrg/*
27023367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
27033367019cSmrg */
27043367019cSmrgstatic Boolean
27053367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
27063367019cSmrg{
27073367019cSmrg    Colormap cmap = xw->core.colormap;
27083367019cSmrg    TScreen *screen = TScreenOf(xw);
2709fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
27103367019cSmrg
2711fa3f02f3Smrg    if (!result
27123367019cSmrg	&& length != 0
27133367019cSmrg	&& length < MAX_COLORTABLE) {
27143367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2715037a25ddSmrg
27163367019cSmrg	if (screen->cmap_data != 0) {
2717037a25ddSmrg	    unsigned i;
2718d4fba8b9Smrg	    unsigned shift;
2719d4fba8b9Smrg
2720d4fba8b9Smrg	    if (getVisualInfo(xw))
2721d4fba8b9Smrg		shift = xw->rgb_shifts[2];
2722d4fba8b9Smrg	    else
2723d4fba8b9Smrg		shift = 0;
2724037a25ddSmrg
27253367019cSmrg	    screen->cmap_size = length;
27263367019cSmrg
27273367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
2728d4fba8b9Smrg		screen->cmap_data[i].pixel = (unsigned long) i << shift;
27293367019cSmrg	    }
27303367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
27313367019cSmrg					     cmap,
27323367019cSmrg					     screen->cmap_data,
27333367019cSmrg					     (int) screen->cmap_size) != 0);
27343367019cSmrg	}
27353367019cSmrg    }
2736d522f475Smrg    return result;
2737d522f475Smrg}
2738d522f475Smrg
2739c48a5815Smrg/***====================================================================***/
2740c48a5815Smrg
2741c48a5815Smrg/*
2742c48a5815Smrg * Call this function with def->{red,green,blue} initialized, to obtain a pixel
2743c48a5815Smrg * value.
2744c48a5815Smrg */
2745c48a5815SmrgBoolean
2746c48a5815SmrgAllocOneColor(XtermWidget xw, XColor *def)
2747c48a5815Smrg{
2748c48a5815Smrg    TScreen *screen = TScreenOf(xw);
2749c48a5815Smrg    XVisualInfo *visInfo;
2750c48a5815Smrg    Boolean result = True;
2751c48a5815Smrg
2752c48a5815Smrg#define MaskIt(name,nn) \
2753c48a5815Smrg	((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
2754c48a5815Smrg	             << xw->rgb_shifts[nn]) \
2755c48a5815Smrg	 & xw->visInfo->name ##_mask)
2756c48a5815Smrg
2757c48a5815Smrg    if ((visInfo = getVisualInfo(xw)) != NULL && xw->has_rgb) {
2758c48a5815Smrg	def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
2759c48a5815Smrg    } else {
2760c48a5815Smrg	Display *dpy = screen->display;
2761c48a5815Smrg	if (!XAllocColor(dpy, xw->core.colormap, def)) {
2762c48a5815Smrg	    /*
2763c48a5815Smrg	     * Decide between foreground and background by a grayscale
2764c48a5815Smrg	     * approximation.
2765c48a5815Smrg	     */
2766c48a5815Smrg	    int bright = def->red * 3 + def->green * 10 + def->blue;
2767c48a5815Smrg	    int levels = 14 * 0x8000;
2768c48a5815Smrg	    def->pixel = ((bright >= levels)
2769c48a5815Smrg			  ? xw->dft_background
2770c48a5815Smrg			  : xw->dft_foreground);
2771c48a5815Smrg	    result = False;
2772c48a5815Smrg	}
2773c48a5815Smrg    }
2774c48a5815Smrg    return result;
2775c48a5815Smrg}
2776c48a5815Smrg
2777c48a5815Smrg/***====================================================================***/
2778c48a5815Smrg
2779c48a5815Smrg/*
2780c48a5815Smrg * Call this function with def->pixel set to the color that we want to convert
2781c48a5815Smrg * to separate red/green/blue.
2782c48a5815Smrg */
2783c48a5815SmrgBoolean
2784c48a5815SmrgQueryOneColor(XtermWidget xw, XColor *def)
2785c48a5815Smrg{
2786c48a5815Smrg    XVisualInfo *visInfo;
2787c48a5815Smrg    Boolean result = True;
2788c48a5815Smrg
2789c48a5815Smrg#define UnMaskIt(name,nn) \
2790c48a5815Smrg	((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
2791c48a5815Smrg#define UnMaskIt2(name,nn) \
2792c48a5815Smrg	(unsigned short)((((UnMaskIt(name,nn) << 8) \
2793c48a5815Smrg			   |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
2794c48a5815Smrg
2795c48a5815Smrg    if ((visInfo = getVisualInfo(xw)) != NULL && xw->has_rgb) {
2796c48a5815Smrg	/* *INDENT-EQLS* */
2797c48a5815Smrg	def->red   = UnMaskIt2(red, 0);
2798c48a5815Smrg	def->green = UnMaskIt2(green, 1);
2799c48a5815Smrg	def->blue  = UnMaskIt2(blue, 2);
2800c48a5815Smrg    } else if (!XQueryColor(TScreenOf(xw)->display, xw->core.colormap, def)) {
2801c48a5815Smrg	result     = False;
2802c48a5815Smrg    }
2803c48a5815Smrg    return result;
2804c48a5815Smrg}
2805c48a5815Smrg
2806c48a5815Smrg/***====================================================================***/
2807c48a5815Smrg
2808d522f475Smrg/*
2809d522f475Smrg * Find closest color for "def" in "cmap".
2810d522f475Smrg * Set "def" to the resulting color.
2811d522f475Smrg *
2812d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2813d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2814d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2815d522f475Smrg *
2816d522f475Smrg * Return False if not able to find or allocate a color.
2817d522f475Smrg */
2818d522f475Smrgstatic Boolean
2819c48a5815SmrgallocateClosestRGB(XtermWidget xw, XColor *def)
2820d522f475Smrg{
28213367019cSmrg    TScreen *screen = TScreenOf(xw);
2822d522f475Smrg    Boolean result = False;
28233367019cSmrg    unsigned cmap_type;
2824d522f475Smrg    unsigned cmap_size;
2825d522f475Smrg
2826fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2827d522f475Smrg
28283367019cSmrg    if ((cmap_type & 1) != 0) {
28293367019cSmrg
28303367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2831037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2832d522f475Smrg
2833d522f475Smrg	    if (tried != 0) {
2834037a25ddSmrg		unsigned attempts;
2835d522f475Smrg
2836d522f475Smrg		/*
2837d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2838d522f475Smrg		 * approximation to the requested color.
2839d522f475Smrg		 */
2840d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2841d522f475Smrg		    Boolean first = True;
2842037a25ddSmrg		    double bestRGB = 0.0;
2843037a25ddSmrg		    unsigned bestInx = 0;
2844037a25ddSmrg		    unsigned i;
2845d522f475Smrg
2846d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2847d522f475Smrg			if (!tried[bestInx]) {
2848037a25ddSmrg			    double diff, thisRGB = 0.0;
2849037a25ddSmrg
2850d522f475Smrg			    /*
2851d522f475Smrg			     * Look for the best match based on luminance.
2852d522f475Smrg			     * Measure this by the least-squares difference of
2853d522f475Smrg			     * the weighted R/G/B components from the color map
2854d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2855d522f475Smrg			     * component of the YIQ color space model for
2856d522f475Smrg			     * weights that correspond to the luminance.
2857d522f475Smrg			     */
2858d522f475Smrg#define AddColorWeight(weight, color) \
28593367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2860037a25ddSmrg			    thisRGB += diff * diff
2861d522f475Smrg
2862d522f475Smrg			    AddColorWeight(0.30, red);
2863d522f475Smrg			    AddColorWeight(0.61, green);
2864d522f475Smrg			    AddColorWeight(0.11, blue);
2865d522f475Smrg
2866d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2867d522f475Smrg				first = False;
2868d522f475Smrg				bestInx = i;
2869d522f475Smrg				bestRGB = thisRGB;
2870d522f475Smrg			    }
2871d522f475Smrg			}
2872d522f475Smrg		    }
2873c48a5815Smrg		    if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
28743367019cSmrg			*def = screen->cmap_data[bestInx];
28753367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
28763367019cSmrg			       def->green, def->blue));
2877d522f475Smrg			result = True;
2878d522f475Smrg			break;
2879d522f475Smrg		    }
2880d522f475Smrg		    /*
2881d522f475Smrg		     * It failed - either the color map entry was readonly, or
2882d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2883d522f475Smrg		     * so we will ignore it
2884d522f475Smrg		     */
2885d522f475Smrg		    tried[bestInx] = True;
2886d522f475Smrg		}
2887d522f475Smrg		free(tried);
2888d522f475Smrg	    }
2889d522f475Smrg	}
2890d522f475Smrg    }
2891d522f475Smrg    return result;
2892d522f475Smrg}
2893d522f475Smrg
28943367019cSmrg#ifndef ULONG_MAX
28953367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
28963367019cSmrg#endif
28973367019cSmrg
2898d522f475Smrg/*
2899d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2900d522f475Smrg * to 256.
2901d522f475Smrg *
2902d522f475Smrg * Returns
2903d522f475Smrg *	-1 on error
2904d522f475Smrg *	0 on no change
2905d522f475Smrg *	1 if a new color was allocated.
2906d522f475Smrg */
2907d522f475Smrgstatic int
2908d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2909d522f475Smrg		  ColorRes * res,
2910cd3331d0Smrg		  const char *spec)
2911d522f475Smrg{
2912d522f475Smrg    int result;
2913d522f475Smrg    XColor def;
2914d522f475Smrg
29153367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2916c48a5815Smrg	if (res->mode == True &&
2917c48a5815Smrg	    EQL_COLOR_RES(res, def.pixel)) {
2918d522f475Smrg	    result = 0;
2919d522f475Smrg	} else {
2920d522f475Smrg	    result = 1;
2921d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
29223367019cSmrg	    res->red = def.red;
29233367019cSmrg	    res->green = def.green;
29243367019cSmrg	    res->blue = def.blue;
29253367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
29263367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
29273367019cSmrg		   def.red,
29283367019cSmrg		   def.green,
29293367019cSmrg		   def.blue,
29303367019cSmrg		   def.pixel));
2931d522f475Smrg	    if (!res->mode)
2932d522f475Smrg		result = 0;
2933d522f475Smrg	    res->mode = True;
2934d522f475Smrg	}
2935d522f475Smrg    } else {
2936d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2937d522f475Smrg	result = -1;
2938d522f475Smrg    }
2939d522f475Smrg    return (result);
2940d522f475Smrg}
2941d522f475Smrg
2942d522f475SmrgPixel
2943cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2944d522f475Smrg{
2945d522f475Smrg    Pixel result = 0;
2946d522f475Smrg
2947d522f475Smrg    if (res->mode) {
2948d522f475Smrg	result = res->value;
2949d522f475Smrg    } else {
2950d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2951cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2952d522f475Smrg
2953cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2954cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2955d522f475Smrg
2956cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2957cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2958d522f475Smrg		res->mode = -True;
29593367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
29603367019cSmrg			     NonNull(res->resource));
2961d522f475Smrg	    }
2962d522f475Smrg	    result = res->value;
2963d522f475Smrg	} else {
2964d522f475Smrg	    result = 0;
2965d522f475Smrg	}
2966d522f475Smrg    }
2967d522f475Smrg    return result;
2968d522f475Smrg}
2969d522f475Smrg
2970cd3331d0Smrgstatic int
2971cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2972cd3331d0Smrg{
2973cd3331d0Smrg    int code;
2974cd3331d0Smrg
2975cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2976cd3331d0Smrg	code = -1;
2977cd3331d0Smrg    } else {
2978cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2979cd3331d0Smrg
2980cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2981cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2982cd3331d0Smrg    }
2983cd3331d0Smrg    return code;
2984cd3331d0Smrg}
2985cd3331d0Smrg
2986cd3331d0Smrg/*
2987cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2988cd3331d0Smrg * values from the given buffer.
2989cd3331d0Smrg *
2990cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2991cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2992cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2993cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2994cd3331d0Smrg * 'first' set to the beginning of those indices.
2995cd3331d0Smrg *
2996cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2997cd3331d0Smrg */
2998d522f475Smrgstatic Bool
2999d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
3000d4fba8b9Smrg		       int opcode,
3001d522f475Smrg		       char *buf,
3002cd3331d0Smrg		       int first,
3003d522f475Smrg		       int final)
3004d522f475Smrg{
3005d522f475Smrg    int repaint = False;
3006d522f475Smrg    int code;
3007cd3331d0Smrg    int last = (MAXCOLORS - first);
3008d4fba8b9Smrg    int queried = 0;
3009d522f475Smrg
3010d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
3011d522f475Smrg
3012d522f475Smrg    while (buf && *buf) {
3013037a25ddSmrg	int color;
3014037a25ddSmrg	char *name = strchr(buf, ';');
3015037a25ddSmrg
3016d522f475Smrg	if (name == NULL)
3017d522f475Smrg	    break;
3018d522f475Smrg	*name = '\0';
3019d522f475Smrg	name++;
3020d522f475Smrg	color = atoi(buf);
3021cd3331d0Smrg	if (color < 0 || color >= last)
3022cd3331d0Smrg	    break;		/* quit on any error */
3023d522f475Smrg	buf = strchr(name, ';');
3024d522f475Smrg	if (buf) {
3025d522f475Smrg	    *buf = '\0';
3026d522f475Smrg	    buf++;
3027d522f475Smrg	}
3028cd3331d0Smrg	if (!strcmp(name, "?")) {
3029d4fba8b9Smrg	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3030d4fba8b9Smrg		++queried;
3031cd3331d0Smrg	} else {
3032cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
3033d522f475Smrg	    if (code < 0) {
3034d522f475Smrg		/* stop on any error */
3035d522f475Smrg		break;
3036d522f475Smrg	    } else if (code > 0) {
3037d522f475Smrg		repaint = True;
3038d522f475Smrg	    }
3039d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
3040d522f475Smrg	     * change style (dynamic colors).
3041d522f475Smrg	     */
3042d522f475Smrg	}
3043d522f475Smrg    }
3044d4fba8b9Smrg    if (queried)
3045d4fba8b9Smrg	unparse_end(xw);
3046d522f475Smrg
3047d522f475Smrg    return (repaint);
3048d522f475Smrg}
3049cd3331d0Smrg
3050cd3331d0Smrgstatic Bool
3051cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
3052cd3331d0Smrg{
3053cd3331d0Smrg    Bool repaint = False;
3054cd3331d0Smrg    int last = MAXCOLORS - start;
3055cd3331d0Smrg
3056cd3331d0Smrg    if (color >= 0 && color < last) {
3057cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3058cd3331d0Smrg
3059cd3331d0Smrg	if (res->mode) {
3060cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
3061cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3062cd3331d0Smrg		repaint = True;
3063cd3331d0Smrg	    }
3064cd3331d0Smrg	}
3065cd3331d0Smrg    }
3066cd3331d0Smrg    return repaint;
3067cd3331d0Smrg}
3068cd3331d0Smrg
3069cd3331d0Smrgint
3070cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3071cd3331d0Smrg{
3072cd3331d0Smrg    int repaint = 0;
3073cd3331d0Smrg    int color;
3074cd3331d0Smrg
3075cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3076cd3331d0Smrg    if (*buf != '\0') {
3077cd3331d0Smrg	/* reset specific colors */
3078cd3331d0Smrg	while (!IsEmpty(buf)) {
3079cd3331d0Smrg	    char *next;
3080cd3331d0Smrg
3081037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
3082037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
3083cd3331d0Smrg		break;		/* no number at all */
3084cd3331d0Smrg	    if (next != 0) {
3085cd3331d0Smrg		if (strchr(";", *next) == 0)
3086cd3331d0Smrg		    break;	/* unexpected delimiter */
3087cd3331d0Smrg		++next;
3088cd3331d0Smrg	    }
3089cd3331d0Smrg
3090cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3091cd3331d0Smrg		++repaint;
3092cd3331d0Smrg	    }
3093cd3331d0Smrg	    buf = next;
3094cd3331d0Smrg	}
3095cd3331d0Smrg    } else {
3096cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3097cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
3098cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3099cd3331d0Smrg		++repaint;
3100cd3331d0Smrg	    }
3101cd3331d0Smrg	}
3102cd3331d0Smrg    }
3103cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3104cd3331d0Smrg    return repaint;
3105cd3331d0Smrg}
3106d522f475Smrg#else
3107c48a5815Smrg#define allocateClosestRGB(xw, def) 0
3108d522f475Smrg#endif /* OPT_ISO_COLORS */
3109d522f475Smrg
3110fa3f02f3SmrgBoolean
31119a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
3112fa3f02f3Smrg{
3113c48a5815Smrg    (void) xw;
3114c48a5815Smrg    (void) def;
3115c48a5815Smrg    return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
3116fa3f02f3Smrg}
3117fa3f02f3Smrg
31183367019cSmrgstatic Boolean
31199a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
31203367019cSmrg{
31213367019cSmrg    Boolean result = False;
31223367019cSmrg    TScreen *screen = TScreenOf(xw);
31233367019cSmrg    Colormap cmap = xw->core.colormap;
31248f44fb3bSmrg    size_t have = strlen(spec);
31253367019cSmrg
31268f44fb3bSmrg    if (have == 0 || have > MAX_U_STRING) {
31278f44fb3bSmrg	if (resource.reportColors) {
3128c48a5815Smrg	    printf("color  (ignored, length %lu)\n", (unsigned long) have);
31298f44fb3bSmrg	}
31308f44fb3bSmrg    } else if (XParseColor(screen->display, cmap, spec, def)) {
3131fa3f02f3Smrg	XColor save_def = *def;
3132fa3f02f3Smrg	if (resource.reportColors) {
3133fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3134fa3f02f3Smrg		   def->red, def->green, def->blue,
3135fa3f02f3Smrg		   spec);
3136fa3f02f3Smrg	}
3137fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
3138fa3f02f3Smrg	    if (resource.reportColors) {
3139fa3f02f3Smrg		if (def->red != save_def.red ||
3140fa3f02f3Smrg		    def->green != save_def.green ||
3141fa3f02f3Smrg		    def->blue != save_def.blue) {
3142fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3143fa3f02f3Smrg			   def->red, def->green, def->blue,
3144fa3f02f3Smrg			   spec);
3145fa3f02f3Smrg		}
3146fa3f02f3Smrg	    }
3147fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3148fa3f02f3Smrg		   def->red, def->green, def->blue));
3149fa3f02f3Smrg	    result = True;
3150fa3f02f3Smrg	}
31513367019cSmrg    }
31523367019cSmrg    return result;
31533367019cSmrg}
31543367019cSmrg
31553367019cSmrg/*
31563367019cSmrg * This provides an approximation (the closest color from xterm's palette)
31573367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
31583367019cSmrg * because of the context in which it is used.
31593367019cSmrg */
31603367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
31613367019cSmrgint
31623367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
31633367019cSmrg{
31643367019cSmrg    int result = -1;
3165c48a5815Smrg#if OPT_ISO_COLORS
31663367019cSmrg    int n;
31673367019cSmrg    int best_index = -1;
31683367019cSmrg    unsigned long best_value = 0;
31693367019cSmrg    unsigned long this_value;
31703367019cSmrg    long diff_red, diff_green, diff_blue;
31713367019cSmrg
31723367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
31733367019cSmrg
31743367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
31753367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
31763367019cSmrg
31773367019cSmrg	/* ensure that we have a value for each of the colors */
31783367019cSmrg	if (!res->mode) {
31793367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
31803367019cSmrg	}
31813367019cSmrg
31823367019cSmrg	/* find the closest match */
31833367019cSmrg	if (res->mode == True) {
31843367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
31853367019cSmrg		    res->value, res->red, res->green, res->blue));
31863367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
31873367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
31883367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
31893367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
31903367019cSmrg					  + (diff_green * diff_green)
31913367019cSmrg					  + (diff_blue * diff_blue));
31923367019cSmrg	    if (best_index < 0 || this_value < best_value) {
31933367019cSmrg		best_index = n;
31943367019cSmrg		best_value = this_value;
31953367019cSmrg	    }
31963367019cSmrg	}
31973367019cSmrg    }
31983367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
31993367019cSmrg    result = best_index;
3200c48a5815Smrg
32013367019cSmrg#else
32023367019cSmrg    (void) xw;
32033367019cSmrg    (void) find_red;
32043367019cSmrg    (void) find_green;
32053367019cSmrg    (void) find_blue;
32063367019cSmrg#endif
32073367019cSmrg    return result;
32083367019cSmrg}
32093367019cSmrg
3210d4fba8b9Smrg#if OPT_DIRECT_COLOR
3211d4fba8b9Smrgint
3212d4fba8b9SmrggetDirectColor(XtermWidget xw, int red, int green, int blue)
3213d4fba8b9Smrg{
3214c48a5815Smrg    Pixel result = 0;
3215c48a5815Smrg
3216c48a5815Smrg#define getRGB(name,shift) \
3217c48a5815Smrg    do { \
3218c48a5815Smrg	Pixel value = (Pixel) name & 0xff; \
3219c48a5815Smrg	if (xw->rgb_widths[shift] < 8) { \
3220c48a5815Smrg	    value >>= (int) (8 - xw->rgb_widths[shift]); \
3221c48a5815Smrg	} \
3222c48a5815Smrg	value <<= xw->rgb_shifts[shift]; \
3223c48a5815Smrg	value &= xw->visInfo->name ##_mask; \
3224c48a5815Smrg	result |= value; \
3225c48a5815Smrg    } while (0)
3226c48a5815Smrg
3227c48a5815Smrg    getRGB(red, 0);
3228c48a5815Smrg    getRGB(green, 1);
3229c48a5815Smrg    getRGB(blue, 2);
3230c48a5815Smrg
3231c48a5815Smrg#undef getRGB
3232c48a5815Smrg
3233d4fba8b9Smrg    return (int) result;
3234d4fba8b9Smrg}
3235d4fba8b9Smrg
3236d4fba8b9Smrgstatic void
3237d4fba8b9SmrgformatDirectColor(char *target, XtermWidget xw, unsigned value)
3238d4fba8b9Smrg{
3239c48a5815Smrg    Pixel result[3];
3240c48a5815Smrg
3241c48a5815Smrg#define getRGB(name, shift) \
3242c48a5815Smrg    do { \
3243c48a5815Smrg	result[shift] = value & xw->visInfo->name ## _mask; \
3244c48a5815Smrg	result[shift] >>= xw->rgb_shifts[shift]; \
3245c48a5815Smrg	if (xw->rgb_widths[shift] < 8) \
3246c48a5815Smrg	    result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
3247c48a5815Smrg    } while(0)
3248c48a5815Smrg
3249c48a5815Smrg    getRGB(red, 0);
3250c48a5815Smrg    getRGB(green, 1);
3251c48a5815Smrg    getRGB(blue, 2);
3252c48a5815Smrg
3253c48a5815Smrg#undef getRGB
3254c48a5815Smrg
3255c48a5815Smrg    sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
3256d4fba8b9Smrg}
3257d4fba8b9Smrg#endif /* OPT_DIRECT_COLOR */
3258d4fba8b9Smrg
3259d4fba8b9Smrg#define fg2SGR(n) \
3260d4fba8b9Smrg		(n) >= 8 ? 9 : 3, \
3261d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3262d4fba8b9Smrg#define bg2SGR(n) \
3263d4fba8b9Smrg		(n) >= 8 ? 10 : 4, \
3264d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3265d4fba8b9Smrg
3266d4fba8b9Smrg#define EndOf(s) (s) + strlen(s)
3267d4fba8b9Smrg
3268d4fba8b9Smrgchar *
3269d4fba8b9SmrgxtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3270d4fba8b9Smrg{
3271d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
3272d4fba8b9Smrg    char *msg = target;
3273d4fba8b9Smrg
3274d4fba8b9Smrg    strcpy(target, "0");
3275d4fba8b9Smrg    if (attr & BOLD)
3276d4fba8b9Smrg	strcat(msg, ";1");
3277d4fba8b9Smrg    if (attr & UNDERLINE)
3278d4fba8b9Smrg	strcat(msg, ";4");
3279d4fba8b9Smrg    if (attr & BLINK)
3280d4fba8b9Smrg	strcat(msg, ";5");
3281d4fba8b9Smrg    if (attr & INVERSE)
3282d4fba8b9Smrg	strcat(msg, ";7");
3283d4fba8b9Smrg    if (attr & INVISIBLE)
3284d4fba8b9Smrg	strcat(msg, ";8");
3285d4fba8b9Smrg#if OPT_WIDE_ATTRS
3286d4fba8b9Smrg    if (attr & ATR_FAINT)
3287d4fba8b9Smrg	strcat(msg, ";2");
3288d4fba8b9Smrg    if (attr & ATR_ITALIC)
3289d4fba8b9Smrg	strcat(msg, ";3");
3290d4fba8b9Smrg    if (attr & ATR_STRIKEOUT)
3291d4fba8b9Smrg	strcat(msg, ";9");
3292d4fba8b9Smrg    if (attr & ATR_DBL_UNDER)
3293d4fba8b9Smrg	strcat(msg, ";21");
3294d4fba8b9Smrg#endif
3295d4fba8b9Smrg#if OPT_256_COLORS || OPT_88_COLORS
3296d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3297d4fba8b9Smrg	if (attr & FG_COLOR) {
3298d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3299d4fba8b9Smrg		strcat(msg, ";38:2::");
3300d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3301d4fba8b9Smrg	    }) if (fg >= 16) {
3302d4fba8b9Smrg		sprintf(EndOf(msg), ";38:5:%d", fg);
3303d4fba8b9Smrg	    } else {
3304d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3305d4fba8b9Smrg	    }
3306d4fba8b9Smrg	}
3307d4fba8b9Smrg	if (attr & BG_COLOR) {
3308d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3309d4fba8b9Smrg		strcat(msg, ";48:2::");
3310d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3311d4fba8b9Smrg	    }) if (bg >= 16) {
3312d4fba8b9Smrg		sprintf(EndOf(msg), ";48:5:%d", bg);
3313d4fba8b9Smrg	    } else {
3314d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3315d4fba8b9Smrg	    }
3316d4fba8b9Smrg	}
3317d4fba8b9Smrg    });
3318d4fba8b9Smrg#elif OPT_ISO_COLORS
3319d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3320d4fba8b9Smrg	if (attr & FG_COLOR) {
3321d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3322d4fba8b9Smrg	}
3323d4fba8b9Smrg	if (attr & BG_COLOR) {
3324d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3325d4fba8b9Smrg	}
3326d4fba8b9Smrg    });
3327d4fba8b9Smrg#else
3328d4fba8b9Smrg    (void) screen;
3329d4fba8b9Smrg    (void) fg;
3330d4fba8b9Smrg    (void) bg;
3331d4fba8b9Smrg#endif
3332d4fba8b9Smrg    return target;
3333d4fba8b9Smrg}
3334d4fba8b9Smrg
3335d522f475Smrg#if OPT_PASTE64
3336d522f475Smrgstatic void
3337fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3338d522f475Smrg{
3339d522f475Smrg#define PDATA(a,b) { a, #b }
3340d522f475Smrg    static struct {
3341d522f475Smrg	char given;
3342cd3331d0Smrg	String result;
3343d522f475Smrg    } table[] = {
3344d522f475Smrg	PDATA('s', SELECT),
3345d522f475Smrg	    PDATA('p', PRIMARY),
3346d4fba8b9Smrg	    PDATA('q', SECONDARY),
3347d522f475Smrg	    PDATA('c', CLIPBOARD),
3348d522f475Smrg	    PDATA('0', CUT_BUFFER0),
3349d522f475Smrg	    PDATA('1', CUT_BUFFER1),
3350d522f475Smrg	    PDATA('2', CUT_BUFFER2),
3351d522f475Smrg	    PDATA('3', CUT_BUFFER3),
3352d522f475Smrg	    PDATA('4', CUT_BUFFER4),
3353d522f475Smrg	    PDATA('5', CUT_BUFFER5),
3354d522f475Smrg	    PDATA('6', CUT_BUFFER6),
3355d522f475Smrg	    PDATA('7', CUT_BUFFER7),
3356d522f475Smrg    };
3357d522f475Smrg
3358cd3331d0Smrg    const char *base = buf;
3359d522f475Smrg    Cardinal j, n = 0;
3360d522f475Smrg
3361d522f475Smrg    TRACE(("Manipulate selection data\n"));
3362d522f475Smrg
3363d522f475Smrg    while (*buf != ';' && *buf != '\0') {
3364d522f475Smrg	++buf;
3365d522f475Smrg    }
3366d522f475Smrg
3367d522f475Smrg    if (*buf == ';') {
3368037a25ddSmrg	char *used;
3369037a25ddSmrg
3370d522f475Smrg	*buf++ = '\0';
3371d522f475Smrg
3372d522f475Smrg	if (*base == '\0')
3373d522f475Smrg	    base = "s0";
3374d522f475Smrg
33753367019cSmrg	if ((used = x_strdup(base)) != 0) {
3376037a25ddSmrg	    String *select_args;
3377037a25ddSmrg
33783367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
33793367019cSmrg		while (*base != '\0') {
33803367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
33813367019cSmrg			if (*base == table[j].given) {
33823367019cSmrg			    used[n] = *base;
33833367019cSmrg			    select_args[n++] = table[j].result;
33843367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
33853367019cSmrg			    break;
33863367019cSmrg			}
33873367019cSmrg		    }
33883367019cSmrg		    ++base;
33893367019cSmrg		}
33903367019cSmrg		used[n] = 0;
33913367019cSmrg
33923367019cSmrg		if (!strcmp(buf, "?")) {
33933367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
33943367019cSmrg			TRACE(("Getting selection\n"));
33953367019cSmrg			unparseputc1(xw, ANSI_OSC);
33963367019cSmrg			unparseputs(xw, "52");
33973367019cSmrg			unparseputc(xw, ';');
33983367019cSmrg
33993367019cSmrg			unparseputs(xw, used);
34003367019cSmrg			unparseputc(xw, ';');
34013367019cSmrg
34023367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
34033367019cSmrg			screen->base64_paste = n;
34043367019cSmrg			screen->base64_final = final;
34053367019cSmrg
3406dfb07bc7Smrg			screen->selection_time =
3407dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3408dfb07bc7Smrg
34093367019cSmrg			/* terminator will be written in this call */
34103367019cSmrg			xtermGetSelection((Widget) xw,
3411dfb07bc7Smrg					  screen->selection_time,
34123367019cSmrg					  select_args, n,
34133367019cSmrg					  NULL);
341494644356Smrg			/*
341594644356Smrg			 * select_args is used via SelectionReceived, cannot
341694644356Smrg			 * free it here.
341794644356Smrg			 */
341894644356Smrg		    } else {
341994644356Smrg			free(select_args);
34203367019cSmrg		    }
34213367019cSmrg		} else {
34223367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
3423d4fba8b9Smrg			char *old = buf;
3424d4fba8b9Smrg
3425d4fba8b9Smrg			TRACE(("Setting selection(%s) with %s\n", used, buf));
3426dfb07bc7Smrg			screen->selection_time =
3427dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3428d4fba8b9Smrg
3429d4fba8b9Smrg			for (j = 0; j < n; ++j) {
3430d4fba8b9Smrg			    buf = old;
3431d4fba8b9Smrg			    ClearSelectionBuffer(screen, select_args[j]);
3432d4fba8b9Smrg			    while (*buf != '\0') {
3433d4fba8b9Smrg				AppendToSelectionBuffer(screen,
3434d4fba8b9Smrg							CharOf(*buf++),
3435d4fba8b9Smrg							select_args[j]);
3436d4fba8b9Smrg			    }
3437d4fba8b9Smrg			}
34383367019cSmrg			CompleteSelection(xw, select_args, n);
34393367019cSmrg		    }
344094644356Smrg		    free(select_args);
34413367019cSmrg		}
3442cd3331d0Smrg	    }
34433367019cSmrg	    free(used);
3444d522f475Smrg	}
3445d522f475Smrg    }
3446d522f475Smrg}
3447d522f475Smrg#endif /* OPT_PASTE64 */
3448d522f475Smrg
3449d522f475Smrg/***====================================================================***/
3450d522f475Smrg
3451d4fba8b9Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3452d4fba8b9Smrg			 || (xw->screen.utf8_title) \
3453d4fba8b9Smrg			 || (xw->screen.c1_printable))
3454cd3331d0Smrg
3455d522f475Smrgstatic Bool
3456fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3457d522f475Smrg{
3458cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3459d522f475Smrg    Bool result = False;
3460d522f475Smrg    Char *cp = *bufp;
3461d522f475Smrg    Char *next = cp;
3462d522f475Smrg
3463d522f475Smrg    (void) screen;
3464d522f475Smrg    (void) last;
3465d522f475Smrg
3466d522f475Smrg#if OPT_WIDE_CHARS
3467cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3468d522f475Smrg	PtyData data;
3469d522f475Smrg
34709a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3471d522f475Smrg	    if (data.utf_data != UCS_REPL
3472d522f475Smrg		&& (data.utf_data >= 128 ||
3473d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3474d522f475Smrg		next += (data.utf_size - 1);
3475d522f475Smrg		result = True;
3476d522f475Smrg	    } else {
3477d522f475Smrg		result = False;
3478d522f475Smrg	    }
3479d522f475Smrg	} else {
3480d522f475Smrg	    result = False;
3481d522f475Smrg	}
3482d522f475Smrg    } else
3483d522f475Smrg#endif
3484d522f475Smrg#if OPT_C1_PRINT
3485d522f475Smrg	if (screen->c1_printable
3486d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3487d522f475Smrg	result = True;
3488d522f475Smrg    } else
3489d522f475Smrg#endif
3490d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3491d522f475Smrg	result = True;
3492d522f475Smrg    }
3493d522f475Smrg    *bufp = next;
3494d522f475Smrg    return result;
3495d522f475Smrg}
3496d522f475Smrg
3497d522f475Smrg/***====================================================================***/
3498d522f475Smrg
3499d522f475Smrg/*
3500d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3501cd3331d0Smrg * array indices.  Compare with TermColors.
3502d522f475Smrg */
3503d522f475Smrgtypedef enum {
3504d522f475Smrg    OSC_TEXT_FG = 10
3505d522f475Smrg    ,OSC_TEXT_BG
3506d522f475Smrg    ,OSC_TEXT_CURSOR
3507d522f475Smrg    ,OSC_MOUSE_FG
3508d522f475Smrg    ,OSC_MOUSE_BG
3509d522f475Smrg#if OPT_TEK4014
3510d522f475Smrg    ,OSC_TEK_FG = 15
3511d522f475Smrg    ,OSC_TEK_BG
3512d522f475Smrg#endif
3513d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3514d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3515d522f475Smrg#endif
3516d522f475Smrg#if OPT_TEK4014
3517d522f475Smrg    ,OSC_TEK_CURSOR = 18
3518d522f475Smrg#endif
3519d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3520d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3521d522f475Smrg#endif
3522d522f475Smrg    ,OSC_NCOLORS
3523d522f475Smrg} OscTextColors;
3524d522f475Smrg
3525cd3331d0Smrg/*
3526cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3527cd3331d0Smrg */
3528cd3331d0Smrg#define OSC_RESET 100
3529cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3530cd3331d0Smrg
3531d522f475Smrgstatic Bool
3532d522f475SmrgGetOldColors(XtermWidget xw)
3533d522f475Smrg{
35349a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3535037a25ddSmrg	int i;
3536037a25ddSmrg
35379a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
35389a64e1c5Smrg	if (xw->work.oldColors == NULL) {
35393367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3540d522f475Smrg	    return (False);
3541d522f475Smrg	}
35429a64e1c5Smrg	xw->work.oldColors->which = 0;
3543d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
35449a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
35459a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3546d522f475Smrg	}
35479a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3548d522f475Smrg    }
3549d522f475Smrg    return (True);
3550d522f475Smrg}
3551d522f475Smrg
3552d522f475Smrgstatic int
3553d4fba8b9SmrgoppositeColor(XtermWidget xw, int n)
3554d522f475Smrg{
3555d4fba8b9Smrg    Boolean reversed = (xw->misc.re_verse);
3556d4fba8b9Smrg
3557d522f475Smrg    switch (n) {
3558d522f475Smrg    case TEXT_FG:
3559d4fba8b9Smrg	n = reversed ? TEXT_FG : TEXT_BG;
3560d522f475Smrg	break;
3561d522f475Smrg    case TEXT_BG:
3562d4fba8b9Smrg	n = reversed ? TEXT_BG : TEXT_FG;
3563d522f475Smrg	break;
3564d522f475Smrg    case MOUSE_FG:
3565d522f475Smrg	n = MOUSE_BG;
3566d522f475Smrg	break;
3567d522f475Smrg    case MOUSE_BG:
3568d522f475Smrg	n = MOUSE_FG;
3569d522f475Smrg	break;
3570d522f475Smrg#if OPT_TEK4014
3571d522f475Smrg    case TEK_FG:
3572d4fba8b9Smrg	n = reversed ? TEK_FG : TEK_BG;
3573d522f475Smrg	break;
3574d522f475Smrg    case TEK_BG:
3575d4fba8b9Smrg	n = reversed ? TEK_BG : TEK_FG;
3576d522f475Smrg	break;
3577d522f475Smrg#endif
3578d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3579d522f475Smrg    case HIGHLIGHT_FG:
3580d522f475Smrg	n = HIGHLIGHT_BG;
3581d522f475Smrg	break;
3582d522f475Smrg    case HIGHLIGHT_BG:
3583d522f475Smrg	n = HIGHLIGHT_FG;
3584d522f475Smrg	break;
3585d522f475Smrg#endif
3586d522f475Smrg    default:
3587d522f475Smrg	break;
3588d522f475Smrg    }
3589d522f475Smrg    return n;
3590d522f475Smrg}
3591d522f475Smrg
3592d4fba8b9Smrgstatic Bool
3593d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3594d522f475Smrg{
3595d4fba8b9Smrg    Bool result = False;
3596d4fba8b9Smrg
3597cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3598cd3331d0Smrg	XColor color;
3599cd3331d0Smrg	char buffer[80];
3600d522f475Smrg
3601cd3331d0Smrg	/*
3602cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3603cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3604cd3331d0Smrg	 * reporting the opposite color which would be used.
3605cd3331d0Smrg	 */
3606d4fba8b9Smrg	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3607cd3331d0Smrg
3608cd3331d0Smrg	GetOldColors(xw);
36099a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3610c48a5815Smrg	(void) QueryOneColor(xw, &color);
3611cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3612cd3331d0Smrg		color.red,
3613cd3331d0Smrg		color.green,
3614cd3331d0Smrg		color.blue);
3615712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
36169a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3617cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3618cd3331d0Smrg	unparseputs(xw, buffer);
3619cd3331d0Smrg	unparseputc1(xw, final);
3620d4fba8b9Smrg	result = True;
3621cd3331d0Smrg    }
3622d4fba8b9Smrg    return result;
3623d522f475Smrg}
3624d522f475Smrg
3625d522f475Smrgstatic Bool
3626d4fba8b9SmrgUpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3627d522f475Smrg{
3628d522f475Smrg    int i;
3629d522f475Smrg
3630d522f475Smrg    /* if we were going to free old colors, this would be the place to
3631d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3632d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3633d522f475Smrg     * we could save some overhead this way.   The only case in which this
3634d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3635d522f475Smrg     * which case they can restart xterm
3636d522f475Smrg     */
3637d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3638d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
36399a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
36409a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
36419a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3642d522f475Smrg	    }
3643d522f475Smrg	    if (pNew->names[i]) {
36449a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3645d522f475Smrg	    }
36469a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3647d522f475Smrg	}
3648d522f475Smrg    }
3649d522f475Smrg    return (True);
3650d522f475Smrg}
3651d522f475Smrg
3652d522f475Smrg/*
3653d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3654d522f475Smrg * xterm is compiled.
3655d522f475Smrg */
3656d522f475Smrgstatic int
3657d522f475SmrgOscToColorIndex(OscTextColors mode)
3658d522f475Smrg{
3659d522f475Smrg    int result = 0;
3660d522f475Smrg
3661d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3662d522f475Smrg    switch (mode) {
3663d522f475Smrg	CASE(TEXT_FG);
3664d522f475Smrg	CASE(TEXT_BG);
3665d522f475Smrg	CASE(TEXT_CURSOR);
3666d522f475Smrg	CASE(MOUSE_FG);
3667d522f475Smrg	CASE(MOUSE_BG);
3668d522f475Smrg#if OPT_TEK4014
3669d522f475Smrg	CASE(TEK_FG);
3670d522f475Smrg	CASE(TEK_BG);
3671d522f475Smrg#endif
3672d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3673d522f475Smrg	CASE(HIGHLIGHT_BG);
3674d522f475Smrg	CASE(HIGHLIGHT_FG);
3675d522f475Smrg#endif
3676d522f475Smrg#if OPT_TEK4014
3677d522f475Smrg	CASE(TEK_CURSOR);
3678d522f475Smrg#endif
3679d522f475Smrg    case OSC_NCOLORS:
3680d522f475Smrg	break;
3681d522f475Smrg    }
3682d522f475Smrg    return result;
3683d522f475Smrg}
3684d522f475Smrg
3685d522f475Smrgstatic Bool
3686d522f475SmrgChangeColorsRequest(XtermWidget xw,
3687d522f475Smrg		    int start,
3688d522f475Smrg		    char *names,
3689d522f475Smrg		    int final)
3690d522f475Smrg{
3691d522f475Smrg    Bool result = False;
3692d522f475Smrg    ScrnColors newColors;
3693d522f475Smrg
3694d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3695d522f475Smrg
3696d522f475Smrg    if (GetOldColors(xw)) {
3697037a25ddSmrg	int i;
3698d4fba8b9Smrg	int queried = 0;
3699037a25ddSmrg
3700d522f475Smrg	newColors.which = 0;
3701d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3702d522f475Smrg	    newColors.names[i] = NULL;
3703d522f475Smrg	}
3704d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3705037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3706d522f475Smrg	    if (xw->misc.re_verse)
3707d4fba8b9Smrg		ndx = oppositeColor(xw, ndx);
3708d522f475Smrg
3709cd3331d0Smrg	    if (IsEmpty(names)) {
3710d522f475Smrg		newColors.names[ndx] = NULL;
3711d522f475Smrg	    } else {
3712037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3713037a25ddSmrg
3714d522f475Smrg		names = strchr(names, ';');
3715d522f475Smrg		if (names != NULL) {
3716d522f475Smrg		    *names++ = '\0';
3717d522f475Smrg		}
3718fa3f02f3Smrg		if (thisName != 0) {
3719fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3720d4fba8b9Smrg			if (ReportColorRequest(xw, ndx, final))
3721d4fba8b9Smrg			    ++queried;
37229a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
37239a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3724fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3725fa3f02f3Smrg		    }
3726d522f475Smrg		}
3727d522f475Smrg	    }
3728d522f475Smrg	}
3729d522f475Smrg
3730d522f475Smrg	if (newColors.which != 0) {
3731d522f475Smrg	    ChangeColors(xw, &newColors);
3732d522f475Smrg	    UpdateOldColors(xw, &newColors);
3733d4fba8b9Smrg	} else if (queried) {
3734d4fba8b9Smrg	    unparse_end(xw);
3735d522f475Smrg	}
3736d522f475Smrg	result = True;
3737d522f475Smrg    }
3738d522f475Smrg    return result;
3739d522f475Smrg}
3740d522f475Smrg
3741cd3331d0Smrgstatic Bool
3742cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3743cd3331d0Smrg		   int code)
3744cd3331d0Smrg{
3745cd3331d0Smrg    Bool result = False;
3746cd3331d0Smrg
3747dfb07bc7Smrg    (void) xw;
3748dfb07bc7Smrg    (void) code;
3749dfb07bc7Smrg
3750cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3751cd3331d0Smrg    if (GetOldColors(xw)) {
3752037a25ddSmrg	ScrnColors newColors;
3753037a25ddSmrg	const char *thisName;
3754037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3755037a25ddSmrg
3756cd3331d0Smrg	if (xw->misc.re_verse)
3757d4fba8b9Smrg	    ndx = oppositeColor(xw, ndx);
3758cd3331d0Smrg
3759cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3760cd3331d0Smrg
3761cd3331d0Smrg	newColors.which = 0;
3762cd3331d0Smrg	newColors.names[ndx] = NULL;
3763cd3331d0Smrg
3764cd3331d0Smrg	if (thisName != 0
37659a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
37669a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3767cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3768cd3331d0Smrg
3769cd3331d0Smrg	    if (newColors.which != 0) {
3770cd3331d0Smrg		ChangeColors(xw, &newColors);
3771cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3772cd3331d0Smrg	    }
3773cd3331d0Smrg	}
3774cd3331d0Smrg	result = True;
3775cd3331d0Smrg    }
3776cd3331d0Smrg    return result;
3777cd3331d0Smrg}
3778cd3331d0Smrg
3779cd3331d0Smrg#if OPT_SHIFT_FONTS
3780cd3331d0Smrg/*
3781cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3782cd3331d0Smrg *
3783cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3784cd3331d0Smrg * the corresponding menu font entry.
3785cd3331d0Smrg */
3786cd3331d0Smrgstatic int
3787fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3788cd3331d0Smrg{
3789cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3790cd3331d0Smrg    int num = screen->menu_font_number;
3791cd3331d0Smrg    int rel = 0;
3792cd3331d0Smrg
3793cd3331d0Smrg    if (*++source == '+') {
3794cd3331d0Smrg	rel = 1;
3795cd3331d0Smrg	source++;
3796cd3331d0Smrg    } else if (*source == '-') {
3797cd3331d0Smrg	rel = -1;
3798cd3331d0Smrg	source++;
3799cd3331d0Smrg    }
3800cd3331d0Smrg
3801cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3802cd3331d0Smrg	int val = atoi(source);
3803cd3331d0Smrg	if (rel > 0)
3804cd3331d0Smrg	    rel = val;
3805cd3331d0Smrg	else if (rel < 0)
3806cd3331d0Smrg	    rel = -val;
3807cd3331d0Smrg	else
3808cd3331d0Smrg	    num = val;
3809cd3331d0Smrg    }
3810cd3331d0Smrg
3811cd3331d0Smrg    if (rel != 0) {
3812cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3813cd3331d0Smrg				     screen->menu_font_number, rel);
3814cd3331d0Smrg
3815cd3331d0Smrg    }
3816cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3817cd3331d0Smrg    *target = source;
3818cd3331d0Smrg    return num;
3819cd3331d0Smrg}
3820cd3331d0Smrg
3821cd3331d0Smrgstatic void
3822cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3823cd3331d0Smrg{
3824cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3825cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3826cd3331d0Smrg	Bool success = True;
3827cd3331d0Smrg	int num;
3828cb4a1343Smrg	String base = buf + 1;
3829cd3331d0Smrg	const char *name = 0;
3830cd3331d0Smrg
3831cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3832cd3331d0Smrg	if (num < 0
3833cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3834cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3835cd3331d0Smrg	    success = False;
3836cd3331d0Smrg	} else {
3837cd3331d0Smrg#if OPT_RENDERFONT
3838cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3839cd3331d0Smrg		name = getFaceName(xw, False);
3840cd3331d0Smrg	    } else
3841cd3331d0Smrg#endif
3842cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3843cd3331d0Smrg		success = False;
3844cd3331d0Smrg	    }
3845cd3331d0Smrg	}
3846cd3331d0Smrg
3847cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3848cd3331d0Smrg	unparseputs(xw, "50");
3849cd3331d0Smrg
3850cd3331d0Smrg	if (success) {
3851cd3331d0Smrg	    unparseputc(xw, ';');
3852cd3331d0Smrg	    if (buf >= base) {
3853cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3854cd3331d0Smrg		if (*buf != '\0') {
3855037a25ddSmrg		    char temp[10];
3856037a25ddSmrg
3857cd3331d0Smrg		    unparseputc(xw, '#');
3858cd3331d0Smrg		    sprintf(temp, "%d", num);
3859cd3331d0Smrg		    unparseputs(xw, temp);
3860cd3331d0Smrg		    if (*name != '\0')
3861cd3331d0Smrg			unparseputc(xw, ' ');
3862cd3331d0Smrg		}
3863cd3331d0Smrg	    }
3864cd3331d0Smrg	    unparseputs(xw, name);
3865cd3331d0Smrg	}
3866cd3331d0Smrg
3867cd3331d0Smrg	unparseputc1(xw, final);
3868cd3331d0Smrg	unparse_end(xw);
3869cd3331d0Smrg    }
3870cd3331d0Smrg}
3871cd3331d0Smrg
3872cd3331d0Smrgstatic void
3873cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3874cd3331d0Smrg{
3875cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3876cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3877cd3331d0Smrg	Bool success = True;
3878cd3331d0Smrg	int num;
3879cd3331d0Smrg	VTFontNames fonts;
3880cd3331d0Smrg	char *name;
3881cd3331d0Smrg
3882cd3331d0Smrg	/*
3883cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3884cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3885cd3331d0Smrg	 *
3886cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3887cd3331d0Smrg	 * to load the font entry.
3888cd3331d0Smrg	 */
3889cd3331d0Smrg	if (*buf == '#') {
3890cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3891cd3331d0Smrg
3892cd3331d0Smrg	    if (num < 0
3893cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3894cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3895cd3331d0Smrg		success = False;
3896cd3331d0Smrg	    } else {
3897cd3331d0Smrg		/*
3898cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3899cd3331d0Smrg		 * for a font specification within the control.
3900cd3331d0Smrg		 */
3901cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3902cd3331d0Smrg		    ++buf;
3903cd3331d0Smrg		}
3904cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3905cd3331d0Smrg		    ++buf;
3906cd3331d0Smrg		}
3907cd3331d0Smrg#if OPT_RENDERFONT
3908cd3331d0Smrg		if (UsingRenderFont(xw)) {
3909c219fbebSmrg		    /* EMPTY */
3910c219fbebSmrg		    /* there is only one font entry to load */
3911c219fbebSmrg		    ;
3912cd3331d0Smrg		} else
3913cd3331d0Smrg#endif
3914cd3331d0Smrg		{
3915cd3331d0Smrg		    /*
3916cd3331d0Smrg		     * Normally there is no font specified in the control.
3917cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3918cd3331d0Smrg		     */
3919cd3331d0Smrg		    if (*buf == '\0') {
3920cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3921cd3331d0Smrg			    success = False;
3922cd3331d0Smrg			}
3923cd3331d0Smrg		    }
3924cd3331d0Smrg		}
3925cd3331d0Smrg	    }
3926cd3331d0Smrg	} else {
3927cd3331d0Smrg	    num = screen->menu_font_number;
3928cd3331d0Smrg	}
3929cd3331d0Smrg	name = x_strtrim(buf);
393094644356Smrg	if (screen->EscapeFontName()) {
393194644356Smrg	    FREE_STRING(screen->EscapeFontName());
393294644356Smrg	    screen->EscapeFontName() = 0;
393394644356Smrg	}
3934cd3331d0Smrg	if (success && !IsEmpty(name)) {
3935cd3331d0Smrg#if OPT_RENDERFONT
3936cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3937cd3331d0Smrg		setFaceName(xw, name);
3938cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3939cd3331d0Smrg	    } else
3940cd3331d0Smrg#endif
3941cd3331d0Smrg	    {
3942cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3943cd3331d0Smrg		fonts.f_n = name;
3944cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
394594644356Smrg		if (num == screen->menu_font_number &&
394694644356Smrg		    num != fontMenu_fontescape) {
394794644356Smrg		    screen->EscapeFontName() = x_strdup(name);
394894644356Smrg		}
3949cd3331d0Smrg	    }
3950cd3331d0Smrg	} else {
3951cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3952cd3331d0Smrg	}
395394644356Smrg	update_font_escape();
3954cd3331d0Smrg	free(name);
3955cd3331d0Smrg    }
3956cd3331d0Smrg}
3957cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3958cd3331d0Smrg
3959d522f475Smrg/***====================================================================***/
3960d522f475Smrg
3961d522f475Smrgvoid
3962fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3963d522f475Smrg{
3964cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3965d522f475Smrg    int mode;
3966d522f475Smrg    Char *cp;
3967d522f475Smrg    int state = 0;
3968d522f475Smrg    char *buf = 0;
3969cd3331d0Smrg    char temp[2];
3970cd3331d0Smrg#if OPT_ISO_COLORS
3971cd3331d0Smrg    int ansi_colors = 0;
3972cd3331d0Smrg#endif
3973cd3331d0Smrg    Bool need_data = True;
3974fa3f02f3Smrg    Bool optional_data = False;
3975d522f475Smrg
3976d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3977d522f475Smrg
3978712a7ff4Smrg    (void) screen;
3979712a7ff4Smrg
3980d522f475Smrg    /*
3981d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3982d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3983d522f475Smrg     * with the same final character as the application sends to make this
3984d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3985d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3986d522f475Smrg     */
3987d522f475Smrg    mode = 0;
3988d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3989d522f475Smrg	switch (state) {
3990d522f475Smrg	case 0:
3991d522f475Smrg	    if (isdigit(*cp)) {
3992d522f475Smrg		mode = 10 * mode + (*cp - '0');
3993d522f475Smrg		if (mode > 65535) {
3994d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3995d522f475Smrg		    return;
3996d522f475Smrg		}
3997d522f475Smrg		break;
3998d4fba8b9Smrg	    } else {
3999d4fba8b9Smrg		switch (*cp) {
4000d4fba8b9Smrg		case 'I':
4001d4fba8b9Smrg		    xtermLoadIcon(xw, (char *) ++cp);
4002d4fba8b9Smrg		    return;
4003d4fba8b9Smrg		case 'l':
4004d4fba8b9Smrg		    ChangeTitle(xw, (char *) ++cp);
4005d4fba8b9Smrg		    return;
4006d4fba8b9Smrg		case 'L':
4007d4fba8b9Smrg		    ChangeIconName(xw, (char *) ++cp);
4008d4fba8b9Smrg		    return;
4009d4fba8b9Smrg		}
4010d522f475Smrg	    }
4011d522f475Smrg	    /* FALLTHRU */
4012d522f475Smrg	case 1:
4013d522f475Smrg	    if (*cp != ';') {
4014cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
4015cd3331d0Smrg		       (int) (cp - oscbuf)));
4016d522f475Smrg		return;
4017d522f475Smrg	    }
4018d522f475Smrg	    state = 2;
4019d522f475Smrg	    break;
4020d522f475Smrg	case 2:
4021d522f475Smrg	    buf = (char *) cp;
4022d522f475Smrg	    state = 3;
4023d522f475Smrg	    /* FALLTHRU */
4024d522f475Smrg	default:
4025cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4026d522f475Smrg		switch (mode) {
4027d522f475Smrg		case 0:
4028d522f475Smrg		case 1:
4029d522f475Smrg		case 2:
4030d522f475Smrg		    break;
4031d522f475Smrg		default:
4032d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
4033d522f475Smrg			   CharOf(*cp),
4034cd3331d0Smrg			   (int) (cp - oscbuf)));
4035d522f475Smrg		    return;
4036d522f475Smrg		}
4037d522f475Smrg	    }
4038d522f475Smrg	}
4039d522f475Smrg    }
4040cd3331d0Smrg
40413367019cSmrg    /*
40423367019cSmrg     * Check if the palette changed and there are no more immediate changes
40433367019cSmrg     * that could be deferred to the next repaint.
40443367019cSmrg     */
4045dfb07bc7Smrg    if (xw->work.palette_changed) {
40463367019cSmrg	switch (mode) {
4047d4fba8b9Smrg	case 03:		/* change X property */
40483367019cSmrg	case 30:		/* Konsole (unused) */
40493367019cSmrg	case 31:		/* Konsole (unused) */
40503367019cSmrg	case 50:		/* font operations */
40513367019cSmrg	case 51:		/* Emacs (unused) */
40523367019cSmrg#if OPT_PASTE64
40533367019cSmrg	case 52:		/* selection data */
40543367019cSmrg#endif
40553367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
4056dfb07bc7Smrg	    xw->work.palette_changed = False;
40573367019cSmrg	    xtermRepaint(xw);
40583367019cSmrg	    break;
4059d4fba8b9Smrg	default:
4060d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
4061d4fba8b9Smrg	    break;
40623367019cSmrg	}
40633367019cSmrg    }
40643367019cSmrg
4065cd3331d0Smrg    /*
4066cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
4067cd3331d0Smrg     * a special case.
4068cd3331d0Smrg     */
4069cd3331d0Smrg    switch (mode) {
407094644356Smrg    case 50:
4071cd3331d0Smrg#if OPT_ISO_COLORS
4072cd3331d0Smrg    case OSC_Reset(4):
4073cd3331d0Smrg    case OSC_Reset(5):
4074fa3f02f3Smrg	need_data = False;
4075fa3f02f3Smrg	optional_data = True;
4076fa3f02f3Smrg	break;
4077cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4078cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4079cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4080cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4081cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4082cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4083cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4084cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4085cd3331d0Smrg#endif
4086cd3331d0Smrg#if OPT_TEK4014
4087cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4088cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4089cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4090cd3331d0Smrg#endif
4091cd3331d0Smrg	need_data = False;
4092cd3331d0Smrg	break;
4093cd3331d0Smrg#endif
4094cd3331d0Smrg    default:
4095cd3331d0Smrg	break;
4096cd3331d0Smrg    }
4097cd3331d0Smrg
4098cd3331d0Smrg    /*
4099cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
4100cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
4101cd3331d0Smrg     */
4102cd3331d0Smrg    if (IsEmpty(buf)) {
4103cd3331d0Smrg	if (need_data) {
4104cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
4105cd3331d0Smrg	    return;
4106cd3331d0Smrg	}
4107cd3331d0Smrg	temp[0] = '\0';
4108cd3331d0Smrg	buf = temp;
4109fa3f02f3Smrg    } else if (!need_data && !optional_data) {
4110fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
4111d522f475Smrg	return;
41120d92cbfdSchristos    }
4113d522f475Smrg
4114d522f475Smrg    switch (mode) {
4115d522f475Smrg    case 0:			/* new icon name and title */
4116b7c89284Ssnj	ChangeIconName(xw, buf);
4117b7c89284Ssnj	ChangeTitle(xw, buf);
4118d522f475Smrg	break;
4119d522f475Smrg
4120d522f475Smrg    case 1:			/* new icon name only */
4121b7c89284Ssnj	ChangeIconName(xw, buf);
4122d522f475Smrg	break;
4123d522f475Smrg
4124d522f475Smrg    case 2:			/* new title only */
4125b7c89284Ssnj	ChangeTitle(xw, buf);
4126d522f475Smrg	break;
4127d522f475Smrg
412822d8e007Schristos#ifdef notdef
4129d522f475Smrg    case 3:			/* change X property */
4130cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
41310d92cbfdSchristos	    ChangeXprop(buf);
4132d522f475Smrg	break;
413322d8e007Schristos#endif
4134d522f475Smrg#if OPT_ISO_COLORS
4135cd3331d0Smrg    case 5:
4136cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4137cd3331d0Smrg	/* FALLTHRU */
4138d522f475Smrg    case 4:
4139d4fba8b9Smrg	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4140dfb07bc7Smrg	    xw->work.palette_changed = True;
4141cd3331d0Smrg	break;
414294644356Smrg    case 6:
414394644356Smrg	/* FALLTHRU */
414494644356Smrg    case OSC_Reset(6):
414594644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
414694644356Smrg	while (*buf != '\0') {
414794644356Smrg	    long which = 0;
414894644356Smrg	    long value = 0;
414994644356Smrg	    char *next;
415094644356Smrg	    if (*buf == ';') {
415194644356Smrg		++buf;
415294644356Smrg	    } else {
415394644356Smrg		which = strtol(buf, &next, 10);
4154037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
415594644356Smrg		    break;
415694644356Smrg		buf = next;
415794644356Smrg		if (*buf == ';')
415894644356Smrg		    ++buf;
415994644356Smrg	    }
416094644356Smrg	    if (*buf == ';') {
416194644356Smrg		++buf;
416294644356Smrg	    } else {
416394644356Smrg		value = strtol(buf, &next, 10);
4164dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
416594644356Smrg		    break;
416694644356Smrg		buf = next;
416794644356Smrg		if (*buf == ';')
416894644356Smrg		    ++buf;
416994644356Smrg	    }
417094644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
417194644356Smrg	    switch (which) {
417294644356Smrg	    case 0:
417394644356Smrg		screen->colorBDMode = (value != 0);
417494644356Smrg		break;
417594644356Smrg	    case 1:
417694644356Smrg		screen->colorULMode = (value != 0);
417794644356Smrg		break;
417894644356Smrg	    case 2:
417994644356Smrg		screen->colorBLMode = (value != 0);
418094644356Smrg		break;
418194644356Smrg	    case 3:
418294644356Smrg		screen->colorRVMode = (value != 0);
418394644356Smrg		break;
418494644356Smrg#if OPT_WIDE_ATTRS
418594644356Smrg	    case 4:
418694644356Smrg		screen->colorITMode = (value != 0);
418794644356Smrg		break;
418894644356Smrg#endif
418994644356Smrg	    default:
419094644356Smrg		TRACE(("...unknown colorXXMode\n"));
419194644356Smrg		break;
419294644356Smrg	    }
419394644356Smrg	}
419494644356Smrg	break;
4195cd3331d0Smrg    case OSC_Reset(5):
4196cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4197cd3331d0Smrg	/* FALLTHRU */
4198cd3331d0Smrg    case OSC_Reset(4):
4199cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4200dfb07bc7Smrg	    xw->work.palette_changed = True;
4201d522f475Smrg	break;
4202d522f475Smrg#endif
4203d522f475Smrg    case OSC_TEXT_FG:
4204d522f475Smrg    case OSC_TEXT_BG:
4205d522f475Smrg    case OSC_TEXT_CURSOR:
4206d522f475Smrg    case OSC_MOUSE_FG:
4207d522f475Smrg    case OSC_MOUSE_BG:
4208d522f475Smrg#if OPT_HIGHLIGHT_COLOR
4209d522f475Smrg    case OSC_HIGHLIGHT_BG:
4210cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
4211d522f475Smrg#endif
4212d522f475Smrg#if OPT_TEK4014
4213d522f475Smrg    case OSC_TEK_FG:
4214d522f475Smrg    case OSC_TEK_BG:
4215d522f475Smrg    case OSC_TEK_CURSOR:
4216d522f475Smrg#endif
4217cd3331d0Smrg	if (xw->misc.dynamicColors) {
4218d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
4219cd3331d0Smrg	}
4220cd3331d0Smrg	break;
4221cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4222cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4223cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4224cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4225cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4226cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4227cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4228cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4229cd3331d0Smrg#endif
4230cd3331d0Smrg#if OPT_TEK4014
4231cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4232cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4233cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4234cd3331d0Smrg#endif
4235cd3331d0Smrg	if (xw->misc.dynamicColors) {
4236cd3331d0Smrg	    ResetColorsRequest(xw, mode);
4237cd3331d0Smrg	}
4238d522f475Smrg	break;
4239d522f475Smrg
42408f44fb3bSmrg    case 22:
42418f44fb3bSmrg	xtermSetupPointer(xw, buf);
42428f44fb3bSmrg	break;
42438f44fb3bSmrg
4244d522f475Smrg    case 30:
4245d522f475Smrg    case 31:
4246d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
4247d522f475Smrg	break;
4248d522f475Smrg
4249d522f475Smrg#ifdef ALLOWLOGGING
4250d522f475Smrg    case 46:			/* new log file */
4251d522f475Smrg#ifdef ALLOWLOGFILECHANGES
4252d522f475Smrg	/*
4253d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
4254d522f475Smrg	 * arbitrary files accessible to the person running xterm.
4255d522f475Smrg	 */
4256037a25ddSmrg	if (strcmp(buf, "?")) {
4257037a25ddSmrg	    char *bp;
4258dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
4259d4fba8b9Smrg		free(screen->logfile);
4260037a25ddSmrg		screen->logfile = bp;
4261037a25ddSmrg		break;
4262037a25ddSmrg	    }
4263d522f475Smrg	}
4264d522f475Smrg#endif
4265cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4266cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4267d522f475Smrg	break;
4268d522f475Smrg#endif /* ALLOWLOGGING */
4269d522f475Smrg
4270d522f475Smrg    case 50:
4271d522f475Smrg#if OPT_SHIFT_FONTS
4272cd3331d0Smrg	if (*buf == '?') {
4273cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
4274cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
4275cd3331d0Smrg	    ChangeFontRequest(xw, buf);
4276d522f475Smrg	}
4277d522f475Smrg#endif /* OPT_SHIFT_FONTS */
4278d522f475Smrg	break;
4279d522f475Smrg    case 51:
4280d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
4281d522f475Smrg	break;
4282d522f475Smrg
4283d522f475Smrg#if OPT_PASTE64
4284d522f475Smrg    case 52:
4285cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
4286d522f475Smrg	break;
4287d522f475Smrg#endif
4288d522f475Smrg	/*
4289d522f475Smrg	 * One could write code to send back the display and host names,
4290d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
4291d522f475Smrg	 */
4292cd3331d0Smrg    default:
4293cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
4294cd3331d0Smrg	break;
4295d522f475Smrg    }
4296d522f475Smrg    unparse_end(xw);
4297d522f475Smrg}
4298d522f475Smrg
4299d522f475Smrg/*
4300d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
4301d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
4302d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4303d522f475Smrg * "real" terminals accept commas in the string definitions).
4304d522f475Smrg */
4305d522f475Smrgstatic int
4306cd3331d0Smrgudk_value(const char **cp)
4307d522f475Smrg{
4308cd3331d0Smrg    int result = -1;
4309d522f475Smrg
4310d522f475Smrg    for (;;) {
4311037a25ddSmrg	int c;
4312037a25ddSmrg
4313d522f475Smrg	if ((c = **cp) != '\0')
4314d522f475Smrg	    *cp = *cp + 1;
4315d522f475Smrg	if (c == ';' || c == '\0')
4316cd3331d0Smrg	    break;
4317cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
4318cd3331d0Smrg	    break;
4319d522f475Smrg    }
4320cd3331d0Smrg
4321cd3331d0Smrg    return result;
4322d522f475Smrg}
4323d522f475Smrg
4324d522f475Smrgvoid
43259a64e1c5Smrgreset_decudk(XtermWidget xw)
4326d522f475Smrg{
4327d522f475Smrg    int n;
4328d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
4329d4fba8b9Smrg	FreeAndNull(xw->work.user_keys[n].str);
4330d4fba8b9Smrg	xw->work.user_keys[n].len = 0;
4331d522f475Smrg    }
4332d522f475Smrg}
4333d522f475Smrg
4334d522f475Smrg/*
4335d522f475Smrg * Parse the data for DECUDK (user-defined keys).
4336d522f475Smrg */
4337d522f475Smrgstatic void
43389a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
4339d522f475Smrg{
4340d522f475Smrg    while (*cp) {
4341cd3331d0Smrg	const char *base = cp;
4342d4fba8b9Smrg	char *str = malloc(strlen(cp) + 3);
4343d522f475Smrg	unsigned key = 0;
4344d522f475Smrg	int len = 0;
4345d522f475Smrg
434694644356Smrg	if (str == NULL)
434794644356Smrg	    break;
434894644356Smrg
4349d522f475Smrg	while (isdigit(CharOf(*cp)))
43500d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
4351037a25ddSmrg
4352d522f475Smrg	if (*cp == '/') {
4353037a25ddSmrg	    int lo, hi;
4354037a25ddSmrg
4355d522f475Smrg	    cp++;
4356d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
4357d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
43580d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
4359d522f475Smrg	    }
4360d522f475Smrg	}
4361d522f475Smrg	if (len > 0 && key < MAX_UDK) {
43623367019cSmrg	    str[len] = '\0';
4363d4fba8b9Smrg	    free(xw->work.user_keys[key].str);
43649a64e1c5Smrg	    xw->work.user_keys[key].str = str;
43659a64e1c5Smrg	    xw->work.user_keys[key].len = len;
4366d4fba8b9Smrg	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4367d522f475Smrg	} else {
4368d522f475Smrg	    free(str);
4369d522f475Smrg	}
4370d522f475Smrg	if (*cp == ';')
4371d522f475Smrg	    cp++;
4372d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
4373d522f475Smrg	    break;
4374d522f475Smrg    }
4375d522f475Smrg}
4376d522f475Smrg
4377fa3f02f3Smrg/*
4378fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
4379fa3f02f3Smrg * interspersing with control characters, but have the string already.
4380fa3f02f3Smrg */
4381fa3f02f3Smrgstatic void
4382fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
4383fa3f02f3Smrg{
4384fa3f02f3Smrg    const char *cp = *string;
4385fa3f02f3Smrg    ParmType nparam = 0;
4386fa3f02f3Smrg    int last_empty = 1;
4387fa3f02f3Smrg
4388fa3f02f3Smrg    memset(params, 0, sizeof(*params));
4389fa3f02f3Smrg    while (*cp != '\0') {
4390fa3f02f3Smrg	Char ch = CharOf(*cp++);
4391fa3f02f3Smrg
4392fa3f02f3Smrg	if (isdigit(ch)) {
4393fa3f02f3Smrg	    last_empty = 0;
4394fa3f02f3Smrg	    if (nparam < NPARAM) {
4395fa3f02f3Smrg		params->a_param[nparam] =
4396fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
4397fa3f02f3Smrg				+ (ch - '0'));
4398fa3f02f3Smrg	    }
4399fa3f02f3Smrg	} else if (ch == ';') {
4400fa3f02f3Smrg	    last_empty = 1;
4401fa3f02f3Smrg	    nparam++;
4402fa3f02f3Smrg	} else if (ch < 32) {
4403fa3f02f3Smrg	    /* EMPTY */ ;
4404fa3f02f3Smrg	} else {
4405fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
4406fa3f02f3Smrg	    params->a_final = ch;
4407fa3f02f3Smrg	    break;
4408fa3f02f3Smrg	}
4409fa3f02f3Smrg    }
4410fa3f02f3Smrg
4411fa3f02f3Smrg    *string = cp;
4412fa3f02f3Smrg    if (!last_empty)
4413fa3f02f3Smrg	nparam++;
4414fa3f02f3Smrg    if (nparam > NPARAM)
4415fa3f02f3Smrg	params->a_nparam = NPARAM;
4416fa3f02f3Smrg    else
4417fa3f02f3Smrg	params->a_nparam = nparam;
4418fa3f02f3Smrg}
4419fa3f02f3Smrg
4420d522f475Smrg#if OPT_TRACE
4421d522f475Smrg#define SOFT_WIDE 10
4422d522f475Smrg#define SOFT_HIGH 20
4423d522f475Smrg
4424d522f475Smrgstatic void
4425fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
4426d522f475Smrg{
4427d522f475Smrg    char DscsName[8];
4428d522f475Smrg    int len;
4429d522f475Smrg    int Pfn = params->a_param[0];
4430d522f475Smrg    int Pcn = params->a_param[1];
4431d522f475Smrg    int Pe = params->a_param[2];
4432d522f475Smrg    int Pcmw = params->a_param[3];
4433d522f475Smrg    int Pw = params->a_param[4];
4434d522f475Smrg    int Pt = params->a_param[5];
4435d522f475Smrg    int Pcmh = params->a_param[6];
4436d522f475Smrg    int Pcss = params->a_param[7];
4437d522f475Smrg
4438d522f475Smrg    int start_char = Pcn + 0x20;
4439d522f475Smrg    int char_wide = ((Pcmw == 0)
4440d522f475Smrg		     ? (Pcss ? 6 : 10)
4441d522f475Smrg		     : (Pcmw > 4
4442d522f475Smrg			? Pcmw
4443d522f475Smrg			: (Pcmw + 3)));
4444d522f475Smrg    int char_high = ((Pcmh == 0)
44453367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
4446d522f475Smrg			? 10
4447d522f475Smrg			: 20)
4448d522f475Smrg		     : Pcmh);
4449d522f475Smrg    Char ch;
4450d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
4451d522f475Smrg    Bool first = True;
4452d522f475Smrg    Bool prior = False;
4453d522f475Smrg    int row = 0, col = 0;
4454d522f475Smrg
4455d522f475Smrg    TRACE(("Parsing DECDLD\n"));
4456d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
4457d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
4458d522f475Smrg    TRACE(("  erase control %d\n", Pe));
4459d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
4460d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
4461d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
4462d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
4463d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
4464d522f475Smrg
4465d522f475Smrg    if (Pfn > 1
4466d522f475Smrg	|| Pcn > 95
4467d522f475Smrg	|| Pe > 2
4468d522f475Smrg	|| Pcmw > 10
4469d522f475Smrg	|| Pcmw == 1
4470d522f475Smrg	|| Pt > 2
4471d522f475Smrg	|| Pcmh > 20
4472d522f475Smrg	|| Pcss > 1
4473d522f475Smrg	|| char_wide > SOFT_WIDE
4474d522f475Smrg	|| char_high > SOFT_HIGH) {
4475d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4476d522f475Smrg	return;
4477d522f475Smrg    }
4478d522f475Smrg
4479d522f475Smrg    len = 0;
4480d522f475Smrg    while (*string != '\0') {
4481d522f475Smrg	ch = CharOf(*string++);
4482d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4483d522f475Smrg	    if (len < 2)
4484b7c89284Ssnj		DscsName[len++] = (char) ch;
4485d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4486b7c89284Ssnj	    DscsName[len++] = (char) ch;
4487d522f475Smrg	    break;
4488d522f475Smrg	}
4489d522f475Smrg    }
4490d522f475Smrg    DscsName[len] = 0;
4491d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4492d522f475Smrg
4493d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4494d522f475Smrg    while (*string != '\0') {
4495d522f475Smrg	if (first) {
4496d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4497d522f475Smrg	    if (prior) {
4498d522f475Smrg		for (row = 0; row < char_high; ++row) {
4499d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4500d522f475Smrg		}
4501d522f475Smrg	    }
4502d522f475Smrg	    prior = False;
4503d522f475Smrg	    first = False;
4504d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4505d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4506d522f475Smrg		    bits[row][col] = '.';
4507d522f475Smrg		}
4508d522f475Smrg	    }
4509d522f475Smrg	    row = col = 0;
4510d522f475Smrg	}
4511d522f475Smrg	ch = CharOf(*string++);
4512d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4513d522f475Smrg	    int n;
4514d522f475Smrg
4515b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4516d522f475Smrg	    for (n = 0; n < 6; ++n) {
4517b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4518d522f475Smrg	    }
4519d522f475Smrg	    col += 1;
4520d522f475Smrg	    prior = True;
4521d522f475Smrg	} else if (ch == '/') {
4522d522f475Smrg	    row += 6;
4523d522f475Smrg	    col = 0;
4524d522f475Smrg	} else if (ch == ';') {
4525d522f475Smrg	    first = True;
4526d522f475Smrg	    ++start_char;
4527d522f475Smrg	}
4528d522f475Smrg    }
4529d522f475Smrg}
4530d522f475Smrg#else
4531d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4532d522f475Smrg#endif
4533d522f475Smrg
4534d4fba8b9Smrg#if OPT_DEC_RECTOPS
4535d4fba8b9Smrgstatic const char *
4536d4fba8b9Smrgskip_params(const char *cp)
4537d4fba8b9Smrg{
4538d4fba8b9Smrg    while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4539d4fba8b9Smrg	++cp;
4540d4fba8b9Smrg    return cp;
4541d4fba8b9Smrg}
4542d4fba8b9Smrg
4543d4fba8b9Smrgstatic int
4544d4fba8b9Smrgparse_int_param(const char **cp)
4545d4fba8b9Smrg{
4546d4fba8b9Smrg    int result = 0;
4547d4fba8b9Smrg    const char *s = *cp;
4548d4fba8b9Smrg    while (*s != '\0') {
4549d4fba8b9Smrg	if (*s == ';') {
4550d4fba8b9Smrg	    ++s;
4551d4fba8b9Smrg	    break;
4552d4fba8b9Smrg	} else if (*s >= '0' && *s <= '9') {
4553d4fba8b9Smrg	    result = (result * 10) + (*s++ - '0');
4554d4fba8b9Smrg	} else {
4555d4fba8b9Smrg	    s += strlen(s);
4556d4fba8b9Smrg	}
4557d4fba8b9Smrg    }
4558d4fba8b9Smrg    TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
4559d4fba8b9Smrg    *cp = s;
4560d4fba8b9Smrg    return result;
4561d4fba8b9Smrg}
4562d4fba8b9Smrg
4563d4fba8b9Smrgstatic int
4564d4fba8b9Smrgparse_chr_param(const char **cp)
4565d4fba8b9Smrg{
4566d4fba8b9Smrg    int result = 0;
4567d4fba8b9Smrg    const char *s = *cp;
4568d4fba8b9Smrg    if (*s != '\0') {
4569d4fba8b9Smrg	if ((result = CharOf(*s++)) != 0) {
4570d4fba8b9Smrg	    if (*s == ';') {
4571d4fba8b9Smrg		++s;
4572d4fba8b9Smrg	    } else if (*s != '\0') {
4573d4fba8b9Smrg		result = 0;
4574d4fba8b9Smrg	    }
4575d4fba8b9Smrg	}
4576d4fba8b9Smrg    }
4577d4fba8b9Smrg    TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
4578d4fba8b9Smrg    *cp = s;
4579d4fba8b9Smrg    return result;
4580d4fba8b9Smrg}
4581d4fba8b9Smrg
4582d4fba8b9Smrgstatic void
4583d4fba8b9Smrgrestore_DECCIR(XtermWidget xw, const char *cp)
4584d4fba8b9Smrg{
4585d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
4586d4fba8b9Smrg    int value;
4587d4fba8b9Smrg
4588d4fba8b9Smrg    /* row */
4589d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4590d4fba8b9Smrg	return;
4591d4fba8b9Smrg    screen->cur_row = (value - 1);
4592d4fba8b9Smrg
4593d4fba8b9Smrg    /* column */
4594d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4595d4fba8b9Smrg	return;
4596d4fba8b9Smrg    screen->cur_col = (value - 1);
4597d4fba8b9Smrg
4598d4fba8b9Smrg    /* page */
4599d4fba8b9Smrg    if (parse_int_param(&cp) != 1)
4600d4fba8b9Smrg	return;
4601d4fba8b9Smrg
4602d4fba8b9Smrg    /* rendition */
4603d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4604d4fba8b9Smrg	return;
4605d4fba8b9Smrg    UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4606d4fba8b9Smrg    xw->flags |= (value & 8) ? INVERSE : 0;
4607d4fba8b9Smrg    xw->flags |= (value & 4) ? BLINK : 0;
4608d4fba8b9Smrg    xw->flags |= (value & 2) ? UNDERLINE : 0;
4609d4fba8b9Smrg    xw->flags |= (value & 1) ? BOLD : 0;
4610d4fba8b9Smrg
4611d4fba8b9Smrg    /* attributes */
4612d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4613d4fba8b9Smrg	return;
4614d4fba8b9Smrg    screen->protected_mode &= ~DEC_PROTECT;
4615d4fba8b9Smrg    screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4616d4fba8b9Smrg
4617d4fba8b9Smrg    /* flags */
4618d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4619d4fba8b9Smrg	return;
4620d4fba8b9Smrg    screen->do_wrap = (value & 8) ? True : False;
4621d4fba8b9Smrg    screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4622d4fba8b9Smrg    UIntClr(xw->flags, ORIGIN);
4623d4fba8b9Smrg    xw->flags |= (value & 1) ? ORIGIN : 0;
4624d4fba8b9Smrg
4625d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4626d4fba8b9Smrg	return;
4627d4fba8b9Smrg    screen->curgl = (Char) value;
4628d4fba8b9Smrg
4629d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4630d4fba8b9Smrg	return;
4631d4fba8b9Smrg    screen->curgr = (Char) value;
4632d4fba8b9Smrg
4633d4fba8b9Smrg    /* character-set size */
4634d4fba8b9Smrg    if (parse_chr_param(&cp) != 0x4f)	/* works for xterm */
4635d4fba8b9Smrg	return;
4636d4fba8b9Smrg
4637d4fba8b9Smrg    /* SCS designators */
4638d4fba8b9Smrg    for (value = 0; value < NUM_GSETS; ++value) {
4639d4fba8b9Smrg	if (*cp == '%') {
4640d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '%', *++cp);
4641d4fba8b9Smrg	} else if (*cp != '\0') {
4642d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4643d4fba8b9Smrg	} else {
4644d4fba8b9Smrg	    return;
4645d4fba8b9Smrg	}
4646d4fba8b9Smrg	cp++;
4647d4fba8b9Smrg    }
4648d4fba8b9Smrg
4649d4fba8b9Smrg    TRACE(("...done DECCIR\n"));
4650d4fba8b9Smrg}
4651d4fba8b9Smrg
4652d4fba8b9Smrgstatic void
4653d4fba8b9Smrgrestore_DECTABSR(XtermWidget xw, const char *cp)
4654d4fba8b9Smrg{
4655d4fba8b9Smrg    int stop = 0;
4656d4fba8b9Smrg    Bool fail = False;
4657d4fba8b9Smrg
4658d4fba8b9Smrg    TabZonk(xw->tabs);
4659d4fba8b9Smrg    while (*cp != '\0' && !fail) {
4660d4fba8b9Smrg	if ((*cp) >= '0' && (*cp) <= '9') {
4661d4fba8b9Smrg	    stop = (stop * 10) + ((*cp) - '0');
4662d4fba8b9Smrg	} else if (*cp == '/') {
4663d4fba8b9Smrg	    --stop;
4664d4fba8b9Smrg	    if (OkTAB(stop)) {
4665d4fba8b9Smrg		TabSet(xw->tabs, stop);
4666d4fba8b9Smrg		stop = 0;
4667d4fba8b9Smrg	    } else {
4668d4fba8b9Smrg		fail = True;
4669d4fba8b9Smrg	    }
4670d4fba8b9Smrg	} else {
4671d4fba8b9Smrg	    fail = True;
4672d4fba8b9Smrg	}
4673d4fba8b9Smrg	++cp;
4674d4fba8b9Smrg    }
4675d4fba8b9Smrg    --stop;
4676d4fba8b9Smrg    if (OkTAB(stop))
4677d4fba8b9Smrg	TabSet(xw->tabs, stop);
4678d4fba8b9Smrg
4679d4fba8b9Smrg    TRACE(("...done DECTABSR\n"));
4680d4fba8b9Smrg}
4681d4fba8b9Smrg#endif
4682d4fba8b9Smrg
4683d522f475Smrgvoid
4684fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4685d522f475Smrg{
4686cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4687d522f475Smrg    char reply[BUFSIZ];
4688cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4689d522f475Smrg    Bool okay;
4690d522f475Smrg    ANSI params;
4691d4fba8b9Smrg#if OPT_DEC_RECTOPS
4692d4fba8b9Smrg    char psarg = '0';
4693d4fba8b9Smrg#endif
4694d522f475Smrg
4695cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4696d522f475Smrg
4697d522f475Smrg    if (dcslen != strlen(cp))
4698d522f475Smrg	/* shouldn't have nulls in the string */
4699d522f475Smrg	return;
4700d522f475Smrg
4701d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4702d522f475Smrg    case '$':			/* DECRQSS */
4703d522f475Smrg	okay = True;
4704d522f475Smrg
4705d522f475Smrg	cp++;
4706d4fba8b9Smrg	if (*cp == 'q') {
4707d4fba8b9Smrg	    *reply = '\0';
4708d4fba8b9Smrg	    cp++;
4709d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4710d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCA\n"));
4711d522f475Smrg		sprintf(reply, "%d%s",
4712d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4713d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4714d522f475Smrg			cp);
4715d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
47163367019cSmrg		if (screen->vtXX_level < 2) {
47173367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
47183367019cSmrg		    break;
47193367019cSmrg		}
4720d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCL\n"));
4721d522f475Smrg		sprintf(reply, "%d%s%s",
4722d522f475Smrg			(screen->vtXX_level ?
4723d522f475Smrg			 screen->vtXX_level : 1) + 60,
4724d522f475Smrg			(screen->vtXX_level >= 2)
4725d522f475Smrg			? (screen->control_eight_bits
4726d522f475Smrg			   ? ";0" : ";1")
4727d522f475Smrg			: "",
4728d522f475Smrg			cp);
4729d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4730d4fba8b9Smrg		TRACE(("DECRQSS -> DECSTBM\n"));
4731d522f475Smrg		sprintf(reply, "%d;%dr",
4732d522f475Smrg			screen->top_marg + 1,
4733d522f475Smrg			screen->bot_marg + 1);
47343367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
47353367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
4736d4fba8b9Smrg		    TRACE(("DECRQSS -> DECSLRM\n"));
47373367019cSmrg		    sprintf(reply, "%d;%ds",
47383367019cSmrg			    screen->lft_marg + 1,
47393367019cSmrg			    screen->rgt_marg + 1);
4740037a25ddSmrg		} else {
4741037a25ddSmrg		    okay = False;
47423367019cSmrg		}
4743d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4744d4fba8b9Smrg		TRACE(("DECRQSS -> SGR\n"));
4745d4fba8b9Smrg		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4746d522f475Smrg		strcat(reply, "m");
4747712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
47483367019cSmrg		int code = STEADY_BLOCK;
47493367019cSmrg		if (isCursorUnderline(screen))
47503367019cSmrg		    code = STEADY_UNDERLINE;
47513367019cSmrg		else if (isCursorBar(screen))
47523367019cSmrg		    code = STEADY_BAR;
47533367019cSmrg#if OPT_BLINK_CURS
475494644356Smrg		if (screen->cursor_blink_esc != 0)
47553367019cSmrg		    code -= 1;
47563367019cSmrg#endif
4757d4fba8b9Smrg		TRACE(("reply DECSCUSR\n"));
47583367019cSmrg		sprintf(reply, "%d%s", code, cp);
4759d4fba8b9Smrg	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4760d4fba8b9Smrg		sprintf(reply, "%d%s",
4761d4fba8b9Smrg			((screen->max_row > 24) ? screen->max_row : 24),
4762d4fba8b9Smrg			cp);
4763d4fba8b9Smrg		TRACE(("reply DECSLPP\n"));
4764d4fba8b9Smrg	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4765d4fba8b9Smrg		TRACE(("reply DECSCPP\n"));
4766d4fba8b9Smrg		sprintf(reply, "%d%s",
4767d4fba8b9Smrg			((xw->flags & IN132COLUMNS) ? 132 : 80),
4768d4fba8b9Smrg			cp);
4769d4fba8b9Smrg	    } else if (!strcmp(cp, "*|")) {	/* DECSNLS */
4770d4fba8b9Smrg		TRACE(("reply DECSNLS\n"));
4771d4fba8b9Smrg		sprintf(reply, "%d%s",
4772d4fba8b9Smrg			screen->max_row + 1,
4773d4fba8b9Smrg			cp);
47740d92cbfdSchristos	    } else {
4775d4fba8b9Smrg		okay = False;
477622d8e007Schristos	    }
4777d4fba8b9Smrg
4778d4fba8b9Smrg	    unparseputc1(xw, ANSI_DCS);
4779d4fba8b9Smrg	    unparseputc(xw, okay ? '1' : '0');
4780d4fba8b9Smrg	    unparseputc(xw, '$');
4781d4fba8b9Smrg	    unparseputc(xw, 'r');
4782d4fba8b9Smrg	    cp = reply;
4783d4fba8b9Smrg	    unparseputs(xw, cp);
4784d4fba8b9Smrg	    unparseputc1(xw, ANSI_ST);
4785d522f475Smrg	} else {
4786d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4787d522f475Smrg	}
4788d522f475Smrg	break;
4789d522f475Smrg    case '+':
4790d522f475Smrg	cp++;
4791cd3331d0Smrg	switch (*cp) {
4792d4fba8b9Smrg#if OPT_TCAP_QUERY
4793cd3331d0Smrg	case 'p':
4794cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4795cd3331d0Smrg		set_termcap(xw, cp + 1);
4796cd3331d0Smrg	    }
4797cd3331d0Smrg	    break;
4798cd3331d0Smrg	case 'q':
4799cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4800cd3331d0Smrg		Bool fkey;
4801cd3331d0Smrg		unsigned state;
4802cd3331d0Smrg		int code;
4803cd3331d0Smrg		const char *tmp;
4804cd3331d0Smrg		const char *parsed = ++cp;
4805d522f475Smrg
4806cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4807d522f475Smrg
4808cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4809b7c89284Ssnj
4810cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4811d522f475Smrg
4812cd3331d0Smrg		unparseputc(xw, '+');
4813cd3331d0Smrg		unparseputc(xw, 'r');
4814d522f475Smrg
4815cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4816cd3331d0Smrg		    if (cp == parsed)
4817cd3331d0Smrg			break;	/* no data found, error */
4818d522f475Smrg
4819cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4820cd3331d0Smrg			unparseputc(xw, *tmp);
4821d522f475Smrg
4822cd3331d0Smrg		    if (code >= 0) {
4823cd3331d0Smrg			unparseputc(xw, '=');
4824cd3331d0Smrg			screen->tc_query_code = code;
4825cd3331d0Smrg			screen->tc_query_fkey = fkey;
4826d522f475Smrg#if OPT_ISO_COLORS
4827cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4828cd3331d0Smrg			 * number of colors) */
4829cd3331d0Smrg			if (code == XK_COLORS) {
4830d4fba8b9Smrg			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
4831d4fba8b9Smrg			} else
4832d4fba8b9Smrg#if OPT_DIRECT_COLOR
4833d4fba8b9Smrg			if (code == XK_RGB) {
4834d4fba8b9Smrg			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
4835d4fba8b9Smrg				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
4836d4fba8b9Smrg				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
4837d4fba8b9Smrg				    unparseputn(xw, xw->rgb_widths[0]);
4838d4fba8b9Smrg				} else {
4839d4fba8b9Smrg				    char temp[1024];
4840d4fba8b9Smrg				    sprintf(temp, "%d/%d/%d",
4841d4fba8b9Smrg					    xw->rgb_widths[0],
4842d4fba8b9Smrg					    xw->rgb_widths[1],
4843d4fba8b9Smrg					    xw->rgb_widths[2]);
4844d4fba8b9Smrg				    unparseputs(xw, temp);
4845d4fba8b9Smrg				}
4846d4fba8b9Smrg			    } else {
4847d4fba8b9Smrg				unparseputs(xw, "-1");
4848d4fba8b9Smrg			    }
4849cd3331d0Smrg			} else
4850d4fba8b9Smrg#endif
4851cd3331d0Smrg#endif
4852cd3331d0Smrg			if (code == XK_TCAPNAME) {
4853c219fbebSmrg			    unparseputs(xw, resource.term_name);
4854cd3331d0Smrg			} else {
4855cd3331d0Smrg			    XKeyEvent event;
4856d4fba8b9Smrg			    memset(&event, 0, sizeof(event));
4857cd3331d0Smrg			    event.state = state;
4858cd3331d0Smrg			    Input(xw, &event, False);
4859cd3331d0Smrg			}
4860cd3331d0Smrg			screen->tc_query_code = -1;
4861cd3331d0Smrg		    } else {
4862cd3331d0Smrg			break;	/* no match found, error */
4863d522f475Smrg		    }
4864d522f475Smrg
4865d522f475Smrg		    cp = parsed;
4866cd3331d0Smrg		    if (*parsed == ';') {
4867cd3331d0Smrg			unparseputc(xw, *parsed++);
4868cd3331d0Smrg			cp = parsed;
4869cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4870cd3331d0Smrg		    }
4871d522f475Smrg		}
4872cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4873d522f475Smrg	    }
4874cd3331d0Smrg	    break;
4875d4fba8b9Smrg#endif
4876d4fba8b9Smrg#if OPT_XRES_QUERY
4877d4fba8b9Smrg	case 'Q':
4878d4fba8b9Smrg	    ++cp;
4879d4fba8b9Smrg	    if (AllowXResOps(xw)) {
4880d4fba8b9Smrg		Boolean first = True;
4881d4fba8b9Smrg		while (*cp != '\0') {
4882d4fba8b9Smrg		    const char *parsed = 0;
4883d4fba8b9Smrg		    const char *tmp;
4884d4fba8b9Smrg		    char *name = x_decode_hex(cp, &parsed);
4885d4fba8b9Smrg		    char *value;
4886d4fba8b9Smrg		    char *result;
4887d4fba8b9Smrg		    if (cp == parsed || name == NULL) {
4888d4fba8b9Smrg			free(name);
4889d4fba8b9Smrg			break;	/* no data found, error */
4890d4fba8b9Smrg		    }
4891d4fba8b9Smrg		    TRACE(("query-feature '%s'\n", name));
4892d4fba8b9Smrg		    if ((value = vt100ResourceToString(xw, name)) != 0) {
4893d4fba8b9Smrg			okay = True;	/* valid */
4894d4fba8b9Smrg		    } else {
4895d4fba8b9Smrg			okay = False;	/* invalid */
4896d4fba8b9Smrg		    }
4897d4fba8b9Smrg		    if (first) {
4898d4fba8b9Smrg			unparseputc1(xw, ANSI_DCS);
4899d4fba8b9Smrg			unparseputc(xw, okay ? '1' : '0');
4900d4fba8b9Smrg			unparseputc(xw, '+');
4901d4fba8b9Smrg			unparseputc(xw, 'R');
4902d4fba8b9Smrg			first = False;
4903d4fba8b9Smrg		    }
4904d4fba8b9Smrg
4905d4fba8b9Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4906d4fba8b9Smrg			unparseputc(xw, *tmp);
4907d4fba8b9Smrg
4908d4fba8b9Smrg		    if (value != 0) {
4909d4fba8b9Smrg			unparseputc1(xw, '=');
4910d4fba8b9Smrg			result = x_encode_hex(value);
4911d4fba8b9Smrg			unparseputs(xw, result);
4912d4fba8b9Smrg		    } else {
4913d4fba8b9Smrg			result = NULL;
4914d4fba8b9Smrg		    }
4915d4fba8b9Smrg
4916d4fba8b9Smrg		    free(name);
4917d4fba8b9Smrg		    free(value);
4918d4fba8b9Smrg		    free(result);
4919d4fba8b9Smrg
4920d4fba8b9Smrg		    cp = parsed;
4921d4fba8b9Smrg		    if (*parsed == ';') {
4922d4fba8b9Smrg			unparseputc(xw, *parsed++);
4923d4fba8b9Smrg			cp = parsed;
4924d4fba8b9Smrg		    }
4925d4fba8b9Smrg		}
4926d4fba8b9Smrg		if (!first)
4927d4fba8b9Smrg		    unparseputc1(xw, ANSI_ST);
4928d4fba8b9Smrg	    }
4929d4fba8b9Smrg	    break;
4930d4fba8b9Smrg#endif
4931d522f475Smrg	}
4932d522f475Smrg	break;
4933d4fba8b9Smrg#if OPT_DEC_RECTOPS
4934d4fba8b9Smrg    case '1':
4935d4fba8b9Smrg	/* FALLTHRU */
4936d4fba8b9Smrg    case '2':
4937d4fba8b9Smrg	if (*skip_params(cp) == '$') {
4938d4fba8b9Smrg	    psarg = *cp++;
4939d4fba8b9Smrg	    if ((*cp++ == '$')
4940d4fba8b9Smrg		&& (*cp++ == 't')
4941d4fba8b9Smrg		&& (screen->vtXX_level >= 3)) {
4942d4fba8b9Smrg		switch (psarg) {
4943d4fba8b9Smrg		case '1':
4944d4fba8b9Smrg		    TRACE(("DECRSPS (DECCIR)\n"));
4945d4fba8b9Smrg		    restore_DECCIR(xw, cp);
4946d4fba8b9Smrg		    break;
4947d4fba8b9Smrg		case '2':
4948d4fba8b9Smrg		    TRACE(("DECRSPS (DECTABSR)\n"));
4949d4fba8b9Smrg		    restore_DECTABSR(xw, cp);
4950d4fba8b9Smrg		    break;
4951d4fba8b9Smrg		}
4952d4fba8b9Smrg	    }
4953d4fba8b9Smrg	    break;
4954d4fba8b9Smrg	}
4955d522f475Smrg#endif
4956d4fba8b9Smrg	/* FALLTHRU */
4957d522f475Smrg    default:
4958d4fba8b9Smrg	if (optRegisGraphics(screen) ||
4959d4fba8b9Smrg	    optSixelGraphics(screen) ||
4960fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
49610d92cbfdSchristos	    parse_ansi_params(&params, &cp);
49620d92cbfdSchristos	    switch (params.a_final) {
4963d4fba8b9Smrg	    case 'p':		/* ReGIS */
49649a64e1c5Smrg#if OPT_REGIS_GRAPHICS
4965d4fba8b9Smrg		if (optRegisGraphics(screen)) {
4966fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4967fa3f02f3Smrg		}
49689a64e1c5Smrg#else
49699a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
49709a64e1c5Smrg#endif
4971fa3f02f3Smrg		break;
4972d4fba8b9Smrg	    case 'q':		/* sixel */
49739a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
4974d4fba8b9Smrg		if (optSixelGraphics(screen)) {
4975037a25ddSmrg		    (void) parse_sixel(xw, &params, cp);
4976fa3f02f3Smrg		}
49779a64e1c5Smrg#else
49789a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
4979fa3f02f3Smrg#endif
49809a64e1c5Smrg		break;
49810d92cbfdSchristos	    case '|':		/* DECUDK */
49829a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
49839a64e1c5Smrg		    if (params.a_param[0] == 0)
49849a64e1c5Smrg			reset_decudk(xw);
49859a64e1c5Smrg		    parse_decudk(xw, cp);
49869a64e1c5Smrg		}
49870d92cbfdSchristos		break;
498894644356Smrg	    case L_CURL:	/* DECDLD */
49899a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
49909a64e1c5Smrg		    parse_decdld(&params, cp);
49919a64e1c5Smrg		}
49920d92cbfdSchristos		break;
49930d92cbfdSchristos	    }
4994d522f475Smrg	}
4995d522f475Smrg	break;
4996d522f475Smrg    }
4997d522f475Smrg    unparse_end(xw);
4998d522f475Smrg}
4999d522f475Smrg
5000cb4a1343Smrg#if OPT_DEC_RECTOPS
5001cb4a1343Smrgenum {
5002cb4a1343Smrg    mdUnknown = 0,
5003cb4a1343Smrg    mdMaybeSet = 1,
5004cb4a1343Smrg    mdMaybeReset = 2,
5005cb4a1343Smrg    mdAlwaysSet = 3,
5006cb4a1343Smrg    mdAlwaysReset = 4
5007cb4a1343Smrg};
5008cb4a1343Smrg
5009cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
50103367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
5011cb4a1343Smrg
5012cb4a1343Smrg/*
5013cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
5014cb4a1343Smrg * 0 - not recognized
5015cb4a1343Smrg * 1 - set
5016cb4a1343Smrg * 2 - reset
5017cb4a1343Smrg * 3 - permanently set
5018cb4a1343Smrg * 4 - permanently reset
5019cb4a1343Smrg * Only one mode can be reported at a time.
5020cb4a1343Smrg */
5021cb4a1343Smrgvoid
5022d4fba8b9Smrgdo_ansi_rqm(XtermWidget xw, int nparams, int *params)
5023cb4a1343Smrg{
5024cb4a1343Smrg    ANSI reply;
5025cb4a1343Smrg    int count = 0;
5026cb4a1343Smrg
5027d4fba8b9Smrg    TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5028cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5029037a25ddSmrg
5030cb4a1343Smrg    if (nparams >= 1) {
5031d4fba8b9Smrg	int result = mdUnknown;
5032037a25ddSmrg
5033d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5034cb4a1343Smrg	switch (params[0]) {
5035cb4a1343Smrg	case 1:		/* GATM */
5036cb4a1343Smrg	    result = mdAlwaysReset;
5037cb4a1343Smrg	    break;
5038cb4a1343Smrg	case 2:
5039cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
5040cb4a1343Smrg	    break;
5041cb4a1343Smrg	case 3:		/* CRM */
5042cb4a1343Smrg	    result = mdMaybeReset;
5043cb4a1343Smrg	    break;
5044cb4a1343Smrg	case 4:
5045cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
5046cb4a1343Smrg	    break;
5047cb4a1343Smrg	case 5:		/* SRTM */
5048cb4a1343Smrg	case 7:		/* VEM */
5049cb4a1343Smrg	case 10:		/* HEM */
5050cb4a1343Smrg	case 11:		/* PUM */
5051cb4a1343Smrg	    result = mdAlwaysReset;
5052cb4a1343Smrg	    break;
5053cb4a1343Smrg	case 12:
5054cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
5055cb4a1343Smrg	    break;
5056cb4a1343Smrg	case 13:		/* FEAM */
5057cb4a1343Smrg	case 14:		/* FETM */
5058cb4a1343Smrg	case 15:		/* MATM */
5059cb4a1343Smrg	case 16:		/* TTM */
5060cb4a1343Smrg	case 17:		/* SATM */
5061cb4a1343Smrg	case 18:		/* TSM */
5062cb4a1343Smrg	case 19:		/* EBM */
5063cb4a1343Smrg	    result = mdAlwaysReset;
5064cb4a1343Smrg	    break;
5065cb4a1343Smrg	case 20:
5066cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
5067cb4a1343Smrg	    break;
5068cb4a1343Smrg	}
5069cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5070cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5071cb4a1343Smrg    }
5072cb4a1343Smrg    reply.a_type = ANSI_CSI;
5073cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5074cb4a1343Smrg    reply.a_inters = '$';
5075cb4a1343Smrg    reply.a_final = 'y';
5076cb4a1343Smrg    unparseseq(xw, &reply);
5077cb4a1343Smrg}
5078cb4a1343Smrg
5079cb4a1343Smrgvoid
5080d4fba8b9Smrgdo_dec_rqm(XtermWidget xw, int nparams, int *params)
5081cb4a1343Smrg{
5082cb4a1343Smrg    ANSI reply;
5083cb4a1343Smrg    int count = 0;
5084cb4a1343Smrg
5085d4fba8b9Smrg    TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5086cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5087037a25ddSmrg
5088cb4a1343Smrg    if (nparams >= 1) {
5089cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
5090d4fba8b9Smrg	int result = mdUnknown;
5091cb4a1343Smrg
5092d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5093d4fba8b9Smrg	switch ((DECSET_codes) params[0]) {
5094fa3f02f3Smrg	case srm_DECCKM:
5095cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5096cb4a1343Smrg	    break;
5097fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
5098cb4a1343Smrg#if OPT_VT52_MODE
50993367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
5100cb4a1343Smrg#else
5101cb4a1343Smrg	    result = mdMaybeSet;
5102cb4a1343Smrg#endif
5103cb4a1343Smrg	    break;
5104fa3f02f3Smrg	case srm_DECCOLM:
5105cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
5106cb4a1343Smrg	    break;
5107fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
5108cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5109cb4a1343Smrg	    break;
5110fa3f02f3Smrg	case srm_DECSCNM:
5111cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5112cb4a1343Smrg	    break;
5113fa3f02f3Smrg	case srm_DECOM:
5114cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
5115cb4a1343Smrg	    break;
5116fa3f02f3Smrg	case srm_DECAWM:
5117cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
5118cb4a1343Smrg	    break;
5119fa3f02f3Smrg	case srm_DECARM:
5120cb4a1343Smrg	    result = mdAlwaysReset;
5121cb4a1343Smrg	    break;
5122fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
5123cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5124cb4a1343Smrg	    break;
5125cb4a1343Smrg#if OPT_TOOLBAR
5126fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
5127cb4a1343Smrg	    result = MdBool(resource.toolBar);
5128cb4a1343Smrg	    break;
5129cb4a1343Smrg#endif
5130cb4a1343Smrg#if OPT_BLINK_CURS
5131d4fba8b9Smrg	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5132d4fba8b9Smrg	    result = MdBool(screen->cursor_blink_esc);
5133d4fba8b9Smrg	    break;
5134d4fba8b9Smrg	case srm_CURSOR_BLINK_OPS:
5135d4fba8b9Smrg	    switch (screen->cursor_blink) {
5136d4fba8b9Smrg	    case cbTrue:
5137d4fba8b9Smrg		result = mdMaybeSet;
5138d4fba8b9Smrg		break;
5139d4fba8b9Smrg	    case cbFalse:
5140d4fba8b9Smrg		result = mdMaybeReset;
5141d4fba8b9Smrg		break;
5142d4fba8b9Smrg	    case cbAlways:
5143d4fba8b9Smrg		result = mdAlwaysSet;
5144d4fba8b9Smrg		break;
5145d4fba8b9Smrg	    case cbLAST:
5146d4fba8b9Smrg		/* FALLTHRU */
5147d4fba8b9Smrg	    case cbNever:
5148d4fba8b9Smrg		result = mdAlwaysReset;
5149d4fba8b9Smrg		break;
5150d4fba8b9Smrg	    }
5151d4fba8b9Smrg	    break;
5152d4fba8b9Smrg	case srm_XOR_CURSOR_BLINKS:
5153d4fba8b9Smrg	    result = (screen->cursor_blink_xor
5154d4fba8b9Smrg		      ? mdAlwaysSet
5155d4fba8b9Smrg		      : mdAlwaysReset);
5156cb4a1343Smrg	    break;
5157cb4a1343Smrg#endif
5158fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
5159712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
5160cb4a1343Smrg	    break;
5161fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
5162712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
5163cb4a1343Smrg	    break;
5164fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5165cb4a1343Smrg	    result = MdBool(screen->cursor_set);
5166cb4a1343Smrg	    break;
5167fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
5168cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5169cb4a1343Smrg	    break;
5170cb4a1343Smrg#if OPT_SHIFT_FONTS
5171fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
5172cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
5173cb4a1343Smrg	    break;
5174cb4a1343Smrg#endif
5175cb4a1343Smrg#if OPT_TEK4014
5176fa3f02f3Smrg	case srm_DECTEK:
5177cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
5178cb4a1343Smrg	    break;
5179cb4a1343Smrg#endif
5180fa3f02f3Smrg	case srm_132COLS:
5181cb4a1343Smrg	    result = MdBool(screen->c132);
5182cb4a1343Smrg	    break;
5183fa3f02f3Smrg	case srm_CURSES_HACK:
5184cb4a1343Smrg	    result = MdBool(screen->curses);
5185cb4a1343Smrg	    break;
5186fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
5187d4fba8b9Smrg	    if (screen->vtXX_level >= 2) {
5188d4fba8b9Smrg		result = MdFlag(xw->flags, NATIONAL);
5189d4fba8b9Smrg	    } else {
5190d4fba8b9Smrg		result = 0;
5191d4fba8b9Smrg	    }
5192cb4a1343Smrg	    break;
5193fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
5194cb4a1343Smrg	    result = MdBool(screen->marginbell);
5195cb4a1343Smrg	    break;
5196d4fba8b9Smrg#if OPT_PRINT_GRAPHICS
5197d4fba8b9Smrg	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5198d4fba8b9Smrg	    result = MdBool(screen->graphics_expanded_print_mode);
5199d4fba8b9Smrg	    break;
5200d4fba8b9Smrg#endif
5201fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
5202d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5203d4fba8b9Smrg		result = MdFlag(xw->flags, REVERSEWRAP);
5204cb4a1343Smrg	    break;
5205d4fba8b9Smrg#if defined(ALLOWLOGGING)
5206fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
5207d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5208d4fba8b9Smrg#if defined(ALLOWLOGFILEONOFF)
5209d4fba8b9Smrg		result = MdBool(screen->logging);
5210d4fba8b9Smrg#else
5211d4fba8b9Smrg		result = ((MdBool(screen->logging) == mdMaybeSet)
5212d4fba8b9Smrg			  ? mdAlwaysSet
5213d4fba8b9Smrg			  : mdAlwaysReset);
5214d4fba8b9Smrg#endif
5215d4fba8b9Smrg	    break;
5216d4fba8b9Smrg#elif OPT_PRINT_GRAPHICS
5217d4fba8b9Smrg	case srm_DECGPBM:	/* Graphics Print Background Mode */
5218d4fba8b9Smrg	    result = MdBool(screen->graphics_print_background_mode);
5219cb4a1343Smrg	    break;
5220cb4a1343Smrg#endif
5221fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5222cb4a1343Smrg	    /* FALLTHRU */
5223fa3f02f3Smrg	case srm_OPT_ALTBUF:
5224cb4a1343Smrg	    result = MdBool(screen->whichBuf);
5225cb4a1343Smrg	    break;
5226d4fba8b9Smrg	case srm_ALTBUF:
5227d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5228d4fba8b9Smrg		result = MdBool(screen->whichBuf);
5229d4fba8b9Smrg	    break;
5230fa3f02f3Smrg	case srm_DECNKM:
5231cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5232cb4a1343Smrg	    break;
5233fa3f02f3Smrg	case srm_DECBKM:
5234cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5235cb4a1343Smrg	    break;
5236fa3f02f3Smrg	case srm_DECLRMM:
5237d4fba8b9Smrg	    if (screen->vtXX_level >= 4) {	/* VT420 */
5238d4fba8b9Smrg		result = MdFlag(xw->flags, LEFT_RIGHT);
5239d4fba8b9Smrg	    } else {
5240d4fba8b9Smrg		result = 0;
5241d4fba8b9Smrg	    }
52423367019cSmrg	    break;
5243fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
5244fa3f02f3Smrg	case srm_DECSDM:
5245fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5246fa3f02f3Smrg	    break;
5247fa3f02f3Smrg#endif
5248fa3f02f3Smrg	case srm_DECNCSM:
5249d4fba8b9Smrg	    if (screen->vtXX_level >= 5) {	/* VT510 */
5250d4fba8b9Smrg		result = MdFlag(xw->flags, NOCLEAR_COLM);
5251d4fba8b9Smrg	    } else {
5252d4fba8b9Smrg		result = 0;
5253d4fba8b9Smrg	    }
52543367019cSmrg	    break;
5255d4fba8b9Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5256cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5257cb4a1343Smrg	    break;
5258fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5259cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5260cb4a1343Smrg	    break;
5261fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
5262cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5263cb4a1343Smrg	    break;
5264fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
5265cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5266cb4a1343Smrg	    break;
5267cb4a1343Smrg#if OPT_FOCUS_EVENT
5268fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
5269cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
5270cb4a1343Smrg	    break;
5271cb4a1343Smrg#endif
5272fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
52733367019cSmrg	    /* FALLTHRU */
5274fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
52753367019cSmrg	    /* FALLTHRU */
5276fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
5277d4fba8b9Smrg	    /* FALLTHRU */
5278d4fba8b9Smrg	case srm_PIXEL_POSITION_MOUSE:
52793367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
52803367019cSmrg	    break;
5281fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
52823367019cSmrg	    result = MdBool(screen->alternateScroll);
5283cb4a1343Smrg	    break;
5284fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
5285cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
5286cb4a1343Smrg	    break;
5287fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5288cb4a1343Smrg	    result = MdBool(screen->scrollkey);
5289cb4a1343Smrg	    break;
5290fa3f02f3Smrg	case srm_EIGHT_BIT_META:
52913367019cSmrg	    result = MdBool(screen->eight_bit_meta);
5292cb4a1343Smrg	    break;
5293cb4a1343Smrg#if OPT_NUM_LOCK
5294fa3f02f3Smrg	case srm_REAL_NUMLOCK:
5295cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
5296cb4a1343Smrg	    break;
5297fa3f02f3Smrg	case srm_META_SENDS_ESC:
5298cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
5299cb4a1343Smrg	    break;
5300cb4a1343Smrg#endif
5301fa3f02f3Smrg	case srm_DELETE_IS_DEL:
5302d4fba8b9Smrg	    result = MdBool(xtermDeleteIsDEL(xw));
5303cb4a1343Smrg	    break;
5304cb4a1343Smrg#if OPT_NUM_LOCK
5305fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
5306cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
5307cb4a1343Smrg	    break;
5308cb4a1343Smrg#endif
5309fa3f02f3Smrg	case srm_KEEP_SELECTION:
5310cb4a1343Smrg	    result = MdBool(screen->keepSelection);
5311cb4a1343Smrg	    break;
5312fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
5313cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
5314cb4a1343Smrg	    break;
5315fa3f02f3Smrg	case srm_BELL_IS_URGENT:
5316cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
5317cb4a1343Smrg	    break;
5318fa3f02f3Smrg	case srm_POP_ON_BELL:
5319cb4a1343Smrg	    result = MdBool(screen->poponbell);
5320cb4a1343Smrg	    break;
5321d4fba8b9Smrg	case srm_KEEP_CLIPBOARD:
5322d4fba8b9Smrg	    result = MdBool(screen->keepClipboard);
5323d4fba8b9Smrg	    break;
5324d4fba8b9Smrg	case srm_ALLOW_ALTBUF:
5325d4fba8b9Smrg	    result = MdBool(xw->misc.titeInhibit);
5326d4fba8b9Smrg	    break;
5327d4fba8b9Smrg	case srm_SAVE_CURSOR:
5328cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
5329cb4a1343Smrg	    break;
5330cb4a1343Smrg#if OPT_TCAP_FKEYS
5331fa3f02f3Smrg	case srm_TCAP_FKEYS:
5332cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5333cb4a1343Smrg	    break;
5334cb4a1343Smrg#endif
5335cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
5336fa3f02f3Smrg	case srm_SUN_FKEYS:
5337cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5338cb4a1343Smrg	    break;
5339cb4a1343Smrg#endif
5340cb4a1343Smrg#if OPT_HP_FUNC_KEYS
5341fa3f02f3Smrg	case srm_HP_FKEYS:
5342cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5343cb4a1343Smrg	    break;
5344cb4a1343Smrg#endif
5345cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
5346fa3f02f3Smrg	case srm_SCO_FKEYS:
5347cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5348cb4a1343Smrg	    break;
5349cb4a1343Smrg#endif
5350fa3f02f3Smrg	case srm_LEGACY_FKEYS:
5351cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5352cb4a1343Smrg	    break;
5353cb4a1343Smrg#if OPT_SUNPC_KBD
5354fa3f02f3Smrg	case srm_VT220_FKEYS:
5355cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5356cb4a1343Smrg	    break;
5357cb4a1343Smrg#endif
5358d4fba8b9Smrg#if OPT_PASTE64 || OPT_READLINE
5359d4fba8b9Smrg	case srm_PASTE_IN_BRACKET:
5360d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5361d4fba8b9Smrg	    break;
5362d4fba8b9Smrg#endif
5363cb4a1343Smrg#if OPT_READLINE
5364fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
5365d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5366cb4a1343Smrg	    break;
5367fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
5368d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5369cb4a1343Smrg	    break;
5370fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
5371d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5372cb4a1343Smrg	    break;
5373fa3f02f3Smrg	case srm_PASTE_QUOTE:
5374d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5375cb4a1343Smrg	    break;
5376fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
5377d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5378cb4a1343Smrg	    break;
5379cb4a1343Smrg#endif /* OPT_READLINE */
5380d4fba8b9Smrg#if OPT_GRAPHICS
53819a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
53829a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
53839a64e1c5Smrg	    break;
53849a64e1c5Smrg#endif
53859a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
53869a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
53879a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
53889a64e1c5Smrg	    break;
53899a64e1c5Smrg#endif
53909a64e1c5Smrg	default:
53919a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
53929a64e1c5Smrg		   params[0]));
5393cb4a1343Smrg	}
5394cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5395cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5396d4fba8b9Smrg	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5397cb4a1343Smrg    }
5398cb4a1343Smrg    reply.a_type = ANSI_CSI;
5399cb4a1343Smrg    reply.a_pintro = '?';
5400cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5401cb4a1343Smrg    reply.a_inters = '$';
5402cb4a1343Smrg    reply.a_final = 'y';
5403cb4a1343Smrg    unparseseq(xw, &reply);
5404cb4a1343Smrg}
5405cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
5406cb4a1343Smrg
5407d522f475Smrgchar *
54089a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
5409d522f475Smrg{
5410d4fba8b9Smrg    char *result = NULL;
5411d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
54129a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
5413d4fba8b9Smrg	result = xw->work.user_keys[keycode].str;
5414d4fba8b9Smrg	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5415d4fba8b9Smrg    } else {
5416d4fba8b9Smrg	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5417d4fba8b9Smrg    }
5418d4fba8b9Smrg    return result;
5419d4fba8b9Smrg}
5420d4fba8b9Smrg
5421d4fba8b9Smrg#if OPT_REPORT_ICONS
5422d4fba8b9Smrgvoid
5423d4fba8b9Smrgreport_icons(const char *fmt, ...)
5424d4fba8b9Smrg{
5425d4fba8b9Smrg    if (resource.reportIcons) {
5426d4fba8b9Smrg	va_list ap;
5427d4fba8b9Smrg	va_start(ap, fmt);
5428d4fba8b9Smrg	vfprintf(stdout, fmt, ap);
5429d4fba8b9Smrg	va_end(ap);
5430d4fba8b9Smrg#if OPT_TRACE
5431d4fba8b9Smrg	va_start(ap, fmt);
5432d4fba8b9Smrg	TraceVA(fmt, ap);
5433d4fba8b9Smrg	va_end(ap);
5434d4fba8b9Smrg#endif
5435d522f475Smrg    }
5436d522f475Smrg}
5437d4fba8b9Smrg#endif
5438d522f475Smrg
54393367019cSmrg#ifdef HAVE_LIBXPM
54403367019cSmrg
54413367019cSmrg#ifndef PIXMAP_ROOTDIR
54423367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
54433367019cSmrg#endif
54443367019cSmrg
54453367019cSmrgtypedef struct {
54463367019cSmrg    const char *name;
54473367019cSmrg    const char *const *data;
54483367019cSmrg} XPM_DATA;
54493367019cSmrg
54503367019cSmrgstatic char *
5451d4fba8b9Smrgx_find_icon(char **work, int *state, const char *filename, const char *suffix)
54523367019cSmrg{
54533367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
54543367019cSmrg    const char *larger = "_48x48";
54553367019cSmrg    char *result = 0;
54563367019cSmrg
54573367019cSmrg    if (*state >= 0) {
54583367019cSmrg	if ((*state & 1) == 0)
54593367019cSmrg	    suffix = "";
54603367019cSmrg	if ((*state & 2) == 0)
54613367019cSmrg	    larger = "";
54623367019cSmrg	if ((*state & 4) == 0) {
54633367019cSmrg	    prefix = "";
54643367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
54653367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
54663367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
54673367019cSmrg	    *state = -1;
54683367019cSmrg	} else if (*state >= 8) {
54693367019cSmrg	    *state = -1;
54703367019cSmrg	}
54713367019cSmrg    }
54723367019cSmrg
54733367019cSmrg    if (*state >= 0) {
5474037a25ddSmrg	size_t length;
5475037a25ddSmrg
5476d4fba8b9Smrg	FreeAndNull(*work);
54773367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
54783367019cSmrg	    strlen(suffix);
54793367019cSmrg	if ((result = malloc(length)) != 0) {
54803367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
54813367019cSmrg	    *work = result;
54823367019cSmrg	}
54833367019cSmrg	*state += 1;
54843367019cSmrg    }
5485d4fba8b9Smrg    TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
54863367019cSmrg    return result;
54873367019cSmrg}
54883367019cSmrg
54893367019cSmrg#if OPT_BUILTIN_XPMS
5490d4fba8b9Smrg
54913367019cSmrgstatic const XPM_DATA *
5492d4fba8b9Smrgbuilt_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
54933367019cSmrg{
54943367019cSmrg    const XPM_DATA *result = 0;
54953367019cSmrg    if (!IsEmpty(find)) {
54963367019cSmrg	Cardinal n;
54973367019cSmrg	for (n = 0; n < length; ++n) {
54983367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
54993367019cSmrg		result = table + n;
5500d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[n].name));
55013367019cSmrg		break;
55023367019cSmrg	    }
55033367019cSmrg	}
55043367019cSmrg
55053367019cSmrg	/*
55063367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
55073367019cSmrg	 * which are all _HHxWW format.
55083367019cSmrg	 */
55093367019cSmrg	if (result == 0) {
55103367019cSmrg	    const char *base = table[0].name;
55113367019cSmrg	    const char *last = strchr(base, '_');
55123367019cSmrg	    if (last != 0
55133367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
55143367019cSmrg		result = table + length - 1;
5515d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[0].name));
55163367019cSmrg	    }
55173367019cSmrg	}
55183367019cSmrg    }
55193367019cSmrg    return result;
55203367019cSmrg}
5521d4fba8b9Smrg#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
55223367019cSmrg#endif /* OPT_BUILTIN_XPMS */
55233367019cSmrg
55243367019cSmrgtypedef enum {
55253367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
55263367019cSmrg    ,eHintNone
55273367019cSmrg    ,eHintSearch
55283367019cSmrg} ICON_HINT;
55293367019cSmrg#endif /* HAVE_LIBXPM */
55303367019cSmrg
55313367019cSmrgint
55323367019cSmrggetVisualDepth(XtermWidget xw)
55333367019cSmrg{
55343367019cSmrg    int result = 0;
55353367019cSmrg
5536fa3f02f3Smrg    if (getVisualInfo(xw)) {
5537fa3f02f3Smrg	result = xw->visInfo->depth;
55383367019cSmrg    }
55393367019cSmrg    return result;
55403367019cSmrg}
55413367019cSmrg
55423367019cSmrg/*
55433367019cSmrg * WM_ICON_SIZE should be honored if possible.
55443367019cSmrg */
55453367019cSmrgvoid
5546d4fba8b9SmrgxtermLoadIcon(XtermWidget xw, const char *icon_hint)
55473367019cSmrg{
55483367019cSmrg#ifdef HAVE_LIBXPM
55493367019cSmrg    Display *dpy = XtDisplay(xw);
55503367019cSmrg    Pixmap myIcon = 0;
55513367019cSmrg    Pixmap myMask = 0;
55523367019cSmrg    char *workname = 0;
5553d4fba8b9Smrg    ICON_HINT hint = eHintDefault;
5554fa3f02f3Smrg#include <builtin_icons.h>
55553367019cSmrg
5556d4fba8b9Smrg    ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5557d4fba8b9Smrg    if (!IsEmpty(icon_hint)) {
5558d4fba8b9Smrg	if (!x_strcasecmp(icon_hint, "none")) {
5559d4fba8b9Smrg	    hint = eHintNone;
5560d4fba8b9Smrg	} else {
5561d4fba8b9Smrg	    hint = eHintSearch;
5562d4fba8b9Smrg	}
5563d4fba8b9Smrg    }
55643367019cSmrg
55653367019cSmrg    if (hint == eHintSearch) {
55663367019cSmrg	int state = 0;
5567d4fba8b9Smrg	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) {
55683367019cSmrg	    Pixmap resIcon = 0;
55693367019cSmrg	    Pixmap shapemask = 0;
55703367019cSmrg	    XpmAttributes attributes;
5571d4fba8b9Smrg	    struct stat sb;
55723367019cSmrg
55733367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
55743367019cSmrg	    attributes.valuemask = XpmDepth;
55753367019cSmrg
5576d4fba8b9Smrg	    if (IsEmpty(workname)
5577d4fba8b9Smrg		|| lstat(workname, &sb) != 0
5578d4fba8b9Smrg		|| !S_ISREG(sb.st_mode)) {
5579d4fba8b9Smrg		TRACE(("...failure (no such file)\n"));
5580d4fba8b9Smrg	    } else {
5581d4fba8b9Smrg		int rc = XpmReadFileToPixmap(dpy,
5582d4fba8b9Smrg					     DefaultRootWindow(dpy),
5583d4fba8b9Smrg					     workname,
5584d4fba8b9Smrg					     &resIcon,
5585d4fba8b9Smrg					     &shapemask,
5586d4fba8b9Smrg					     &attributes);
5587d4fba8b9Smrg		if (rc == XpmSuccess) {
5588d4fba8b9Smrg		    myIcon = resIcon;
5589d4fba8b9Smrg		    myMask = shapemask;
5590d4fba8b9Smrg		    TRACE(("...success\n"));
5591d4fba8b9Smrg		    ReportIcons(("found/loaded icon-file %s\n", workname));
5592d4fba8b9Smrg		    break;
5593d4fba8b9Smrg		} else {
5594d4fba8b9Smrg		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5595d4fba8b9Smrg		}
55963367019cSmrg	    }
55973367019cSmrg	}
55983367019cSmrg    }
55993367019cSmrg
56003367019cSmrg    /*
56013367019cSmrg     * If no external file was found, look for the name in the built-in table.
56023367019cSmrg     * If that fails, just use the biggest mini-icon.
56033367019cSmrg     */
56043367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
56053367019cSmrg	char **data;
56063367019cSmrg#if OPT_BUILTIN_XPMS
56073367019cSmrg	const XPM_DATA *myData = 0;
5608d4fba8b9Smrg	myData = BuiltInXPM(mini_xterm_xpms);
56093367019cSmrg	if (myData == 0)
5610d4fba8b9Smrg	    myData = BuiltInXPM(filled_xterm_xpms);
56113367019cSmrg	if (myData == 0)
5612d4fba8b9Smrg	    myData = BuiltInXPM(xterm_color_xpms);
56133367019cSmrg	if (myData == 0)
5614d4fba8b9Smrg	    myData = BuiltInXPM(xterm_xpms);
56153367019cSmrg	if (myData == 0)
56163367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
561794644356Smrg	data = (char **) myData->data;
56183367019cSmrg#else
56193367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
56203367019cSmrg#endif
56213367019cSmrg	if (XpmCreatePixmapFromData(dpy,
56223367019cSmrg				    DefaultRootWindow(dpy),
56233367019cSmrg				    data,
5624d4fba8b9Smrg				    &myIcon, &myMask, 0) == 0) {
5625d4fba8b9Smrg	    ReportIcons(("loaded built-in pixmap icon\n"));
5626d4fba8b9Smrg	} else {
56273367019cSmrg	    myIcon = 0;
56283367019cSmrg	    myMask = 0;
56293367019cSmrg	}
56303367019cSmrg    }
56313367019cSmrg
56323367019cSmrg    if (myIcon != 0) {
56333367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
56343367019cSmrg	if (!hints)
56353367019cSmrg	    hints = XAllocWMHints();
56363367019cSmrg
56373367019cSmrg	if (hints) {
56383367019cSmrg	    hints->flags |= IconPixmapHint;
56393367019cSmrg	    hints->icon_pixmap = myIcon;
56403367019cSmrg	    if (myMask) {
56413367019cSmrg		hints->flags |= IconMaskHint;
56423367019cSmrg		hints->icon_mask = myMask;
56433367019cSmrg	    }
56443367019cSmrg
56453367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
56463367019cSmrg	    XFree(hints);
5647d4fba8b9Smrg	    ReportIcons(("updated window-manager hints\n"));
56483367019cSmrg	}
56493367019cSmrg    }
56503367019cSmrg
5651d4fba8b9Smrg    free(workname);
56523367019cSmrg
56533367019cSmrg#else
56543367019cSmrg    (void) xw;
5655d4fba8b9Smrg    (void) icon_hint;
56563367019cSmrg#endif
56573367019cSmrg}
56583367019cSmrg
56593367019cSmrgvoid
5660cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
5661d522f475Smrg{
5662d522f475Smrg    Arg args[1];
5663cd3331d0Smrg    Boolean changed = True;
5664d522f475Smrg    Widget w = CURRENT_EMU();
5665d522f475Smrg    Widget top = SHELL_OF(w);
5666d522f475Smrg
5667d4fba8b9Smrg    char *my_attr = NULL;
5668d4fba8b9Smrg    char *old_value = value;
5669d4fba8b9Smrg#if OPT_WIDE_CHARS
5670d4fba8b9Smrg    Boolean titleIsUTF8;
5671d4fba8b9Smrg#endif
5672d522f475Smrg
5673b7c89284Ssnj    if (!AllowTitleOps(xw))
5674d522f475Smrg	return;
5675d522f475Smrg
5676d4fba8b9Smrg    /*
5677d4fba8b9Smrg     * Ignore empty or too-long requests.
5678d4fba8b9Smrg     */
5679d4fba8b9Smrg    if (value == 0 || strlen(value) > 1000)
5680d4fba8b9Smrg	return;
5681d4fba8b9Smrg
5682cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
5683cd3331d0Smrg	const char *temp;
5684cd3331d0Smrg	char *test;
5685cd3331d0Smrg
5686d4fba8b9Smrg	/* this allocates a new string, if no error is detected */
5687cd3331d0Smrg	value = x_decode_hex(value, &temp);
5688d4fba8b9Smrg	if (value == 0 || *temp != '\0') {
56893367019cSmrg	    free(value);
5690cd3331d0Smrg	    return;
56913367019cSmrg	}
5692cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
5693cd3331d0Smrg	    if (CharOf(*test) < 32) {
5694cd3331d0Smrg		*test = '\0';
5695cd3331d0Smrg		break;
5696cd3331d0Smrg	    }
5697cd3331d0Smrg	}
5698cd3331d0Smrg    }
5699d4fba8b9Smrg#if OPT_WIDE_CHARS
5700d522f475Smrg    /*
5701d4fba8b9Smrg     * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
5702d4fba8b9Smrg     * the WM_NAME property, rather than doing this directly.  That relies on
5703d4fba8b9Smrg     * the application to tell it if the format should be something other than
5704d4fba8b9Smrg     * STRING, i.e., by setting the XtNtitleEncoding resource.
5705d4fba8b9Smrg     *
5706d4fba8b9Smrg     * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
5707d4fba8b9Smrg     * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
5708d4fba8b9Smrg     * added UTF8_STRING (the documentation for that was discarded by an Xorg
5709d4fba8b9Smrg     * developer, although the source-code provides this feature).
5710d4fba8b9Smrg     *
5711d4fba8b9Smrg     * Since X11R5, if the X11 library fails to store a text property as
5712d4fba8b9Smrg     * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
5713d4fba8b9Smrg     * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
5714d4fba8b9Smrg     * case, limit the resulting characters to the printable ISO-8859-1 set.
5715d522f475Smrg     */
5716d4fba8b9Smrg    titleIsUTF8 = isValidUTF8((Char *) value);
5717d4fba8b9Smrg    if (IsSetUtf8Title(xw) && titleIsUTF8) {
5718d4fba8b9Smrg	char *testc = malloc(strlen(value) + 1);
5719d4fba8b9Smrg	Char *nextc = (Char *) value;
5720d4fba8b9Smrg	Boolean ok8bit = True;
5721d522f475Smrg
5722d4fba8b9Smrg	if (testc != NULL) {
5723d4fba8b9Smrg	    /*
5724d4fba8b9Smrg	     * Check if the data fits in STRING.  Along the way, replace
5725d4fba8b9Smrg	     * control characters.
5726d4fba8b9Smrg	     */
5727d4fba8b9Smrg	    Char *lastc = (Char *) testc;
5728d4fba8b9Smrg	    while (*nextc != '\0') {
5729d4fba8b9Smrg		unsigned ch;
5730d4fba8b9Smrg		nextc = convertFromUTF8(nextc, &ch);
5731d4fba8b9Smrg		if (ch > 255) {
5732d4fba8b9Smrg		    ok8bit = False;
5733d4fba8b9Smrg		} else if (!IsLatin1(ch)) {
5734d4fba8b9Smrg		    ch = OnlyLatin1(ch);
5735d4fba8b9Smrg		}
5736d4fba8b9Smrg		*lastc++ = (Char) ch;
5737d4fba8b9Smrg	    }
5738d4fba8b9Smrg	    *lastc = '\0';
5739d4fba8b9Smrg	    if (ok8bit) {
5740d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
5741d4fba8b9Smrg		if (value != old_value)
5742d4fba8b9Smrg		    free(value);
5743d4fba8b9Smrg		value = testc;
5744d4fba8b9Smrg		titleIsUTF8 = False;
5745d4fba8b9Smrg	    } else {
5746d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
5747d4fba8b9Smrg		       "\t%s\n", value));
5748d4fba8b9Smrg		free(testc);
5749d4fba8b9Smrg		nextc = (Char *) value;
5750d4fba8b9Smrg		while (*nextc != '\0') {
5751d4fba8b9Smrg		    unsigned ch;
5752d4fba8b9Smrg		    Char *skip = convertFromUTF8(nextc, &ch);
5753d4fba8b9Smrg		    if (iswcntrl((wint_t) ch)) {
5754d4fba8b9Smrg			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
5755d4fba8b9Smrg		    }
5756d4fba8b9Smrg		    nextc = skip;
5757d4fba8b9Smrg		}
5758cd3331d0Smrg	    }
5759d522f475Smrg	}
5760d4fba8b9Smrg    } else
5761d4fba8b9Smrg#endif
5762d4fba8b9Smrg    {
5763d4fba8b9Smrg	Char *c1 = (Char *) value;
5764d4fba8b9Smrg
5765d4fba8b9Smrg	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
5766d4fba8b9Smrg	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
5767d4fba8b9Smrg	    *c1 = (Char) OnlyLatin1(*c1);
5768d4fba8b9Smrg	}
5769d4fba8b9Smrg    }
5770d4fba8b9Smrg
5771d4fba8b9Smrg    my_attr = x_strdup(attribute);
5772d4fba8b9Smrg
5773d4fba8b9Smrg    ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
5774d522f475Smrg
5775d522f475Smrg#if OPT_WIDE_CHARS
5776d4fba8b9Smrg    /*
5777d4fba8b9Smrg     * If we're running in UTF-8 mode, and have not been told that the
5778d4fba8b9Smrg     * title string is in UTF-8, it is likely that non-ASCII text in the
5779d4fba8b9Smrg     * string will be rejected because it is not printable in the current
5780d4fba8b9Smrg     * locale.  So we convert it to UTF-8, allowing the X library to
5781d4fba8b9Smrg     * convert it back.
5782d4fba8b9Smrg     */
5783d4fba8b9Smrg    TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
5784d4fba8b9Smrg    if (xtermEnvUTF8() && !titleIsUTF8) {
5785d4fba8b9Smrg	size_t limit = strlen(value);
5786d4fba8b9Smrg	Char *c1 = (Char *) value;
5787d4fba8b9Smrg	int n;
5788cd3331d0Smrg
5789d4fba8b9Smrg	for (n = 0; c1[n] != '\0'; ++n) {
5790d4fba8b9Smrg	    if (c1[n] > 127) {
5791d4fba8b9Smrg		Char *converted;
5792d4fba8b9Smrg		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
5793d4fba8b9Smrg		    Char *temp = converted;
5794d4fba8b9Smrg		    while (*c1 != 0) {
5795d4fba8b9Smrg			temp = convertToUTF8(temp, *c1++);
5796d522f475Smrg		    }
5797d4fba8b9Smrg		    *temp = 0;
5798d4fba8b9Smrg		    if (value != old_value)
5799d4fba8b9Smrg			free(value);
5800d4fba8b9Smrg		    value = (char *) converted;
5801d4fba8b9Smrg		    ReportIcons(("...converted{%s}\n", value));
5802d522f475Smrg		}
5803d4fba8b9Smrg		break;
5804d522f475Smrg	    }
5805d522f475Smrg	}
5806d4fba8b9Smrg    }
5807d522f475Smrg#endif
5808d522f475Smrg
5809d522f475Smrg#if OPT_SAME_NAME
5810d4fba8b9Smrg    /* If the attribute isn't going to change, then don't bother... */
5811d4fba8b9Smrg    if (resource.sameName) {
5812d4fba8b9Smrg	char *buf = 0;
5813d4fba8b9Smrg	XtSetArg(args[0], my_attr, &buf);
5814d4fba8b9Smrg	XtGetValues(top, args, 1);
5815d4fba8b9Smrg	TRACE(("...comparing{%s}\n", NonNull(buf)));
5816d4fba8b9Smrg	if (buf != 0 && strcmp(value, buf) == 0)
5817d4fba8b9Smrg	    changed = False;
5818d4fba8b9Smrg    }
5819d522f475Smrg#endif /* OPT_SAME_NAME */
5820d522f475Smrg
5821d4fba8b9Smrg    if (changed) {
5822d4fba8b9Smrg	ReportIcons(("...updating %s\n", my_attr));
5823d4fba8b9Smrg	ReportIcons(("...value is %s\n", value));
5824d4fba8b9Smrg	XtSetArg(args[0], my_attr, value);
5825d4fba8b9Smrg	XtSetValues(top, args, 1);
5826d4fba8b9Smrg    }
5827d522f475Smrg#if OPT_WIDE_CHARS
5828d4fba8b9Smrg    if (xtermEnvUTF8()) {
5829d4fba8b9Smrg	Display *dpy = XtDisplay(xw);
5830d4fba8b9Smrg	const char *propname = (!strcmp(my_attr, XtNtitle)
5831d4fba8b9Smrg				? "_NET_WM_NAME"
5832d4fba8b9Smrg				: "_NET_WM_ICON_NAME");
5833d4fba8b9Smrg	Atom my_atom = XInternAtom(dpy, propname, False);
5834d4fba8b9Smrg
5835d4fba8b9Smrg	if (my_atom != None) {
5836d4fba8b9Smrg	    changed = True;
5837d4fba8b9Smrg
5838d4fba8b9Smrg	    if (IsSetUtf8Title(xw)) {
5839d4fba8b9Smrg#if OPT_SAME_NAME
5840d4fba8b9Smrg		if (resource.sameName) {
5841d4fba8b9Smrg		    Atom actual_type;
5842d4fba8b9Smrg		    Atom requested_type = XA_UTF8_STRING(dpy);
5843d4fba8b9Smrg		    int actual_format = 0;
5844d4fba8b9Smrg		    long long_length = 1024;
5845d4fba8b9Smrg		    unsigned long nitems = 0;
5846d4fba8b9Smrg		    unsigned long bytes_after = 0;
5847d4fba8b9Smrg		    unsigned char *prop = 0;
5848d4fba8b9Smrg
5849d4fba8b9Smrg		    if (xtermGetWinProp(dpy,
5850d4fba8b9Smrg					VShellWindow(xw),
5851d4fba8b9Smrg					my_atom,
5852d4fba8b9Smrg					0L,
5853d4fba8b9Smrg					long_length,
5854d4fba8b9Smrg					requested_type,
5855d4fba8b9Smrg					&actual_type,
5856d4fba8b9Smrg					&actual_format,
5857d4fba8b9Smrg					&nitems,
5858d4fba8b9Smrg					&bytes_after,
5859d4fba8b9Smrg					&prop)
5860d4fba8b9Smrg			&& actual_type == requested_type
5861d4fba8b9Smrg			&& actual_format == 8
5862d4fba8b9Smrg			&& prop != 0
5863d4fba8b9Smrg			&& nitems == strlen(value)
5864d4fba8b9Smrg			&& memcmp(value, prop, nitems) == 0) {
5865d4fba8b9Smrg			changed = False;
5866cd3331d0Smrg		    }
5867cd3331d0Smrg		}
5868d4fba8b9Smrg#endif /* OPT_SAME_NAME */
5869d4fba8b9Smrg		if (changed) {
5870d4fba8b9Smrg		    ReportIcons(("...updating %s\n", propname));
5871d4fba8b9Smrg		    ReportIcons(("...value is %s\n", value));
5872d4fba8b9Smrg		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
5873d4fba8b9Smrg				    XA_UTF8_STRING(dpy), 8,
5874d4fba8b9Smrg				    PropModeReplace,
5875d4fba8b9Smrg				    (Char *) value,
5876d4fba8b9Smrg				    (int) strlen(value));
5877d4fba8b9Smrg		}
5878d4fba8b9Smrg	    } else {
5879d4fba8b9Smrg		ReportIcons(("...deleting %s\n", propname));
5880d4fba8b9Smrg		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5881d522f475Smrg	    }
5882d522f475Smrg	}
5883d522f475Smrg    }
5884d4fba8b9Smrg#endif
5885d4fba8b9Smrg    if (value != old_value) {
58863367019cSmrg	free(value);
58873367019cSmrg    }
58883367019cSmrg    free(my_attr);
58893367019cSmrg
5890cd3331d0Smrg    return;
5891d522f475Smrg}
5892d522f475Smrg
5893d522f475Smrgvoid
5894b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
5895d522f475Smrg{
5896cd3331d0Smrg    if (name == 0) {
58973367019cSmrg	name = emptyString;
58983367019cSmrg    }
58993367019cSmrg    if (!showZIconBeep(xw, name))
5900b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
5901d522f475Smrg}
5902d522f475Smrg
5903d522f475Smrgvoid
5904b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
5905d522f475Smrg{
5906b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
5907d522f475Smrg}
5908d522f475Smrg
5909712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
5910d522f475Smrg
5911d522f475Smrgvoid
5912d522f475SmrgChangeXprop(char *buf)
5913d522f475Smrg{
5914d522f475Smrg    Display *dpy = XtDisplay(toplevel);
5915d522f475Smrg    Window w = XtWindow(toplevel);
5916d522f475Smrg    XTextProperty text_prop;
5917d522f475Smrg    Atom aprop;
5918d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
5919d522f475Smrg
5920d522f475Smrg    if (pchEndPropName)
5921d522f475Smrg	*pchEndPropName = '\0';
5922d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
5923d522f475Smrg    if (pchEndPropName == NULL) {
5924d522f475Smrg	/* no "=value" given, so delete the property */
5925d522f475Smrg	XDeleteProperty(dpy, w, aprop);
5926d522f475Smrg    } else {
5927d522f475Smrg	text_prop.value = pchEndPropName + 1;
5928d522f475Smrg	text_prop.encoding = XA_STRING;
5929d522f475Smrg	text_prop.format = 8;
5930d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
5931d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
5932d522f475Smrg    }
5933d522f475Smrg}
5934d522f475Smrg
5935d522f475Smrg/***====================================================================***/
5936d522f475Smrg
5937d522f475Smrg/*
5938d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
5939d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
5940d522f475Smrg */
5941d522f475Smrgvoid
59429a64e1c5SmrgReverseOldColors(XtermWidget xw)
5943d522f475Smrg{
59449a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
5945d522f475Smrg    Pixel tmpPix;
5946d522f475Smrg    char *tmpName;
5947d522f475Smrg
5948d522f475Smrg    if (pOld) {
5949d4fba8b9Smrg	/* change text cursor, if necessary */
5950d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
5951d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
5952d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
59539a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
5954d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
5955d522f475Smrg	    }
5956d522f475Smrg	    if (pOld->names[TEXT_BG]) {
5957d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
5958d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
5959d522f475Smrg		}
5960d522f475Smrg	    }
5961d522f475Smrg	}
5962d522f475Smrg
5963d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
5964d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
5965d522f475Smrg
5966d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
5967d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
5968d522f475Smrg
5969d522f475Smrg#if OPT_TEK4014
5970d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
5971d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
5972d522f475Smrg#endif
5973d4fba8b9Smrg	FreeMarkGCs(xw);
5974d522f475Smrg    }
5975d522f475Smrg    return;
5976d522f475Smrg}
5977d522f475Smrg
5978d522f475SmrgBool
5979d522f475SmrgAllocateTermColor(XtermWidget xw,
5980d522f475Smrg		  ScrnColors * pNew,
5981d522f475Smrg		  int ndx,
5982cd3331d0Smrg		  const char *name,
5983cd3331d0Smrg		  Bool always)
5984d522f475Smrg{
5985cd3331d0Smrg    Bool result = False;
5986d522f475Smrg
5987cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
5988cd3331d0Smrg	XColor def;
5989cd3331d0Smrg	char *newName;
5990cd3331d0Smrg
5991712a7ff4Smrg	result = True;
5992712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
5993712a7ff4Smrg	    def.pixel = xw->old_foreground;
5994712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
5995712a7ff4Smrg	    def.pixel = xw->old_background;
59963367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
5997712a7ff4Smrg	    result = False;
5998712a7ff4Smrg	}
5999712a7ff4Smrg
6000712a7ff4Smrg	if (result
6001cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
6002712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
6003cd3331d0Smrg		free(pNew->names[ndx]);
6004712a7ff4Smrg	    }
6005cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
6006cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
6007712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
6008712a7ff4Smrg		   ndx, newName, def.pixel));
6009cd3331d0Smrg	} else {
6010cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
6011712a7ff4Smrg	    result = False;
6012cd3331d0Smrg	}
6013cd3331d0Smrg    }
6014cd3331d0Smrg    return result;
6015d522f475Smrg}
6016d522f475Smrg/***====================================================================***/
6017d522f475Smrg
6018d522f475Smrg/* ARGSUSED */
6019d522f475Smrgvoid
6020cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
6021d522f475Smrg{
60223367019cSmrg    if_DEBUG({
60233367019cSmrg	xtermWarning(s, a);
60243367019cSmrg    });
6025d522f475Smrg}
6026d522f475Smrg
6027d522f475Smrgconst char *
6028d522f475SmrgSysErrorMsg(int code)
6029d522f475Smrg{
603094644356Smrg    static const char unknown[] = "unknown error";
6031d4fba8b9Smrg    const char *s = strerror(code);
6032d522f475Smrg    return s ? s : unknown;
6033d522f475Smrg}
6034d522f475Smrg
6035d522f475Smrgconst char *
6036d522f475SmrgSysReasonMsg(int code)
6037d522f475Smrg{
6038d522f475Smrg    /* *INDENT-OFF* */
6039d522f475Smrg    static const struct {
6040d522f475Smrg	int code;
6041d522f475Smrg	const char *name;
6042d522f475Smrg    } table[] = {
6043d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
6044d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
6045d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
6046d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
6047d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
6048d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
6049d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
6050d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
6051d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
6052d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
6053d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
6054d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
6055d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
6056d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
6057d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
6058d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
6059d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
6060d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
6061d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
6062d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
6063d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6064d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
6065d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
6066d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6067d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6068d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6069d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
6070d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6071d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6072d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
6073d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6074d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6075d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6076d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
6077d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6078d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6079d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6080d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6081d522f475Smrg    };
6082d522f475Smrg    /* *INDENT-ON* */
6083d522f475Smrg
6084d522f475Smrg    Cardinal n;
6085d522f475Smrg    const char *result = "?";
6086d522f475Smrg
6087d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
6088d522f475Smrg	if (code == table[n].code) {
6089d522f475Smrg	    result = table[n].name;
6090d522f475Smrg	    break;
6091d522f475Smrg	}
6092d522f475Smrg    }
6093d522f475Smrg    return result;
6094d522f475Smrg}
6095d522f475Smrg
6096d522f475Smrgvoid
6097d522f475SmrgSysError(int code)
6098d522f475Smrg{
6099d522f475Smrg    int oerrno = errno;
6100d522f475Smrg
6101c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6102d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6103d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6104d522f475Smrg
6105d522f475Smrg    Cleanup(code);
6106d522f475Smrg}
6107d522f475Smrg
6108d522f475Smrgvoid
61093367019cSmrgNormalExit(void)
6110d522f475Smrg{
6111d522f475Smrg    static Bool cleaning;
6112d522f475Smrg
6113d522f475Smrg    /*
6114d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
6115d522f475Smrg     */
61163367019cSmrg    if (cleaning) {
61173367019cSmrg	hold_screen = 0;
61183367019cSmrg	return;
61193367019cSmrg    }
6120d522f475Smrg
61213367019cSmrg    cleaning = True;
61223367019cSmrg    need_cleanup = False;
6123d522f475Smrg
61243367019cSmrg    if (hold_screen) {
61253367019cSmrg	hold_screen = 2;
61263367019cSmrg	while (hold_screen) {
6127d4fba8b9Smrg	    xtermFlushDbe(term);
6128d4fba8b9Smrg	    xevents(term);
6129d4fba8b9Smrg	    Sleep(EVENT_DELAY);
6130d522f475Smrg	}
61313367019cSmrg    }
6132d522f475Smrg#if OPT_SESSION_MGT
61333367019cSmrg    if (resource.sessionMgt) {
61343367019cSmrg	XtVaSetValues(toplevel,
61353367019cSmrg		      XtNjoinSession, False,
61363367019cSmrg		      (void *) 0);
6137d522f475Smrg    }
61383367019cSmrg#endif
61393367019cSmrg    Cleanup(0);
61403367019cSmrg}
61413367019cSmrg
6142d4fba8b9Smrg#if USE_DOUBLE_BUFFER
6143d4fba8b9Smrgvoid
6144d4fba8b9SmrgxtermFlushDbe(XtermWidget xw)
6145d4fba8b9Smrg{
6146d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
6147d4fba8b9Smrg    if (resource.buffered && screen->needSwap) {
6148d4fba8b9Smrg	XdbeSwapInfo swap;
6149d4fba8b9Smrg	swap.swap_window = VWindow(screen);
6150d4fba8b9Smrg	swap.swap_action = XdbeCopied;
6151d4fba8b9Smrg	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6152d4fba8b9Smrg	XFlush(XtDisplay(xw));
6153d4fba8b9Smrg	screen->needSwap = 0;
6154d4fba8b9Smrg	ScrollBarDrawThumb(xw, 2);
6155d4fba8b9Smrg	X_GETTIMEOFDAY(&screen->buffered_at);
6156d4fba8b9Smrg    }
6157d4fba8b9Smrg}
6158d4fba8b9Smrg
6159d4fba8b9Smrgvoid
6160d4fba8b9SmrgxtermTimedDbe(XtermWidget xw)
6161d4fba8b9Smrg{
6162d4fba8b9Smrg    if (resource.buffered) {
6163d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
6164d4fba8b9Smrg	struct timeval now;
6165d4fba8b9Smrg	long elapsed;
6166d4fba8b9Smrg	long limit = DbeMsecs(xw);
6167d4fba8b9Smrg
6168d4fba8b9Smrg	X_GETTIMEOFDAY(&now);
6169d4fba8b9Smrg	if (screen->buffered_at.tv_sec) {
6170d4fba8b9Smrg	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6171d4fba8b9Smrg		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6172d4fba8b9Smrg	} else {
6173d4fba8b9Smrg	    elapsed = limit;
6174d4fba8b9Smrg	}
6175d4fba8b9Smrg	if (elapsed >= limit) {
6176d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
6177d4fba8b9Smrg	    xtermFlushDbe(xw);
6178d4fba8b9Smrg	}
6179d4fba8b9Smrg    }
6180d4fba8b9Smrg}
6181d4fba8b9Smrg#endif
6182d4fba8b9Smrg
61833367019cSmrg/*
61843367019cSmrg * cleanup by sending SIGHUP to client processes
61853367019cSmrg */
61863367019cSmrgvoid
61873367019cSmrgCleanup(int code)
61883367019cSmrg{
61893367019cSmrg    TScreen *screen = TScreenOf(term);
61903367019cSmrg
61913367019cSmrg    TRACE(("Cleanup %d\n", code));
6192d522f475Smrg
6193d522f475Smrg    if (screen->pid > 1) {
6194d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
6195d522f475Smrg    }
6196d522f475Smrg    Exit(code);
6197d522f475Smrg}
6198d522f475Smrg
6199fa3f02f3Smrg#ifndef S_IXOTH
6200fa3f02f3Smrg#define S_IXOTH 1
6201fa3f02f3Smrg#endif
6202fa3f02f3Smrg
6203fa3f02f3SmrgBoolean
6204fa3f02f3SmrgvalidProgram(const char *pathname)
6205fa3f02f3Smrg{
6206fa3f02f3Smrg    Boolean result = False;
6207fa3f02f3Smrg    struct stat sb;
6208fa3f02f3Smrg
6209fa3f02f3Smrg    if (!IsEmpty(pathname)
6210fa3f02f3Smrg	&& *pathname == '/'
6211fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
6212fa3f02f3Smrg	&& stat(pathname, &sb) == 0
6213fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
6214fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
6215fa3f02f3Smrg	result = True;
6216fa3f02f3Smrg    }
6217fa3f02f3Smrg    return result;
6218fa3f02f3Smrg}
6219fa3f02f3Smrg
6220d522f475Smrg#ifndef VMS
62213367019cSmrg#ifndef PATH_MAX
62223367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
62233367019cSmrg#endif
6224d522f475Smrgchar *
6225d522f475SmrgxtermFindShell(char *leaf, Bool warning)
6226d522f475Smrg{
62273367019cSmrg    char *s0;
6228d522f475Smrg    char *s;
6229d522f475Smrg    char *d;
6230d522f475Smrg    char *tmp;
6231d522f475Smrg    char *result = leaf;
62323367019cSmrg    Bool allocated = False;
6233d522f475Smrg
6234d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
62353367019cSmrg
62363367019cSmrg    if (!strncmp("./", result, (size_t) 2)
62373367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
62383367019cSmrg	size_t need = PATH_MAX;
62393367019cSmrg	size_t used = strlen(result) + 2;
62403367019cSmrg	char *buffer = malloc(used + need);
62413367019cSmrg	if (buffer != 0) {
62423367019cSmrg	    if (getcwd(buffer, need) != 0) {
62433367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
62443367019cSmrg		result = buffer;
62453367019cSmrg		allocated = True;
62463367019cSmrg	    } else {
62473367019cSmrg		free(buffer);
62483367019cSmrg	    }
62493367019cSmrg	}
62503367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
6251d522f475Smrg	/* find it in $PATH */
62523367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
62530d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
6254d522f475Smrg		Bool found = False;
6255d522f475Smrg		while (*s != '\0') {
6256d522f475Smrg		    strcpy(tmp, s);
6257d522f475Smrg		    for (d = tmp;; ++d) {
6258d522f475Smrg			if (*d == ':' || *d == '\0') {
6259d522f475Smrg			    int skip = (*d != '\0');
6260d522f475Smrg			    *d = '/';
6261d522f475Smrg			    strcpy(d + 1, leaf);
6262d522f475Smrg			    if (skip)
6263d522f475Smrg				++d;
6264d522f475Smrg			    s += (d - tmp);
6265fa3f02f3Smrg			    if (validProgram(tmp)) {
6266d522f475Smrg				result = x_strdup(tmp);
6267d522f475Smrg				found = True;
62683367019cSmrg				allocated = True;
6269d522f475Smrg			    }
6270d522f475Smrg			    break;
6271d522f475Smrg			}
6272d522f475Smrg		    }
6273d522f475Smrg		    if (found)
6274d522f475Smrg			break;
6275d522f475Smrg		}
6276d522f475Smrg		free(tmp);
6277d522f475Smrg	    }
62783367019cSmrg	    free(s0);
6279d522f475Smrg	}
6280d522f475Smrg    }
6281d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
6282fa3f02f3Smrg    if (!validProgram(result)) {
6283d522f475Smrg	if (warning)
62843367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
62853367019cSmrg	if (allocated)
62863367019cSmrg	    free(result);
6287d522f475Smrg	result = 0;
6288d522f475Smrg    }
62893367019cSmrg    /* be consistent, so that caller can always free the result */
62903367019cSmrg    if (result != 0 && !allocated)
62913367019cSmrg	result = x_strdup(result);
6292d522f475Smrg    return result;
6293d522f475Smrg}
6294d522f475Smrg#endif /* VMS */
6295d522f475Smrg
62960d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6297d522f475Smrg
62983367019cSmrg/*
62993367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
63003367019cSmrg * This could happen on some older machines due to the uneven standardization
63013367019cSmrg * process for the two functions.
63023367019cSmrg *
63033367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
63043367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
63053367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
63063367019cSmrg * could copy environ.
63073367019cSmrg */
63083367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
63093367019cSmrg#undef HAVE_PUTENV
63103367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
63113367019cSmrg#undef HAVE_UNSETENV
63123367019cSmrg#endif
63133367019cSmrg
6314d522f475Smrg/*
6315d522f475Smrg * copy the environment before Setenv'ing.
6316d522f475Smrg */
6317d522f475Smrgvoid
6318d522f475SmrgxtermCopyEnv(char **oldenv)
6319d522f475Smrg{
63203367019cSmrg#ifdef HAVE_PUTENV
63213367019cSmrg    (void) oldenv;
63223367019cSmrg#else
6323d522f475Smrg    unsigned size;
6324d522f475Smrg    char **newenv;
6325d522f475Smrg
6326d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
6327d522f475Smrg	;
6328d522f475Smrg    }
6329d522f475Smrg
6330d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
6331d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
6332d522f475Smrg    environ = newenv;
63333367019cSmrg#endif
63343367019cSmrg}
63353367019cSmrg
63363367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
63373367019cSmrgstatic int
63383367019cSmrgfindEnv(const char *var, int *lengthp)
63393367019cSmrg{
63403367019cSmrg    char *test;
63413367019cSmrg    int envindex = 0;
63423367019cSmrg    size_t len = strlen(var);
63433367019cSmrg    int found = -1;
63443367019cSmrg
63453367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
63463367019cSmrg
63473367019cSmrg    while ((test = environ[envindex]) != NULL) {
63483367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
63493367019cSmrg	    found = envindex;
63503367019cSmrg	    break;
63513367019cSmrg	}
63523367019cSmrg	envindex++;
63533367019cSmrg    }
63543367019cSmrg    *lengthp = envindex;
63553367019cSmrg    return found;
6356d522f475Smrg}
63573367019cSmrg#endif
6358d522f475Smrg
6359d522f475Smrg/*
6360d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6361d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
6362d522f475Smrg * This procedure assumes the memory for the first level of environ
6363d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
6364d522f475Smrg * to have to do a realloc().
6365d522f475Smrg */
6366d522f475Smrgvoid
6367cd3331d0SmrgxtermSetenv(const char *var, const char *value)
6368d522f475Smrg{
6369d522f475Smrg    if (value != 0) {
63703367019cSmrg#ifdef HAVE_PUTENV
63713367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
63723367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
63733367019cSmrg	if (both) {
63743367019cSmrg	    sprintf(both, "%s=%s", var, value);
63753367019cSmrg	    putenv(both);
63763367019cSmrg	}
63773367019cSmrg#else
6378d522f475Smrg	size_t len = strlen(var);
63793367019cSmrg	int envindex;
63803367019cSmrg	int found = findEnv(var, &envindex);
6381d522f475Smrg
6382d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6383d522f475Smrg
6384d522f475Smrg	if (found < 0) {
6385d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
6386d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
6387d522f475Smrg
6388d522f475Smrg	    if (need > have) {
6389d522f475Smrg		char **newenv;
6390d522f475Smrg		newenv = TypeMallocN(char *, need);
6391d522f475Smrg		if (newenv == 0) {
63923367019cSmrg		    xtermWarning("Cannot increase environment\n");
6393d522f475Smrg		    return;
6394d522f475Smrg		}
6395d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
6396d522f475Smrg		free(environ);
6397d522f475Smrg		environ = newenv;
6398d522f475Smrg	    }
6399d522f475Smrg
6400d522f475Smrg	    found = envindex;
6401d522f475Smrg	    environ[found + 1] = NULL;
6402d522f475Smrg	    environ = environ;
6403d522f475Smrg	}
6404d522f475Smrg
6405d4fba8b9Smrg	environ[found] = malloc(2 + len + strlen(value));
6406d522f475Smrg	if (environ[found] == 0) {
64073367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
6408d522f475Smrg	    return;
6409d522f475Smrg	}
6410d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
64113367019cSmrg#endif
6412d522f475Smrg    }
6413d522f475Smrg}
6414d522f475Smrg
64153367019cSmrgvoid
64163367019cSmrgxtermUnsetenv(const char *var)
64173367019cSmrg{
64183367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
64193367019cSmrg#ifdef HAVE_UNSETENV
64203367019cSmrg    unsetenv(var);
64213367019cSmrg#else
64223367019cSmrg    {
64233367019cSmrg	int ignore;
64243367019cSmrg	int item = findEnv(var, &ignore);
64253367019cSmrg	if (item >= 0) {
64263367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
64273367019cSmrg		++item;
64283367019cSmrg	    }
64293367019cSmrg	}
64303367019cSmrg    }
64313367019cSmrg#endif
64323367019cSmrg}
64333367019cSmrg
6434d522f475Smrg/*ARGSUSED*/
6435d522f475Smrgint
64369a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
6437d522f475Smrg{
64383367019cSmrg    xtermWarning("warning, error event received:\n");
6439d4fba8b9Smrg    TRACE_X_ERR(d, ev);
6440d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6441d522f475Smrg    Exit(ERROR_XERROR);
6442d522f475Smrg    return 0;			/* appease the compiler */
6443d522f475Smrg}
6444d522f475Smrg
6445712a7ff4Smrgvoid
6446712a7ff4Smrgice_error(IceConn iceConn)
6447712a7ff4Smrg{
6448712a7ff4Smrg    (void) iceConn;
6449712a7ff4Smrg
64503367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
64513367019cSmrg		 (long) getpid(), errno);
6452712a7ff4Smrg
6453712a7ff4Smrg    Exit(ERROR_ICEERROR);
6454712a7ff4Smrg}
6455712a7ff4Smrg
6456d522f475Smrg/*ARGSUSED*/
6457d522f475Smrgint
6458fa3f02f3Smrgxioerror(Display *dpy)
6459d522f475Smrg{
6460d522f475Smrg    int the_error = errno;
6461d522f475Smrg
64623367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
64633367019cSmrg		 the_error, SysErrorMsg(the_error),
64643367019cSmrg		 DisplayString(dpy));
6465d522f475Smrg
6466d522f475Smrg    Exit(ERROR_XIOERROR);
6467d522f475Smrg    return 0;			/* appease the compiler */
6468d522f475Smrg}
6469d522f475Smrg
6470728ff447Schristosvoid
6471d522f475Smrgxt_error(String message)
6472d522f475Smrg{
64733367019cSmrg    xtermWarning("Xt error: %s\n", message);
6474d522f475Smrg
6475d522f475Smrg    /*
6476d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
6477d522f475Smrg     */
6478d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
64793367019cSmrg	xtermWarning("DISPLAY is not set\n");
6480d522f475Smrg    }
6481d522f475Smrg    exit(1);
6482d522f475Smrg}
6483d522f475Smrg
6484d522f475Smrgint
6485d522f475SmrgXStrCmp(char *s1, char *s2)
6486d522f475Smrg{
6487d522f475Smrg    if (s1 && s2)
6488d522f475Smrg	return (strcmp(s1, s2));
6489d522f475Smrg    if (s1 && *s1)
6490d522f475Smrg	return (1);
6491d522f475Smrg    if (s2 && *s2)
6492d522f475Smrg	return (-1);
6493d522f475Smrg    return (0);
6494d522f475Smrg}
6495d522f475Smrg
6496d522f475Smrg#if OPT_TEK4014
6497d522f475Smrgstatic void
6498fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
6499d522f475Smrg{
6500d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
6501d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
6502d522f475Smrg    XWithdrawWindow(dpy, w, scr);
6503d522f475Smrg    return;
6504d522f475Smrg}
6505d522f475Smrg#endif
6506d522f475Smrg
6507d522f475Smrgvoid
6508d522f475Smrgset_vt_visibility(Bool on)
6509d522f475Smrg{
6510c219fbebSmrg    XtermWidget xw = term;
6511c219fbebSmrg    TScreen *screen = TScreenOf(xw);
6512d522f475Smrg
6513d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
6514d522f475Smrg    if (on) {
6515c219fbebSmrg	if (!screen->Vshow && xw) {
6516c219fbebSmrg	    VTInit(xw);
6517c219fbebSmrg	    XtMapWidget(XtParent(xw));
6518d522f475Smrg#if OPT_TOOLBAR
6519d522f475Smrg	    /* we need both of these during initialization */
6520c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
6521d522f475Smrg	    ShowToolbar(resource.toolBar);
6522d522f475Smrg#endif
6523d522f475Smrg	    screen->Vshow = True;
6524d522f475Smrg	}
6525d522f475Smrg    }
6526d522f475Smrg#if OPT_TEK4014
6527d522f475Smrg    else {
6528c219fbebSmrg	if (screen->Vshow && xw) {
6529c219fbebSmrg	    withdraw_window(XtDisplay(xw),
6530c219fbebSmrg			    VShellWindow(xw),
6531c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
6532d522f475Smrg	    screen->Vshow = False;
6533d522f475Smrg	}
6534d522f475Smrg    }
6535d522f475Smrg    set_vthide_sensitivity();
6536d522f475Smrg    set_tekhide_sensitivity();
6537d522f475Smrg    update_vttekmode();
6538d522f475Smrg    update_tekshow();
6539d522f475Smrg    update_vtshow();
6540d522f475Smrg#endif
6541d522f475Smrg    return;
6542d522f475Smrg}
6543d522f475Smrg
6544d522f475Smrg#if OPT_TEK4014
6545d522f475Smrgvoid
6546d522f475Smrgset_tek_visibility(Bool on)
6547d522f475Smrg{
6548d4fba8b9Smrg    XtermWidget xw = term;
6549d4fba8b9Smrg
6550d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
6551d522f475Smrg
6552d522f475Smrg    if (on) {
6553d4fba8b9Smrg	if (!TEK4014_SHOWN(xw)) {
6554cd3331d0Smrg	    if (tekWidget == 0) {
6555cd3331d0Smrg		TekInit();	/* will exit on failure */
6556cd3331d0Smrg	    }
6557cd3331d0Smrg	    if (tekWidget != 0) {
6558cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
6559cd3331d0Smrg		XtRealizeWidget(tekParent);
6560cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
6561d522f475Smrg#if OPT_TOOLBAR
6562cd3331d0Smrg		/* we need both of these during initialization */
6563cd3331d0Smrg		XtMapWidget(tekParent);
6564cd3331d0Smrg		XtMapWidget(tekWidget);
6565d522f475Smrg#endif
6566cd3331d0Smrg		XtOverrideTranslations(tekParent,
6567cd3331d0Smrg				       XtParseTranslationTable
6568cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6569cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
6570cd3331d0Smrg				       XtWindow(tekParent),
6571cd3331d0Smrg				       &wm_delete_window, 1);
6572d4fba8b9Smrg		TEK4014_SHOWN(xw) = True;
6573cd3331d0Smrg	    }
6574d522f475Smrg	}
6575d522f475Smrg    } else {
6576d4fba8b9Smrg	if (TEK4014_SHOWN(xw) && tekWidget) {
6577d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
6578d522f475Smrg			    TShellWindow,
6579d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6580d4fba8b9Smrg	    TEK4014_SHOWN(xw) = False;
6581d522f475Smrg	}
6582d522f475Smrg    }
6583d522f475Smrg    set_tekhide_sensitivity();
6584d522f475Smrg    set_vthide_sensitivity();
6585d522f475Smrg    update_vtshow();
6586d522f475Smrg    update_tekshow();
6587d522f475Smrg    update_vttekmode();
6588d522f475Smrg    return;
6589d522f475Smrg}
6590d522f475Smrg
6591d522f475Smrgvoid
6592d522f475Smrgend_tek_mode(void)
6593d522f475Smrg{
6594cd3331d0Smrg    XtermWidget xw = term;
6595cd3331d0Smrg
6596cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
6597cd3331d0Smrg	FlushLog(xw);
6598dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
6599dfb07bc7Smrg	xtermSetWinSize(xw);
6600d522f475Smrg	longjmp(Tekend, 1);
6601d522f475Smrg    }
6602d522f475Smrg    return;
6603d522f475Smrg}
6604d522f475Smrg
6605d522f475Smrgvoid
6606d522f475Smrgend_vt_mode(void)
6607d522f475Smrg{
6608cd3331d0Smrg    XtermWidget xw = term;
6609cd3331d0Smrg
6610cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
6611cd3331d0Smrg	FlushLog(xw);
6612d4fba8b9Smrg	set_tek_visibility(True);
6613cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
6614dfb07bc7Smrg	TekSetWinSize(tekWidget);
6615d522f475Smrg	longjmp(VTend, 1);
6616d522f475Smrg    }
6617d522f475Smrg    return;
6618d522f475Smrg}
6619d522f475Smrg
6620d522f475Smrgvoid
6621d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
6622d522f475Smrg{
6623d522f475Smrg    if (tovt) {
6624d522f475Smrg	if (tekRefreshList)
6625d522f475Smrg	    TekRefresh(tekWidget);
6626d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
6627d522f475Smrg    } else {
6628d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
6629d522f475Smrg    }
6630d522f475Smrg}
6631d522f475Smrg
6632d522f475Smrgvoid
6633d522f475Smrghide_vt_window(void)
6634d522f475Smrg{
6635d522f475Smrg    set_vt_visibility(False);
6636d522f475Smrg    if (!TEK4014_ACTIVE(term))
6637d522f475Smrg	switch_modes(False);	/* switch to tek mode */
6638d522f475Smrg}
6639d522f475Smrg
6640d522f475Smrgvoid
6641d522f475Smrghide_tek_window(void)
6642d522f475Smrg{
6643d522f475Smrg    set_tek_visibility(False);
6644d522f475Smrg    tekRefreshList = (TekLink *) 0;
6645d522f475Smrg    if (TEK4014_ACTIVE(term))
6646d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
6647d522f475Smrg}
6648d522f475Smrg#endif /* OPT_TEK4014 */
6649d522f475Smrg
6650d522f475Smrgstatic const char *
6651d522f475Smrgskip_punct(const char *s)
6652d522f475Smrg{
6653d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
6654d522f475Smrg	++s;
6655d522f475Smrg    }
6656d522f475Smrg    return s;
6657d522f475Smrg}
6658d522f475Smrg
6659d522f475Smrgstatic int
6660d522f475Smrgcmp_options(const void *a, const void *b)
6661d522f475Smrg{
6662d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
6663d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
6664d522f475Smrg    return strcmp(s1, s2);
6665d522f475Smrg}
6666d522f475Smrg
6667d522f475Smrgstatic int
6668d522f475Smrgcmp_resources(const void *a, const void *b)
6669d522f475Smrg{
6670d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
6671d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
6672d522f475Smrg}
6673d522f475Smrg
6674d522f475SmrgXrmOptionDescRec *
6675d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
6676d522f475Smrg{
6677d522f475Smrg    static XrmOptionDescRec *res_array = 0;
6678d522f475Smrg
6679d522f475Smrg#ifdef NO_LEAKS
6680cd3331d0Smrg    if (descs == 0) {
6681d4fba8b9Smrg	FreeAndNull(res_array);
6682d522f475Smrg    } else
6683d522f475Smrg#endif
6684d522f475Smrg    if (res_array == 0) {
6685d522f475Smrg	Cardinal j;
6686d522f475Smrg
6687d522f475Smrg	/* make a sorted index to 'resources' */
6688d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
6689cd3331d0Smrg	if (res_array != 0) {
6690cd3331d0Smrg	    for (j = 0; j < res_count; j++)
6691cd3331d0Smrg		res_array[j] = descs[j];
6692cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
6693cd3331d0Smrg	}
6694d522f475Smrg    }
6695d522f475Smrg    return res_array;
6696d522f475Smrg}
6697d522f475Smrg
6698d522f475Smrg/*
6699d522f475Smrg * The first time this is called, construct sorted index to the main program's
6700d522f475Smrg * list of options, taking into account the on/off options which will be
6701d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
6702d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
6703d522f475Smrg */
6704d522f475SmrgOptionHelp *
6705d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
6706d522f475Smrg{
6707d522f475Smrg    static OptionHelp *opt_array = 0;
6708d522f475Smrg
6709d522f475Smrg#ifdef NO_LEAKS
6710d522f475Smrg    if (descs == 0 && opt_array != 0) {
6711d522f475Smrg	sortedOptDescs(descs, numDescs);
6712d4fba8b9Smrg	FreeAndNull(opt_array);
6713d522f475Smrg	return 0;
6714d522f475Smrg    } else if (options == 0 || descs == 0) {
6715d522f475Smrg	return 0;
6716d522f475Smrg    }
6717d522f475Smrg#endif
6718d522f475Smrg
6719d522f475Smrg    if (opt_array == 0) {
6720cd3331d0Smrg	size_t opt_count, j;
6721d522f475Smrg#if OPT_TRACE
6722d522f475Smrg	Cardinal k;
6723d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
6724d522f475Smrg	int code;
6725cd3331d0Smrg	const char *mesg;
6726d522f475Smrg#else
6727d522f475Smrg	(void) descs;
6728d522f475Smrg	(void) numDescs;
6729d522f475Smrg#endif
6730d522f475Smrg
6731d522f475Smrg	/* count 'options' and make a sorted index to it */
6732d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
6733d522f475Smrg	    ;
6734d522f475Smrg	}
6735d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
6736d522f475Smrg	for (j = 0; j < opt_count; j++)
6737d522f475Smrg	    opt_array[j] = options[j];
6738d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
6739d522f475Smrg
6740d522f475Smrg	/* supply the "turn on/off" strings if needed */
6741d522f475Smrg#if OPT_TRACE
6742d522f475Smrg	for (j = 0; j < opt_count; j++) {
6743712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
6744c219fbebSmrg		char temp[80];
6745cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
6746d522f475Smrg		for (k = 0; k < numDescs; ++k) {
6747cd3331d0Smrg		    const char *value = res_array[k].value;
6748d522f475Smrg		    if (res_array[k].option[0] == '-') {
6749d522f475Smrg			code = -1;
6750d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
6751d522f475Smrg			code = 1;
6752d522f475Smrg		    } else {
6753d522f475Smrg			code = 0;
6754d522f475Smrg		    }
67553367019cSmrg		    sprintf(temp, "%.*s",
67563367019cSmrg			    (int) sizeof(temp) - 2,
67573367019cSmrg			    opt_array[j].desc);
6758c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
6759d522f475Smrg			code = -code;
6760d522f475Smrg		    if (code != 0
6761d522f475Smrg			&& res_array[k].value != 0
6762d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
6763d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
6764d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
6765d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
6766d522f475Smrg			    mesg = "turn on/off";
6767d522f475Smrg			} else {
6768d522f475Smrg			    mesg = "turn off/on";
6769d522f475Smrg			}
6770d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
6771712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
6772d4fba8b9Smrg				char *s = malloc(strlen(mesg)
6773d4fba8b9Smrg						 + strlen(opt_array[j].desc)
6774d4fba8b9Smrg						 + 2);
6775d522f475Smrg				if (s != 0) {
6776d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
6777d522f475Smrg				    opt_array[j].desc = s;
6778d522f475Smrg				}
6779d522f475Smrg			    } else {
6780d522f475Smrg				TRACE(("OOPS "));
6781d522f475Smrg			    }
6782d522f475Smrg			}
6783d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
6784d522f475Smrg			       mesg,
6785d522f475Smrg			       res_array[k].option,
6786d522f475Smrg			       res_array[k].value,
6787d522f475Smrg			       opt_array[j].opt,
6788d522f475Smrg			       opt_array[j].desc));
6789d522f475Smrg			break;
6790d522f475Smrg		    }
6791d522f475Smrg		}
6792d522f475Smrg	    }
6793d522f475Smrg	}
6794d522f475Smrg#endif
6795d522f475Smrg    }
6796d522f475Smrg    return opt_array;
6797d522f475Smrg}
6798d522f475Smrg
6799d522f475Smrg/*
6800d522f475Smrg * Report the character-type locale that xterm was started in.
6801d522f475Smrg */
68023367019cSmrgString
6803d522f475SmrgxtermEnvLocale(void)
6804d522f475Smrg{
68053367019cSmrg    static String result;
6806d522f475Smrg
6807d522f475Smrg    if (result == 0) {
6808d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
6809cd3331d0Smrg	    result = x_strdup("C");
6810cd3331d0Smrg	} else {
6811cd3331d0Smrg	    result = x_strdup(result);
6812d522f475Smrg	}
6813d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
6814d522f475Smrg    }
6815d522f475Smrg    return result;
6816d522f475Smrg}
6817d522f475Smrg
6818d522f475Smrgchar *
6819d522f475SmrgxtermEnvEncoding(void)
6820d522f475Smrg{
6821d522f475Smrg    static char *result;
6822d522f475Smrg
6823d522f475Smrg    if (result == 0) {
6824d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6825d522f475Smrg	result = nl_langinfo(CODESET);
6826d522f475Smrg#else
6827d4fba8b9Smrg	const char *locale = xtermEnvLocale();
6828d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
6829d4fba8b9Smrg	    result = x_strdup("ASCII");
6830d522f475Smrg	} else {
6831d4fba8b9Smrg	    result = x_strdup("ISO-8859-1");
6832d522f475Smrg	}
6833d522f475Smrg#endif
6834d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
6835d522f475Smrg    }
6836d522f475Smrg    return result;
6837d522f475Smrg}
6838d522f475Smrg
6839d522f475Smrg#if OPT_WIDE_CHARS
6840d522f475Smrg/*
6841d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
6842d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
6843d522f475Smrg * various library calls.
6844d522f475Smrg */
6845d522f475SmrgBool
6846d522f475SmrgxtermEnvUTF8(void)
6847d522f475Smrg{
6848d522f475Smrg    static Bool init = False;
6849d522f475Smrg    static Bool result = False;
6850d522f475Smrg
6851d522f475Smrg    if (!init) {
6852d522f475Smrg	init = True;
6853d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6854d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
6855d522f475Smrg#else
6856fa3f02f3Smrg	{
6857fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
6858fa3f02f3Smrg	    int n;
6859fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
6860fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
6861fa3f02f3Smrg	    }
6862fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
6863fa3f02f3Smrg		result = True;
6864fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
6865fa3f02f3Smrg		result = True;
6866fa3f02f3Smrg	    free(locale);
6867fa3f02f3Smrg	}
6868d522f475Smrg#endif
6869d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
6870d522f475Smrg    }
6871d522f475Smrg    return result;
6872d522f475Smrg}
6873d522f475Smrg#endif /* OPT_WIDE_CHARS */
6874d522f475Smrg
6875b7c89284Ssnj/*
6876b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
6877b7c89284Ssnj */
6878b7c89284SsnjXtermWidget
6879b7c89284SsnjgetXtermWidget(Widget w)
6880b7c89284Ssnj{
6881b7c89284Ssnj    XtermWidget xw;
6882b7c89284Ssnj
6883b7c89284Ssnj    if (w == 0) {
6884b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
6885b7c89284Ssnj	if (!IsXtermWidget(xw)) {
6886b7c89284Ssnj	    xw = 0;
6887b7c89284Ssnj	}
6888b7c89284Ssnj    } else if (IsXtermWidget(w)) {
6889b7c89284Ssnj	xw = (XtermWidget) w;
6890b7c89284Ssnj    } else {
6891b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
6892b7c89284Ssnj    }
6893b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
6894b7c89284Ssnj    return xw;
6895b7c89284Ssnj}
68963367019cSmrg
68973367019cSmrg#if OPT_SESSION_MGT
6898636d5e9fSmrg
6899636d5e9fSmrg#if OPT_TRACE
6900636d5e9fSmrgstatic void
6901636d5e9fSmrgtrace_1_SM(const char *tag, String name)
6902636d5e9fSmrg{
6903636d5e9fSmrg    Arg args[1];
6904636d5e9fSmrg    char *buf = 0;
6905636d5e9fSmrg
6906636d5e9fSmrg    XtSetArg(args[0], name, &buf);
6907636d5e9fSmrg    XtGetValues(toplevel, args, 1);
6908636d5e9fSmrg
6909636d5e9fSmrg    if (strstr(name, "Path") || strstr(name, "Directory")) {
6910636d5e9fSmrg	TRACE(("%s %s: %s\n", tag, name, NonNull(buf)));
6911636d5e9fSmrg    } else if (strstr(name, "Command")) {
6912636d5e9fSmrg	if (buf != NULL) {
6913636d5e9fSmrg	    char **vec = (char **) (void *) buf;
6914636d5e9fSmrg	    int n;
6915636d5e9fSmrg	    TRACE(("%s %s:\n", tag, name));
6916636d5e9fSmrg	    for (n = 0; vec[n] != NULL; ++n) {
6917636d5e9fSmrg		TRACE((" arg[%d] = %s\n", n, vec[n]));
6918636d5e9fSmrg	    }
6919636d5e9fSmrg	} else {
6920636d5e9fSmrg	    TRACE(("%s %s: %p\n", tag, name, buf));
6921636d5e9fSmrg	}
6922636d5e9fSmrg    } else {
6923636d5e9fSmrg	TRACE(("%s %s: %p\n", tag, name, buf));
6924636d5e9fSmrg    }
6925636d5e9fSmrg}
6926636d5e9fSmrg
6927636d5e9fSmrgstatic void
6928636d5e9fSmrgtrace_SM_props(void)
6929636d5e9fSmrg{
6930636d5e9fSmrg    /* *INDENT-OFF* */
6931636d5e9fSmrg    static struct { String app, cls; } table[] = {
6932636d5e9fSmrg	{ XtNcurrentDirectory,	XtCCurrentDirectory },
6933636d5e9fSmrg	{ XtNdieCallback,	XtNdiscardCommand },
6934636d5e9fSmrg	{ XtCDiscardCommand,	XtNenvironment },
6935636d5e9fSmrg	{ XtCEnvironment,	XtNinteractCallback },
6936636d5e9fSmrg	{ XtNjoinSession,	XtCJoinSession },
6937636d5e9fSmrg	{ XtNprogramPath,	XtCProgramPath },
6938636d5e9fSmrg	{ XtNresignCommand,	XtCResignCommand },
6939636d5e9fSmrg	{ XtNrestartCommand,	XtCRestartCommand },
6940636d5e9fSmrg	{ XtNrestartStyle,	XtCRestartStyle },
6941636d5e9fSmrg	{ XtNsaveCallback,	XtNsaveCompleteCallback },
6942636d5e9fSmrg	{ XtNsessionID,		XtCSessionID },
6943636d5e9fSmrg	{ XtNshutdownCommand,	XtCShutdownCommand },
6944636d5e9fSmrg    };
6945636d5e9fSmrg    /* *INDENT-ON* */
6946636d5e9fSmrg    Cardinal n;
6947636d5e9fSmrg    TRACE(("Session properties:\n"));
6948636d5e9fSmrg    for (n = 0; n < XtNumber(table); ++n) {
6949636d5e9fSmrg	trace_1_SM("app", table[n].app);
6950636d5e9fSmrg	trace_1_SM("cls", table[n].cls);
6951636d5e9fSmrg    }
6952636d5e9fSmrg}
6953636d5e9fSmrg#define TRACE_SM_PROPS()	trace_SM_props()
6954636d5e9fSmrg#else
6955636d5e9fSmrg#define TRACE_SM_PROPS()	/* nothing */
6956636d5e9fSmrg#endif
6957636d5e9fSmrg
69583367019cSmrgstatic void
69593367019cSmrgdie_callback(Widget w GCC_UNUSED,
69603367019cSmrg	     XtPointer client_data GCC_UNUSED,
69613367019cSmrg	     XtPointer call_data GCC_UNUSED)
69623367019cSmrg{
6963c48a5815Smrg    TRACE(("die_callback client=%p, call=%p\n",
6964c48a5815Smrg	   (void *) client_data,
6965c48a5815Smrg	   (void *) call_data));
6966636d5e9fSmrg    TRACE_SM_PROPS();
69673367019cSmrg    NormalExit();
69683367019cSmrg}
69693367019cSmrg
69703367019cSmrgstatic void
69713367019cSmrgsave_callback(Widget w GCC_UNUSED,
69723367019cSmrg	      XtPointer client_data GCC_UNUSED,
69733367019cSmrg	      XtPointer call_data)
69743367019cSmrg{
69753367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
6976636d5e9fSmrg    TRACE(("save_callback:\n"));
6977636d5e9fSmrg    TRACE(("... save_type            <-%d\n", token->save_type));
6978636d5e9fSmrg    TRACE(("... interact_style       <-%d\n", token->interact_style));
6979636d5e9fSmrg    TRACE(("... shutdown             <-%s\n", BtoS(token->shutdown)));
6980636d5e9fSmrg    TRACE(("... fast                 <-%s\n", BtoS(token->fast)));
6981636d5e9fSmrg    TRACE(("... cancel_shutdown      <-%s\n", BtoS(token->cancel_shutdown)));
6982636d5e9fSmrg    TRACE(("... phase                <-%d\n", token->phase));
6983636d5e9fSmrg    TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type));
6984636d5e9fSmrg    TRACE(("... request_cancel       ->%s\n", BtoS(token->request_cancel)));
6985636d5e9fSmrg    TRACE(("... request_next_phase   ->%s\n", BtoS(token->request_next_phase)));
6986636d5e9fSmrg    TRACE(("... save_success         ->%s\n", BtoS(token->save_success)));
6987636d5e9fSmrg    xtermUpdateRestartCommand(term);
6988636d5e9fSmrg    /* we have nothing more to save */
69893367019cSmrg    token->save_success = True;
69903367019cSmrg}
69913367019cSmrg
69923367019cSmrgstatic void
69933367019cSmrgicewatch(IceConn iceConn,
69943367019cSmrg	 IcePointer clientData GCC_UNUSED,
69953367019cSmrg	 Bool opening,
69963367019cSmrg	 IcePointer * watchData GCC_UNUSED)
69973367019cSmrg{
69983367019cSmrg    if (opening) {
69993367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
70003367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
70013367019cSmrg    } else {
70023367019cSmrg	ice_fd = -1;
70033367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
70043367019cSmrg    }
70053367019cSmrg}
70063367019cSmrg
70073367019cSmrgvoid
70083367019cSmrgxtermOpenSession(void)
70093367019cSmrg{
70103367019cSmrg    if (resource.sessionMgt) {
70113367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
70123367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
70133367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
7014636d5e9fSmrg
7015636d5e9fSmrg	TRACE_SM_PROPS();
70163367019cSmrg    }
70173367019cSmrg}
70183367019cSmrg
70193367019cSmrgvoid
70203367019cSmrgxtermCloseSession(void)
70213367019cSmrg{
70223367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
70233367019cSmrg}
7024636d5e9fSmrg
7025636d5e9fSmrgtypedef enum {
7026636d5e9fSmrg    B_ARG = 0,
7027636d5e9fSmrg    I_ARG,
7028636d5e9fSmrg    D_ARG,
7029636d5e9fSmrg    S_ARG
7030636d5e9fSmrg} ParamType;
7031636d5e9fSmrg
7032636d5e9fSmrg#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) }
7033636d5e9fSmrg#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) }
7034636d5e9fSmrg#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) }
7035636d5e9fSmrg#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) }
7036636d5e9fSmrg
7037636d5e9fSmrgtypedef struct {
7038636d5e9fSmrg    const char name[30];
7039636d5e9fSmrg    ParamType type;
7040636d5e9fSmrg    Cardinal offset;
7041636d5e9fSmrg} FontParams;
7042636d5e9fSmrg
7043636d5e9fSmrg/* *INDENT-OFF* */
7044636d5e9fSmrgstatic const FontParams fontParams[] = {
7045636d5e9fSmrg    Iarg(XtNinitialFont,     screen.menu_font_number),	/* "-fc" */
7046636d5e9fSmrg    Barg(XtNallowBoldFonts,  screen.allowBoldFonts),	/* menu */
7047636d5e9fSmrg#if OPT_BOX_CHARS
7048636d5e9fSmrg    Barg(XtNforceBoxChars,   screen.force_box_chars),	/* "-fbx" */
7049636d5e9fSmrg    Barg(XtNforcePackedFont, screen.force_packed),	/* menu */
7050636d5e9fSmrg#endif
7051636d5e9fSmrg#if OPT_DEC_CHRSET
7052636d5e9fSmrg    Barg(XtNfontDoublesize,  screen.font_doublesize),	/* menu */
7053636d5e9fSmrg#endif
7054636d5e9fSmrg#if OPT_WIDE_CHARS
7055636d5e9fSmrg    Barg(XtNutf8Fonts,       screen.utf8_fonts),	/* menu */
7056636d5e9fSmrg#endif
7057636d5e9fSmrg#if OPT_RENDERFONT
7058636d5e9fSmrg    Darg(XtNfaceSize,        misc.face_size[0]),	/* "-fs" */
7059636d5e9fSmrg    Sarg(XtNfaceName,        misc.default_xft.f_n),	/* "-fa" */
7060636d5e9fSmrg    Sarg(XtNrenderFont,      misc.render_font_s),	/* (resource) */
7061636d5e9fSmrg#endif
7062636d5e9fSmrg};
7063636d5e9fSmrg/* *INDENT-ON* */
7064636d5e9fSmrg
7065636d5e9fSmrg#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2)
7066636d5e9fSmrg#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset)
7067636d5e9fSmrg
7068636d5e9fSmrg/*
7069636d5e9fSmrg * If no widget is given, no value is used.
7070636d5e9fSmrg */
7071636d5e9fSmrgstatic char *
7072636d5e9fSmrgformatFontParam(char *result, XtermWidget xw, const FontParams * parameter)
7073636d5e9fSmrg{
7074636d5e9fSmrg    sprintf(result, "%s*%s:", ProgramName, parameter->name);
7075636d5e9fSmrg    if (xw != None) {
7076636d5e9fSmrg	char *next = result + strlen(result);
7077636d5e9fSmrg	switch (parameter->type) {
7078636d5e9fSmrg	case B_ARG:
7079636d5e9fSmrg	    sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset)
7080636d5e9fSmrg		    ? "true"
7081636d5e9fSmrg		    : "false");
7082636d5e9fSmrg	    break;
7083636d5e9fSmrg	case I_ARG:
7084636d5e9fSmrg	    sprintf(next, "%d", TypedPtr(int));
7085636d5e9fSmrg	    break;
7086636d5e9fSmrg	case D_ARG:
7087636d5e9fSmrg	    sprintf(next, "%.1f", TypedPtr(float));
7088636d5e9fSmrg	    break;
7089636d5e9fSmrg	case S_ARG:
7090636d5e9fSmrg	    strcpy(next, TypedPtr(char *));
7091636d5e9fSmrg#if OPT_RENDERFONT
7092636d5e9fSmrg	    if (!strcmp(parameter->name, XtNfaceName)) {
7093636d5e9fSmrg		if (IsEmpty(next)
7094636d5e9fSmrg		    && xw->work.render_font) {
7095636d5e9fSmrg		    strcpy(next, DEFFACENAME_AUTO);
7096636d5e9fSmrg		}
7097636d5e9fSmrg	    } else if (!strcmp(parameter->name, XtNrenderFont)) {
7098636d5e9fSmrg		if (xw->work.render_font == erDefault
7099636d5e9fSmrg		    && IsEmpty(xw->misc.default_xft.f_n)) {
7100636d5e9fSmrg		    strcpy(next, "DefaultOff");
7101636d5e9fSmrg		}
7102636d5e9fSmrg	    }
7103636d5e9fSmrg#endif
7104636d5e9fSmrg	    break;
7105636d5e9fSmrg	}
7106636d5e9fSmrg    }
7107636d5e9fSmrg    return result;
7108636d5e9fSmrg}
7109636d5e9fSmrg
7110636d5e9fSmrg#if OPT_TRACE
7111636d5e9fSmrgstatic void
7112636d5e9fSmrgdumpFontParams(XtermWidget xw)
7113636d5e9fSmrg{
7114636d5e9fSmrg    char buffer[1024];
7115636d5e9fSmrg    Cardinal n;
7116636d5e9fSmrg
7117636d5e9fSmrg    TRACE(("FontParams:\n"));
7118636d5e9fSmrg    for (n = 0; n < XtNumber(fontParams); ++n) {
7119636d5e9fSmrg	TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n)));
7120636d5e9fSmrg    }
7121636d5e9fSmrg}
7122636d5e9fSmrg#else
7123636d5e9fSmrg#define dumpFontParams(xw)	/* nothing */
7124636d5e9fSmrg#endif
7125636d5e9fSmrg
7126636d5e9fSmrgstatic Boolean
7127636d5e9fSmrgfindFontParams(int argc, char **argv)
7128636d5e9fSmrg{
7129636d5e9fSmrg    Boolean result = False;
7130636d5e9fSmrg
7131636d5e9fSmrg    if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) {
7132636d5e9fSmrg	int n;
7133636d5e9fSmrg
7134636d5e9fSmrg	for (n = 0; n < RESTART_PARAMS; ++n) {
7135636d5e9fSmrg	    int my_index = argc - restart_params - n - 1;
7136636d5e9fSmrg	    int my_param = (RESTART_PARAMS - n - 1) / 2;
7137636d5e9fSmrg	    char *actual = argv[my_index];
7138636d5e9fSmrg	    char expect[1024];
7139636d5e9fSmrg	    Boolean value = (Boolean) ((n % 2) == 0);
7140636d5e9fSmrg
7141636d5e9fSmrg	    result = False;
7142636d5e9fSmrg	    TRACE(("...index: %d\n", my_index));
7143636d5e9fSmrg	    TRACE(("...param: %d\n", my_param));
7144636d5e9fSmrg	    TRACE(("...actual %s\n", actual));
7145636d5e9fSmrg	    if (IsEmpty(actual))
7146636d5e9fSmrg		break;
7147636d5e9fSmrg
7148636d5e9fSmrg	    if (value) {
7149636d5e9fSmrg		formatFontParam(expect, None, fontParams + my_param);
7150636d5e9fSmrg	    } else {
7151636d5e9fSmrg		strcpy(expect, "-xrm");
7152636d5e9fSmrg	    }
7153636d5e9fSmrg
7154636d5e9fSmrg	    TRACE(("...expect %s\n", expect));
7155636d5e9fSmrg
7156636d5e9fSmrg	    if (value) {
7157636d5e9fSmrg		if (strlen(expect) >= strlen(actual))
7158636d5e9fSmrg		    break;
7159636d5e9fSmrg		if (strncmp(expect, actual, strlen(expect)))
7160636d5e9fSmrg		    break;
7161636d5e9fSmrg	    } else {
7162636d5e9fSmrg		if (strcmp(actual, expect))
7163636d5e9fSmrg		    break;
7164636d5e9fSmrg	    }
7165636d5e9fSmrg	    TRACE(("fixme/ok:%d\n", n));
7166636d5e9fSmrg	    result = True;
7167636d5e9fSmrg	}
7168636d5e9fSmrg	TRACE(("findFontParams: %s (tested %d of %d parameters)\n",
7169636d5e9fSmrg	       BtoS(result), n + 1, RESTART_PARAMS));
7170636d5e9fSmrg    }
7171636d5e9fSmrg    return result;
7172636d5e9fSmrg}
7173636d5e9fSmrg
7174636d5e9fSmrgstatic int
7175c48a5815SmrginsertFontParams(XtermWidget xw, int *targetp, Bool first)
7176636d5e9fSmrg{
7177636d5e9fSmrg    int changed = 0;
7178636d5e9fSmrg    int n;
7179636d5e9fSmrg    int target = *targetp;
7180636d5e9fSmrg    char buffer[1024];
7181636d5e9fSmrg    const char *option = "-xrm";
7182636d5e9fSmrg
7183636d5e9fSmrg    for (n = 0; n < (int) XtNumber(fontParams); ++n) {
7184636d5e9fSmrg	formatFontParam(buffer, xw, fontParams + n);
7185636d5e9fSmrg	TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer));
7186636d5e9fSmrg	if (restart_command[target] == NULL)
7187636d5e9fSmrg	    restart_command[target] = x_strdup(option);
7188636d5e9fSmrg	++target;
7189636d5e9fSmrg	if (first) {
7190636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7191636d5e9fSmrg	    ++changed;
7192636d5e9fSmrg	} else if (restart_command[target] == NULL
7193636d5e9fSmrg		   || strcmp(restart_command[target], buffer)) {
7194636d5e9fSmrg	    free(restart_command[target]);
7195636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7196636d5e9fSmrg	    ++changed;
7197636d5e9fSmrg	}
7198636d5e9fSmrg	++target;
7199636d5e9fSmrg    }
7200636d5e9fSmrg    *targetp = target;
7201636d5e9fSmrg    return changed;
7202636d5e9fSmrg}
7203636d5e9fSmrg
7204636d5e9fSmrgvoid
7205636d5e9fSmrgxtermUpdateRestartCommand(XtermWidget xw)
7206636d5e9fSmrg{
7207636d5e9fSmrg    if (resource.sessionMgt) {
7208636d5e9fSmrg	Arg args[1];
7209636d5e9fSmrg	char **argv = 0;
7210636d5e9fSmrg
7211636d5e9fSmrg	XtSetArg(args[0], XtNrestartCommand, &argv);
7212636d5e9fSmrg	XtGetValues(toplevel, args, 1);
7213636d5e9fSmrg	if (argv != NULL) {
7214636d5e9fSmrg	    static int my_params = 0;
7215636d5e9fSmrg
7216636d5e9fSmrg	    int changes = 0;
7217636d5e9fSmrg	    Boolean first = False;
7218636d5e9fSmrg	    int argc;
7219636d5e9fSmrg	    int want;
7220636d5e9fSmrg	    int source, target;
7221636d5e9fSmrg
7222636d5e9fSmrg	    TRACE(("xtermUpdateRestartCommand\n"));
7223636d5e9fSmrg	    dumpFontParams(xw);
7224636d5e9fSmrg	    for (argc = 0; argv[argc] != NULL; ++argc) {
7225636d5e9fSmrg		TRACE((" arg[%d] = %s\n", argc, argv[argc]));
7226636d5e9fSmrg		;
7227636d5e9fSmrg	    }
7228636d5e9fSmrg	    want = argc - (restart_params + RESTART_PARAMS);
7229636d5e9fSmrg
7230636d5e9fSmrg	    TRACE((" argc:           %d\n", argc));
7231636d5e9fSmrg	    TRACE((" restart_params: %d\n", restart_params));
7232636d5e9fSmrg	    TRACE((" want to insert: %d\n", want));
7233636d5e9fSmrg
7234636d5e9fSmrg	    /*
7235636d5e9fSmrg	     * If we already have the font-choice option, do not add it again.
7236636d5e9fSmrg	     */
7237636d5e9fSmrg	    if (findFontParams(argc, argv)) {
7238636d5e9fSmrg		my_params = (want);
7239636d5e9fSmrg	    } else {
7240636d5e9fSmrg		first = True;
7241636d5e9fSmrg		my_params = (argc - restart_params);
7242636d5e9fSmrg	    }
7243636d5e9fSmrg	    TRACE((" my_params:      %d\n", my_params));
7244636d5e9fSmrg
7245636d5e9fSmrg	    if (my_params > argc) {
7246636d5e9fSmrg		TRACE((" re-allocate restartCommand\n"));
7247636d5e9fSmrg		FreeAndNull(restart_command);
7248636d5e9fSmrg	    }
7249636d5e9fSmrg
7250636d5e9fSmrg	    if (restart_command == NULL) {
7251636d5e9fSmrg		int need = argc + RESTART_PARAMS + 1;
7252636d5e9fSmrg
7253636d5e9fSmrg		restart_command = TypeCallocN(char *, need);
7254636d5e9fSmrg
7255636d5e9fSmrg		TRACE(("..inserting font-parameters\n"));
7256636d5e9fSmrg		for (source = target = 0; source < argc; ++source) {
7257636d5e9fSmrg		    if (source == my_params) {
7258636d5e9fSmrg			changes += insertFontParams(xw, &target, first);
7259636d5e9fSmrg			if (!first) {
7260636d5e9fSmrg			    source += (RESTART_PARAMS - 1);
7261636d5e9fSmrg			    continue;
7262636d5e9fSmrg			}
7263636d5e9fSmrg		    }
7264636d5e9fSmrg		    if (argv[source] == NULL)
7265636d5e9fSmrg			break;
7266636d5e9fSmrg		    restart_command[target++] = x_strdup(argv[source]);
7267636d5e9fSmrg		}
7268636d5e9fSmrg		restart_command[target] = NULL;
7269636d5e9fSmrg	    } else {
7270636d5e9fSmrg		TRACE(("..replacing font-parameters\n"));
7271636d5e9fSmrg		target = my_params;
7272636d5e9fSmrg		changes += insertFontParams(xw, &target, first);
7273636d5e9fSmrg	    }
7274636d5e9fSmrg	    if (changes) {
7275636d5e9fSmrg		TRACE(("..%d parameters changed\n", changes));
7276636d5e9fSmrg		XtSetArg(args[0], XtNrestartCommand, restart_command);
7277636d5e9fSmrg		XtSetValues(toplevel, args, 1);
7278636d5e9fSmrg	    } else {
7279636d5e9fSmrg		TRACE(("..NO parameters changed\n"));
7280636d5e9fSmrg	    }
7281636d5e9fSmrg	}
7282636d5e9fSmrg	TRACE_SM_PROPS();
7283636d5e9fSmrg    }
7284636d5e9fSmrg}
72853367019cSmrg#endif /* OPT_SESSION_MGT */
72863367019cSmrg
72873367019cSmrgWidget
72883367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
72893367019cSmrg		     String my_class,
72903367019cSmrg		     XrmOptionDescRec * options,
72913367019cSmrg		     Cardinal num_options,
72923367019cSmrg		     int *argc_in_out,
7293d4fba8b9Smrg		     char **argv_in_out,
7294fa3f02f3Smrg		     String *fallback_resources,
72953367019cSmrg		     WidgetClass widget_class,
72963367019cSmrg		     ArgList args,
72973367019cSmrg		     Cardinal num_args)
72983367019cSmrg{
72993367019cSmrg    Widget result;
73003367019cSmrg
73013367019cSmrg    XtSetErrorHandler(xt_error);
73023367019cSmrg#if OPT_SESSION_MGT
73033367019cSmrg    result = XtOpenApplication(app_context_return,
73043367019cSmrg			       my_class,
73053367019cSmrg			       options,
73063367019cSmrg			       num_options,
73073367019cSmrg			       argc_in_out,
73083367019cSmrg			       argv_in_out,
73093367019cSmrg			       fallback_resources,
73103367019cSmrg			       widget_class,
73113367019cSmrg			       args,
73123367019cSmrg			       num_args);
73133367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
73143367019cSmrg#else
73159a64e1c5Smrg    (void) widget_class;
73169a64e1c5Smrg    (void) args;
73179a64e1c5Smrg    (void) num_args;
73183367019cSmrg    result = XtAppInitialize(app_context_return,
73193367019cSmrg			     my_class,
73203367019cSmrg			     options,
73213367019cSmrg			     num_options,
73223367019cSmrg			     argc_in_out,
73233367019cSmrg			     argv_in_out,
73243367019cSmrg			     fallback_resources,
73253367019cSmrg			     NULL, 0);
73263367019cSmrg#endif /* OPT_SESSION_MGT */
7327e8264990Smrg    XtSetErrorHandler(NULL);
73283367019cSmrg
73293367019cSmrg    return result;
73303367019cSmrg}
73313367019cSmrg
7332d4fba8b9Smrg/*
7333d4fba8b9Smrg * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
7334d4fba8b9Smrg * our own error-handler.
7335d4fba8b9Smrg */
7336d4fba8b9Smrg/* ARGSUSED */
7337d4fba8b9Smrgint
7338d4fba8b9Smrgignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
7339d4fba8b9Smrg{
7340d4fba8b9Smrg    return 1;
7341d4fba8b9Smrg}
7342d4fba8b9Smrg
73433367019cSmrgstatic int x11_errors;
73443367019cSmrg
73453367019cSmrgstatic int
73469a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
73473367019cSmrg{
73483367019cSmrg    (void) display;
73493367019cSmrg    (void) error_event;
73503367019cSmrg    ++x11_errors;
73513367019cSmrg    return 0;
73523367019cSmrg}
73533367019cSmrg
73543367019cSmrgBoolean
7355fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
73563367019cSmrg{
73573367019cSmrg    Boolean result = False;
73583367019cSmrg    Status code;
73593367019cSmrg
73603367019cSmrg    memset(attrs, 0, sizeof(*attrs));
73613367019cSmrg    if (win != None) {
73623367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
73633367019cSmrg	x11_errors = 0;
73643367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
73653367019cSmrg	XSetErrorHandler(save);
73663367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
73673367019cSmrg	if (result) {
73683367019cSmrg	    TRACE_WIN_ATTRS(attrs);
73693367019cSmrg	} else {
73703367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
73713367019cSmrg	}
73723367019cSmrg    }
73733367019cSmrg    return result;
73743367019cSmrg}
73753367019cSmrg
73763367019cSmrgBoolean
7377fa3f02f3SmrgxtermGetWinProp(Display *display,
73783367019cSmrg		Window win,
73793367019cSmrg		Atom property,
73803367019cSmrg		long long_offset,
73813367019cSmrg		long long_length,
73823367019cSmrg		Atom req_type,
73839a64e1c5Smrg		Atom *actual_type_return,
73843367019cSmrg		int *actual_format_return,
73853367019cSmrg		unsigned long *nitems_return,
73863367019cSmrg		unsigned long *bytes_after_return,
73873367019cSmrg		unsigned char **prop_return)
73883367019cSmrg{
7389d4fba8b9Smrg    Boolean result = False;
73903367019cSmrg
73913367019cSmrg    if (win != None) {
73923367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
73933367019cSmrg	x11_errors = 0;
73943367019cSmrg	if (XGetWindowProperty(display,
73953367019cSmrg			       win,
73963367019cSmrg			       property,
73973367019cSmrg			       long_offset,
73983367019cSmrg			       long_length,
73993367019cSmrg			       False,
74003367019cSmrg			       req_type,
74013367019cSmrg			       actual_type_return,
74023367019cSmrg			       actual_format_return,
74033367019cSmrg			       nitems_return,
74043367019cSmrg			       bytes_after_return,
74053367019cSmrg			       prop_return) == Success
74063367019cSmrg	    && x11_errors == 0) {
74073367019cSmrg	    result = True;
74083367019cSmrg	}
74093367019cSmrg	XSetErrorHandler(save);
74103367019cSmrg    }
74113367019cSmrg    return result;
74123367019cSmrg}
74133367019cSmrg
74143367019cSmrgvoid
74153367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
74163367019cSmrg{
74173367019cSmrg    Display *dpy = XtDisplay(toplevel);
74183367019cSmrg    XWindowAttributes attrs;
74193367019cSmrg
74203367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
74213367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
74223367019cSmrg	XtermWidget xw = term;
74233367019cSmrg	TScreen *screen = TScreenOf(xw);
74243367019cSmrg
74253367019cSmrg	XtRealizeWidget(toplevel);
74263367019cSmrg
74273367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
74283367019cSmrg	       XtWindow(toplevel),
74293367019cSmrg	       winToEmbedInto));
74303367019cSmrg	XReparentWindow(dpy,
74313367019cSmrg			XtWindow(toplevel),
74323367019cSmrg			winToEmbedInto, 0, 0);
74333367019cSmrg
74343367019cSmrg	screen->embed_high = (Dimension) attrs.height;
74353367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
74363367019cSmrg    }
74373367019cSmrg}
743894644356Smrg
743994644356Smrgvoid
744094644356Smrgfree_string(String value)
744194644356Smrg{
744294644356Smrg    free((void *) value);
744394644356Smrg}
7444dfb07bc7Smrg
7445dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
7446d4fba8b9Smrgint
7447dfb07bc7Smrgupdate_winsize(int fd, int rows, int cols, int height, int width)
7448dfb07bc7Smrg{
7449d4fba8b9Smrg    int code = -1;
7450dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
7451d4fba8b9Smrg    static int last_rows = -1;
7452d4fba8b9Smrg    static int last_cols = -1;
7453636d5e9fSmrg    static int last_high = -1;
7454636d5e9fSmrg    static int last_wide = -1;
7455636d5e9fSmrg
7456636d5e9fSmrg    TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n",
7457636d5e9fSmrg	   last_rows, last_cols, last_high, last_wide,
7458636d5e9fSmrg	   rows, cols, height, width));
7459dfb07bc7Smrg
7460636d5e9fSmrg    if (rows != last_rows
7461636d5e9fSmrg	|| cols != last_cols
7462636d5e9fSmrg	|| last_high != height
7463636d5e9fSmrg	|| last_wide != width) {
7464d4fba8b9Smrg	TTYSIZE_STRUCT ts;
7465d4fba8b9Smrg
7466d4fba8b9Smrg	last_rows = rows;
7467d4fba8b9Smrg	last_cols = cols;
7468636d5e9fSmrg	last_high = height;
7469636d5e9fSmrg	last_wide = width;
7470d4fba8b9Smrg	setup_winsize(ts, rows, cols, height, width);
7471d4fba8b9Smrg	TRACE_RC(code, SET_TTYSIZE(fd, ts));
7472d4fba8b9Smrg	trace_winsize(ts, "from SET_TTYSIZE");
7473d4fba8b9Smrg    }
7474dfb07bc7Smrg#endif
7475dfb07bc7Smrg
7476dfb07bc7Smrg    (void) rows;
7477dfb07bc7Smrg    (void) cols;
7478dfb07bc7Smrg    (void) height;
7479dfb07bc7Smrg    (void) width;
7480d4fba8b9Smrg
7481d4fba8b9Smrg    return code;
7482dfb07bc7Smrg}
7483dfb07bc7Smrg
7484dfb07bc7Smrg/*
7485dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
7486dfb07bc7Smrg * manipulation 18 and 19.
7487dfb07bc7Smrg */
7488dfb07bc7Smrgvoid
7489dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
7490dfb07bc7Smrg{
7491dfb07bc7Smrg#if OPT_TEK4014
7492dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
7493dfb07bc7Smrg#endif
7494dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
7495dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
7496dfb07bc7Smrg
7497dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
7498dfb07bc7Smrg	    update_winsize(screen->respond,
7499dfb07bc7Smrg			   MaxRows(screen),
7500dfb07bc7Smrg			   MaxCols(screen),
7501dfb07bc7Smrg			   Height(screen),
7502dfb07bc7Smrg			   Width(screen));
7503dfb07bc7Smrg	}
7504dfb07bc7Smrg}
7505d4fba8b9Smrg
7506d4fba8b9Smrg#if OPT_XTERM_SGR
7507d4fba8b9Smrg
7508d4fba8b9Smrg#if OPT_TRACE
7509d4fba8b9Smrgstatic char *
7510d4fba8b9SmrgtraceIFlags(IFlags flags)
7511d4fba8b9Smrg{
7512d4fba8b9Smrg    static char result[1000];
7513d4fba8b9Smrg    result[0] = '\0';
7514d4fba8b9Smrg#define DATA(name) if (flags & name) { strcat(result, " " #name); }
7515d4fba8b9Smrg    DATA(INVERSE);
7516d4fba8b9Smrg    DATA(UNDERLINE);
7517d4fba8b9Smrg    DATA(BOLD);
7518d4fba8b9Smrg    DATA(BLINK);
7519d4fba8b9Smrg    DATA(INVISIBLE);
7520d4fba8b9Smrg    DATA(BG_COLOR);
7521d4fba8b9Smrg    DATA(FG_COLOR);
7522d4fba8b9Smrg
7523d4fba8b9Smrg#if OPT_WIDE_ATTRS
7524d4fba8b9Smrg    DATA(ATR_FAINT);
7525d4fba8b9Smrg    DATA(ATR_ITALIC);
7526d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7527d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7528d4fba8b9Smrg    DATA(ATR_DIRECT_FG);
7529d4fba8b9Smrg    DATA(ATR_DIRECT_BG);
7530d4fba8b9Smrg#endif
7531d4fba8b9Smrg#undef DATA
7532d4fba8b9Smrg    return result;
7533d4fba8b9Smrg}
7534d4fba8b9Smrg
7535d4fba8b9Smrgstatic char *
7536d4fba8b9SmrgtraceIStack(unsigned flags)
7537d4fba8b9Smrg{
7538d4fba8b9Smrg    static char result[1000];
7539d4fba8b9Smrg    result[0] = '\0';
7540d4fba8b9Smrg#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7541d4fba8b9Smrg    DATA(INVERSE);
7542d4fba8b9Smrg    DATA(UNDERLINE);
7543d4fba8b9Smrg    DATA(BOLD);
7544d4fba8b9Smrg    DATA(BLINK);
7545d4fba8b9Smrg    DATA(INVISIBLE);
7546d4fba8b9Smrg#if OPT_ISO_COLORS
7547d4fba8b9Smrg    DATA(BG_COLOR);
7548d4fba8b9Smrg    DATA(FG_COLOR);
7549d4fba8b9Smrg#endif
7550d4fba8b9Smrg
7551d4fba8b9Smrg#if OPT_WIDE_ATTRS
7552d4fba8b9Smrg    DATA(ATR_FAINT);
7553d4fba8b9Smrg    DATA(ATR_ITALIC);
7554d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7555d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7556d4fba8b9Smrg    /* direct-colors are a special case of ISO-colors (see above) */
7557d4fba8b9Smrg#endif
7558d4fba8b9Smrg#undef DATA
7559d4fba8b9Smrg    return result;
7560d4fba8b9Smrg}
7561d4fba8b9Smrg#endif
7562d4fba8b9Smrg
7563d4fba8b9Smrgvoid
7564d4fba8b9SmrgxtermPushSGR(XtermWidget xw, int value)
7565d4fba8b9Smrg{
7566d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7567d4fba8b9Smrg
7568d4fba8b9Smrg    TRACE(("xtermPushSGR %d mask %#x %s\n",
7569d4fba8b9Smrg	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
7570d4fba8b9Smrg
7571d4fba8b9Smrg    if (s->used < MAX_SAVED_SGR) {
7572d4fba8b9Smrg	s->stack[s->used].mask = (IFlags) value;
7573d4fba8b9Smrg#define PUSH_FLAG(name) \
7574d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7575d4fba8b9Smrg	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
7576d4fba8b9Smrg#define PUSH_DATA(name) \
7577d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7578d4fba8b9Smrg	    TRACE(("...may pop %s %d\n", #name, xw->name))
7579d4fba8b9Smrg	PUSH_FLAG(flags);
7580d4fba8b9Smrg#if OPT_ISO_COLORS
7581d4fba8b9Smrg	PUSH_DATA(sgr_foreground);
7582d4fba8b9Smrg	PUSH_DATA(sgr_background);
7583d4fba8b9Smrg	PUSH_DATA(sgr_38_xcolors);
7584d4fba8b9Smrg#endif
7585d4fba8b9Smrg    }
7586d4fba8b9Smrg    s->used++;
7587d4fba8b9Smrg}
7588d4fba8b9Smrg
7589d4fba8b9Smrg#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
7590d4fba8b9Smrg
7591d4fba8b9Smrgvoid
7592d4fba8b9SmrgxtermReportSGR(XtermWidget xw, XTermRect *value)
7593d4fba8b9Smrg{
7594d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
7595d4fba8b9Smrg    char reply[BUFSIZ];
7596d4fba8b9Smrg    CellData working;
7597d4fba8b9Smrg    int row, col;
7598d4fba8b9Smrg    Boolean first = True;
7599d4fba8b9Smrg
7600d4fba8b9Smrg    TRACE(("xtermReportSGR %d,%d - %d,%d\n",
7601d4fba8b9Smrg	   value->top, value->left,
7602d4fba8b9Smrg	   value->bottom, value->right));
7603d4fba8b9Smrg
7604d4fba8b9Smrg    memset(&working, 0, sizeof(working));
7605d4fba8b9Smrg    for (row = value->top - 1; row < value->bottom; ++row) {
7606d4fba8b9Smrg	LineData *ld = getLineData(screen, row);
7607d4fba8b9Smrg	if (ld == 0)
7608d4fba8b9Smrg	    continue;
7609d4fba8b9Smrg	for (col = value->left - 1; col < value->right; ++col) {
7610d4fba8b9Smrg	    if (first) {
7611d4fba8b9Smrg		first = False;
7612d4fba8b9Smrg		saveCellData(screen, &working, 0, ld, NULL, col);
7613d4fba8b9Smrg	    }
7614d4fba8b9Smrg	    working.attribs &= ld->attribs[col];
7615d4fba8b9Smrg#if OPT_ISO_COLORS
7616d4fba8b9Smrg	    if (working.attribs & FG_COLOR
7617d4fba8b9Smrg		&& GetCellColorFG(working.color)
7618d4fba8b9Smrg		!= GetCellColorFG(ld->color[col])) {
7619d4fba8b9Smrg		IAttrClr(working.attribs, FG_COLOR);
7620d4fba8b9Smrg	    }
7621d4fba8b9Smrg	    if (working.attribs & BG_COLOR
7622d4fba8b9Smrg		&& GetCellColorBG(working.color)
7623d4fba8b9Smrg		!= GetCellColorBG(ld->color[col])) {
7624d4fba8b9Smrg		IAttrClr(working.attribs, BG_COLOR);
7625d4fba8b9Smrg	    }
7626d4fba8b9Smrg#endif
7627d4fba8b9Smrg	}
7628d4fba8b9Smrg    }
7629d4fba8b9Smrg    xtermFormatSGR(xw, reply,
7630d4fba8b9Smrg		   working.attribs,
7631d4fba8b9Smrg		   GetCellColorFG(working.color),
7632d4fba8b9Smrg		   GetCellColorBG(working.color));
7633d4fba8b9Smrg    unparseputc1(xw, ANSI_CSI);
7634d4fba8b9Smrg    unparseputs(xw, reply);
7635d4fba8b9Smrg    unparseputc(xw, 'm');
7636d4fba8b9Smrg    unparse_end(xw);
7637d4fba8b9Smrg}
7638d4fba8b9Smrg
7639d4fba8b9Smrgvoid
7640d4fba8b9SmrgxtermPopSGR(XtermWidget xw)
7641d4fba8b9Smrg{
7642d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7643d4fba8b9Smrg
7644d4fba8b9Smrg    TRACE(("xtermPopSGR %d\n", s->used));
7645d4fba8b9Smrg
7646d4fba8b9Smrg    if (s->used > 0) {
7647d4fba8b9Smrg	if (s->used-- <= MAX_SAVED_SGR) {
7648d4fba8b9Smrg	    IFlags mask = s->stack[s->used].mask;
7649d4fba8b9Smrg	    Boolean changed = False;
7650d4fba8b9Smrg
7651d4fba8b9Smrg	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
7652d4fba8b9Smrg	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
7653d4fba8b9Smrg	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
7654d4fba8b9Smrg#define POP_FLAG(name) \
7655d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7656d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7657d4fba8b9Smrg		    changed = True; \
7658d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7659d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7660d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7661d4fba8b9Smrg		} \
7662d4fba8b9Smrg	    }
7663d4fba8b9Smrg#define POP_FLAG2(name,part) \
7664d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7665d4fba8b9Smrg	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
7666d4fba8b9Smrg		    changed = True; \
7667d4fba8b9Smrg		    UIntClr(xw->flags, part); \
7668d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
7669d4fba8b9Smrg		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
7670d4fba8b9Smrg		} \
7671d4fba8b9Smrg	    }
7672d4fba8b9Smrg#define POP_DATA(name,value) \
7673d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7674d4fba8b9Smrg	        Bool always = False; \
7675d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7676d4fba8b9Smrg		    always = changed = True; \
7677d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7678d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7679d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7680d4fba8b9Smrg		} \
7681d4fba8b9Smrg		if (always || (xw->value != s->stack[s->used].value)) { \
7682d4fba8b9Smrg		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
7683d4fba8b9Smrg		    xw->value = s->stack[s->used].value; \
7684d4fba8b9Smrg		    changed = True; \
7685d4fba8b9Smrg		} \
7686d4fba8b9Smrg	    }
7687d4fba8b9Smrg	    POP_FLAG(BOLD);
7688d4fba8b9Smrg	    POP_FLAG(UNDERLINE);
7689d4fba8b9Smrg	    POP_FLAG(BLINK);
7690d4fba8b9Smrg	    POP_FLAG(INVERSE);
7691d4fba8b9Smrg	    POP_FLAG(INVISIBLE);
7692d4fba8b9Smrg#if OPT_WIDE_ATTRS
7693d4fba8b9Smrg	    if (xBIT(psATR_ITALIC - 1) & mask) {
7694d4fba8b9Smrg		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
7695d4fba8b9Smrg	    }
7696d4fba8b9Smrg	    POP_FLAG(ATR_ITALIC);
7697d4fba8b9Smrg	    POP_FLAG(ATR_FAINT);
7698d4fba8b9Smrg	    POP_FLAG(ATR_STRIKEOUT);
7699d4fba8b9Smrg	    POP_FLAG(ATR_DBL_UNDER);
7700d4fba8b9Smrg#endif
7701d4fba8b9Smrg#if OPT_ISO_COLORS
7702d4fba8b9Smrg	    POP_DATA(FG_COLOR, sgr_foreground);
7703d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_background);
7704d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_38_xcolors);
7705d4fba8b9Smrg#if OPT_DIRECT_COLOR
7706d4fba8b9Smrg	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
7707d4fba8b9Smrg	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
7708d4fba8b9Smrg#endif
7709d4fba8b9Smrg	    if (changed) {
7710d4fba8b9Smrg		setExtendedColors(xw);
7711d4fba8b9Smrg	    }
7712d4fba8b9Smrg#else
7713d4fba8b9Smrg	    (void) changed;
7714d4fba8b9Smrg#endif
7715d4fba8b9Smrg	}
7716d4fba8b9Smrg#if OPT_ISO_COLORS
7717d4fba8b9Smrg	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
7718d4fba8b9Smrg	       traceIFlags(xw->flags),
7719d4fba8b9Smrg	       xw->sgr_foreground,
7720d4fba8b9Smrg	       xw->sgr_background,
7721d4fba8b9Smrg	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
7722d4fba8b9Smrg#else
7723d4fba8b9Smrg	TRACE(("xtermP -> flags%s\n",
7724d4fba8b9Smrg	       traceIFlags(xw->flags)));
7725d4fba8b9Smrg#endif
7726d4fba8b9Smrg    }
7727d4fba8b9Smrg}
7728d4fba8b9Smrg
7729d4fba8b9Smrg#if OPT_ISO_COLORS
7730d4fba8b9Smrgstatic ColorSlot *
7731d4fba8b9SmrgallocColorSlot(XtermWidget xw, int slot)
7732d4fba8b9Smrg{
7733d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7734d4fba8b9Smrg    ColorSlot *result = NULL;
7735d4fba8b9Smrg
7736d4fba8b9Smrg    if (slot >= 0 && slot < MAX_SAVED_SGR) {
7737d4fba8b9Smrg	if (s->palettes[slot] == NULL) {
7738d4fba8b9Smrg	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
7739d4fba8b9Smrg						     sizeof(ColorSlot)
7740d4fba8b9Smrg						     + (sizeof(ColorRes)
7741d4fba8b9Smrg							* MAXCOLORS));
7742d4fba8b9Smrg	}
7743d4fba8b9Smrg	result = s->palettes[slot];
7744d4fba8b9Smrg    }
7745d4fba8b9Smrg    return result;
7746d4fba8b9Smrg}
7747d4fba8b9Smrg
7748d4fba8b9Smrgstatic void
7749d4fba8b9SmrgpopOldColors(XtermWidget xw, ScrnColors * source)
7750d4fba8b9Smrg{
7751d4fba8b9Smrg    Boolean changed = False;
7752d4fba8b9Smrg    ScrnColors *target = xw->work.oldColors;
7753d4fba8b9Smrg
7754d4fba8b9Smrg    if (source->which != target->which) {
7755d4fba8b9Smrg	changed = True;
7756d4fba8b9Smrg    } else {
7757d4fba8b9Smrg	int n;
7758d4fba8b9Smrg	for (n = 0; n < NCOLORS; ++n) {
7759d4fba8b9Smrg	    if (COLOR_DEFINED(source, n)) {
7760d4fba8b9Smrg		if (COLOR_DEFINED(target, n)) {
7761d4fba8b9Smrg		    if (source->colors[n] != target->colors[n]) {
7762d4fba8b9Smrg			changed = True;
7763d4fba8b9Smrg			break;
7764d4fba8b9Smrg		    }
7765d4fba8b9Smrg		} else {
7766d4fba8b9Smrg		    changed = True;
7767d4fba8b9Smrg		    break;
7768d4fba8b9Smrg		}
7769d4fba8b9Smrg	    } else if (COLOR_DEFINED(target, n)) {
7770d4fba8b9Smrg		changed = True;
7771d4fba8b9Smrg		break;
7772d4fba8b9Smrg	    }
7773d4fba8b9Smrg	}
7774d4fba8b9Smrg    }
7775d4fba8b9Smrg    if (changed) {
7776d4fba8b9Smrg	ChangeColors(xw, source);
7777d4fba8b9Smrg	UpdateOldColors(xw, source);
7778d4fba8b9Smrg    }
7779d4fba8b9Smrg}
7780d4fba8b9Smrg#endif /* OPT_ISO_COLORS */
7781d4fba8b9Smrg
7782d4fba8b9Smrg#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
7783d4fba8b9Smrg#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
7784d4fba8b9Smrg
7785d4fba8b9Smrg/*
7786d4fba8b9Smrg * By default, a "push" increments the stack after copying to the current
7787d4fba8b9Smrg * slot.  But a specific target allows one to copy into a specific slot.
7788d4fba8b9Smrg */
7789d4fba8b9Smrgvoid
7790d4fba8b9SmrgxtermPushColors(XtermWidget xw, int value)
7791d4fba8b9Smrg{
7792d4fba8b9Smrg#if OPT_ISO_COLORS
7793d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7794d4fba8b9Smrg    int pushed = s->used;
7795d4fba8b9Smrg    int actual = (value <= 0) ? pushed : (value - 1);
7796d4fba8b9Smrg
7797d4fba8b9Smrg    TRACE(("xtermPushColors %d:%d\n", actual, pushed));
7798d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7799d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7800d4fba8b9Smrg	ColorSlot *palette;
7801d4fba8b9Smrg
7802d4fba8b9Smrg	if ((palette = allocColorSlot(xw, actual)) != NULL) {
7803d4fba8b9Smrg	    GetColors(xw, &(palette->base));
7804d4fba8b9Smrg	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
7805d4fba8b9Smrg	    if (value < 0) {
7806d4fba8b9Smrg		s->used++;
7807d4fba8b9Smrg		if (s->last < s->used)
7808d4fba8b9Smrg		    s->last = s->used;
7809d4fba8b9Smrg	    } else {
7810d4fba8b9Smrg		s->used = value;
7811d4fba8b9Smrg	    }
7812d4fba8b9Smrg	}
7813d4fba8b9Smrg    }
7814d4fba8b9Smrg#else
7815d4fba8b9Smrg    (void) xw;
7816d4fba8b9Smrg    (void) value;
7817d4fba8b9Smrg#endif
7818d4fba8b9Smrg}
7819d4fba8b9Smrg
7820d4fba8b9Smrgvoid
7821d4fba8b9SmrgxtermPopColors(XtermWidget xw, int value)
7822d4fba8b9Smrg{
7823d4fba8b9Smrg#if OPT_ISO_COLORS
7824d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7825d4fba8b9Smrg    int popped = (s->used - 1);
7826d4fba8b9Smrg    int actual = (value <= 0) ? popped : (value - 1);
7827d4fba8b9Smrg
7828d4fba8b9Smrg    TRACE(("xtermPopColors %d:%d\n", actual, popped));
7829d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7830d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7831d4fba8b9Smrg	ColorSlot *palette;
7832d4fba8b9Smrg
7833d4fba8b9Smrg	if ((palette = s->palettes[actual]) != NULL) {
7834d4fba8b9Smrg	    Boolean changed = DiffColorSlot(screen->Acolors,
7835d4fba8b9Smrg					    palette->ansi,
7836d4fba8b9Smrg					    MAXCOLORS);
7837d4fba8b9Smrg
7838d4fba8b9Smrg	    GetOldColors(xw);
7839d4fba8b9Smrg	    popOldColors(xw, &(palette->base));
7840d4fba8b9Smrg	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
7841d4fba8b9Smrg	    s->used = actual;
7842d4fba8b9Smrg	    if (changed)
7843d4fba8b9Smrg		xtermRepaint(xw);
7844d4fba8b9Smrg	}
7845d4fba8b9Smrg    }
7846d4fba8b9Smrg#else
7847d4fba8b9Smrg    (void) xw;
7848d4fba8b9Smrg    (void) value;
7849d4fba8b9Smrg#endif
7850d4fba8b9Smrg}
7851d4fba8b9Smrg
7852d4fba8b9Smrgvoid
7853d4fba8b9SmrgxtermReportColors(XtermWidget xw)
7854d4fba8b9Smrg{
7855d4fba8b9Smrg    ANSI reply;
7856d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7857d4fba8b9Smrg
7858d4fba8b9Smrg    memset(&reply, 0, sizeof(reply));
7859d4fba8b9Smrg    reply.a_type = ANSI_CSI;
7860d4fba8b9Smrg    reply.a_pintro = '?';
7861d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->used;
7862d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->last;
7863d4fba8b9Smrg    reply.a_inters = '#';
7864d4fba8b9Smrg    reply.a_final = 'Q';
7865d4fba8b9Smrg    unparseseq(xw, &reply);
7866d4fba8b9Smrg}
7867d4fba8b9Smrg#endif /* OPT_XTERM_SGR */
7868