1190d7dceSmrg/* $XTermId: misc.c,v 1.1107 2024/12/01 20:06:49 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4980988aeSmrg * Copyright 1999-2023,2024 by Thomas E. Dickey
5d522f475Smrg *
6cd3331d0Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg#include <version.h>
56b7c89284Ssnj#include <main.h>
57d522f475Smrg#include <xterm.h>
58dfb07bc7Smrg#include <xterm_io.h>
59d522f475Smrg
60d522f475Smrg#include <sys/stat.h>
61d522f475Smrg#include <stdio.h>
623367019cSmrg#include <stdarg.h>
63d522f475Smrg#include <signal.h>
64d522f475Smrg#include <ctype.h>
65d522f475Smrg#include <pwd.h>
66d522f475Smrg#include <sys/wait.h>
67d522f475Smrg
68d522f475Smrg#include <X11/keysym.h>
69d522f475Smrg#include <X11/Xatom.h>
70d522f475Smrg
71d522f475Smrg#include <X11/Xmu/Error.h>
72d522f475Smrg#include <X11/Xmu/SysUtil.h>
73d522f475Smrg#include <X11/Xmu/WinUtil.h>
74d522f475Smrg#include <X11/Xmu/Xmu.h>
75d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
76d522f475Smrg#include <X11/Sunkeysym.h>
77d522f475Smrg#endif
78d522f475Smrg
793367019cSmrg#ifdef HAVE_LIBXPM
803367019cSmrg#include <X11/xpm.h>
813367019cSmrg#endif
823367019cSmrg
83d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
84d522f475Smrg#include <langinfo.h>
85d522f475Smrg#endif
86d522f475Smrg
87d522f475Smrg#include <xutf8.h>
88d522f475Smrg
89d522f475Smrg#include <data.h>
90d522f475Smrg#include <error.h>
91d522f475Smrg#include <menu.h>
92d522f475Smrg#include <fontutils.h>
93d522f475Smrg#include <xstrings.h>
94d522f475Smrg#include <xtermcap.h>
95d522f475Smrg#include <VTparse.h>
96fa3f02f3Smrg#include <graphics.h>
979a64e1c5Smrg#include <graphics_regis.h>
989a64e1c5Smrg#include <graphics_sixel.h>
99d522f475Smrg
100d522f475Smrg#include <assert.h>
101d522f475Smrg
102d1603babSmrg#ifdef HAVE_MKSTEMP
103d1603babSmrg#define MakeTemp(f) mkstemp(f)
104d1603babSmrg#else
105d1603babSmrg#define MakeTemp(f) mktemp(f)
106d1603babSmrg#endif
107d1603babSmrg
108d4fba8b9Smrg#if USE_DOUBLE_BUFFER
109d4fba8b9Smrg#include <X11/extensions/Xdbe.h>
110d4fba8b9Smrg#endif
111d4fba8b9Smrg
112d4fba8b9Smrg#if OPT_WIDE_CHARS
113d4fba8b9Smrg#include <wctype.h>
114d4fba8b9Smrg#endif
115d4fba8b9Smrg
116d522f475Smrg#if OPT_TEK4014
117d522f475Smrg#define OUR_EVENT(event,Type) \
118d522f475Smrg		(event.type == Type && \
119d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
120d522f475Smrg		    (tekWidget && \
121d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
122d522f475Smrg#else
123d522f475Smrg#define OUR_EVENT(event,Type) \
124d522f475Smrg		(event.type == Type && \
125d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
126d522f475Smrg#endif
127d522f475Smrg
128d4fba8b9Smrg#define VB_DELAY    screen->visualBellDelay
129d4fba8b9Smrg#define EVENT_DELAY TScreenOf(term)->nextEventDelay
130d4fba8b9Smrg
1313367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
132d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
133d522f475Smrg
134d522f475Smrg#if OPT_EXEC_XTERM
135d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
136d522f475Smrg   error; adapted from libc docs */
137d522f475Smrgstatic char *
138d522f475SmrgReadlink(const char *filename)
139d522f475Smrg{
140d522f475Smrg    char *buf = NULL;
141cd3331d0Smrg    size_t size = 100;
142d522f475Smrg
143d522f475Smrg    for (;;) {
144037a25ddSmrg	int n;
145037a25ddSmrg	char *tmp = TypeRealloc(char, size, buf);
146037a25ddSmrg	if (tmp == NULL) {
147037a25ddSmrg	    free(buf);
148037a25ddSmrg	    return NULL;
149037a25ddSmrg	}
150037a25ddSmrg	buf = tmp;
151d522f475Smrg	memset(buf, 0, size);
152d522f475Smrg
153cd3331d0Smrg	n = (int) readlink(filename, buf, size);
154d522f475Smrg	if (n < 0) {
155d522f475Smrg	    free(buf);
156d522f475Smrg	    return NULL;
157d522f475Smrg	}
158d522f475Smrg
159d522f475Smrg	if ((unsigned) n < size) {
160d522f475Smrg	    return buf;
161d522f475Smrg	}
162d522f475Smrg
163d522f475Smrg	size *= 2;
164d522f475Smrg    }
165d522f475Smrg}
166d522f475Smrg#endif /* OPT_EXEC_XTERM */
167d522f475Smrg
168d522f475Smrgstatic void
169d522f475SmrgSleep(int msec)
170d522f475Smrg{
171d522f475Smrg    static struct timeval select_timeout;
172d522f475Smrg
173d522f475Smrg    select_timeout.tv_sec = 0;
174d522f475Smrg    select_timeout.tv_usec = msec * 1000;
175190d7dceSmrg    select(0, NULL, NULL, NULL, &select_timeout);
176d522f475Smrg}
177d522f475Smrg
178d522f475Smrgstatic void
1793367019cSmrgselectwindow(XtermWidget xw, int flag)
180d522f475Smrg{
1813367019cSmrg    TScreen *screen = TScreenOf(xw);
1823367019cSmrg
183d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
184d522f475Smrg
185d522f475Smrg#if OPT_TEK4014
1863367019cSmrg    if (TEK4014_ACTIVE(xw)) {
187d522f475Smrg	if (!Ttoggled)
188d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
189d522f475Smrg	screen->select |= flag;
190d522f475Smrg	if (!Ttoggled)
191d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
192d522f475Smrg    } else
193d522f475Smrg#endif
194d522f475Smrg    {
195d4fba8b9Smrg#if OPT_INPUT_METHOD
1963367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
1973367019cSmrg	if (input && input->xic)
1983367019cSmrg	    XSetICFocus(input->xic);
1993367019cSmrg#endif
200d522f475Smrg
201d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
202d4fba8b9Smrg	    HideCursor(xw);
203d522f475Smrg	screen->select |= flag;
204d522f475Smrg	if (screen->cursor_state)
205d4fba8b9Smrg	    ShowCursor(xw);
206d522f475Smrg    }
207cd3331d0Smrg    GetScrollLock(screen);
208d522f475Smrg}
209d522f475Smrg
210d522f475Smrgstatic void
2113367019cSmrgunselectwindow(XtermWidget xw, int flag)
212d522f475Smrg{
2133367019cSmrg    TScreen *screen = TScreenOf(xw);
2143367019cSmrg
215d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
216d522f475Smrg
2173367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
218d522f475Smrg	screen->hide_pointer = False;
2198f44fb3bSmrg	xtermDisplayPointer(xw);
220d522f475Smrg    }
221d522f475Smrg
222d4fba8b9Smrg    screen->select &= ~flag;
223d4fba8b9Smrg
224d522f475Smrg    if (!screen->always_highlight) {
225d522f475Smrg#if OPT_TEK4014
2263367019cSmrg	if (TEK4014_ACTIVE(xw)) {
227d522f475Smrg	    if (!Ttoggled)
228d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
229d522f475Smrg	    if (!Ttoggled)
230d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
231d522f475Smrg	} else
232d522f475Smrg#endif
233d522f475Smrg	{
234d4fba8b9Smrg#if OPT_INPUT_METHOD
2353367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2363367019cSmrg	    if (input && input->xic)
2373367019cSmrg		XUnsetICFocus(input->xic);
2383367019cSmrg#endif
239d522f475Smrg
240d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
241d4fba8b9Smrg		HideCursor(xw);
242d522f475Smrg	    if (screen->cursor_state)
243d4fba8b9Smrg		ShowCursor(xw);
244d522f475Smrg	}
245d522f475Smrg    }
246d522f475Smrg}
247d522f475Smrg
248d522f475Smrgstatic void
2499a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
250d522f475Smrg{
251d522f475Smrg    TScreen *screen = TScreenOf(xw);
252d522f475Smrg
253d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
254cd3331d0Smrg    TRACE_FOCUS(xw, ev);
255cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
256cd3331d0Smrg	ev->focus &&
257cd3331d0Smrg	!(screen->select & FOCUS))
2583367019cSmrg	selectwindow(xw, INWINDOW);
259d522f475Smrg}
260d522f475Smrg
261d522f475Smrgstatic void
2629a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
263d522f475Smrg{
264d522f475Smrg    TScreen *screen = TScreenOf(xw);
265d522f475Smrg
266d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
267cd3331d0Smrg    TRACE_FOCUS(xw, ev);
268cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
269cd3331d0Smrg	ev->focus &&
270cd3331d0Smrg	!(screen->select & FOCUS))
2713367019cSmrg	unselectwindow(xw, INWINDOW);
272d522f475Smrg}
273d522f475Smrg
274d522f475Smrg#ifndef XUrgencyHint
275d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
276d522f475Smrg#endif
277d522f475Smrg
278d522f475Smrgstatic void
279c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
280d522f475Smrg{
281c219fbebSmrg    TScreen *screen = TScreenOf(xw);
282c219fbebSmrg
283d522f475Smrg    if (screen->bellIsUrgent) {
284c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
285190d7dceSmrg	if (h != NULL) {
286c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
287d522f475Smrg		h->flags |= XUrgencyHint;
288d522f475Smrg	    } else {
289d522f475Smrg		h->flags &= ~XUrgencyHint;
290d522f475Smrg	    }
291c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
292d522f475Smrg	}
293d522f475Smrg    }
294d522f475Smrg}
295d522f475Smrg
296d522f475Smrgvoid
297d4fba8b9Smrgdo_xevents(XtermWidget xw)
298d522f475Smrg{
299d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
300d522f475Smrg
3013367019cSmrg    if (xtermAppPending()
302980988aeSmrg	|| GetBytesAvailable(screen->display) > 0) {
303d4fba8b9Smrg	xevents(xw);
304980988aeSmrg    }
305d522f475Smrg}
306d522f475Smrg
307d522f475Smrgvoid
3088f44fb3bSmrgxtermDisplayPointer(XtermWidget xw)
309d522f475Smrg{
310d522f475Smrg    TScreen *screen = TScreenOf(xw);
311d522f475Smrg
312d522f475Smrg    if (screen->Vshow) {
313d522f475Smrg	if (screen->hide_pointer) {
3148f44fb3bSmrg	    TRACE(("Display text pointer (hidden)\n"));
315d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
316d522f475Smrg	} else {
3178f44fb3bSmrg	    TRACE(("Display text pointer (visible)\n"));
318d522f475Smrg	    recolor_cursor(screen,
319d522f475Smrg			   screen->pointer_cursor,
320d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
321d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
322d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
323d522f475Smrg	}
324d522f475Smrg    }
325d522f475Smrg}
326d522f475Smrg
327d522f475Smrgvoid
328d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
329d522f475Smrg{
330d522f475Smrg    static int tried = -1;
331d522f475Smrg    TScreen *screen = TScreenOf(xw);
332d522f475Smrg
333d522f475Smrg#if OPT_TEK4014
334d522f475Smrg    if (TEK4014_SHOWN(xw))
335d522f475Smrg	enable = True;
336d522f475Smrg#endif
337d522f475Smrg
338d522f475Smrg    /*
339d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
340d522f475Smrg     * the mouse-mode:
341d522f475Smrg     */
342d522f475Smrg    if (!enable) {
343d522f475Smrg	switch (screen->pointer_mode) {
344d522f475Smrg	case pNever:
345d522f475Smrg	    enable = True;
346d522f475Smrg	    break;
347d522f475Smrg	case pNoMouse:
348d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
349d522f475Smrg		enable = True;
350d522f475Smrg	    break;
351d522f475Smrg	case pAlways:
3523367019cSmrg	case pFocused:
353d522f475Smrg	    break;
354d522f475Smrg	}
355d522f475Smrg    }
356d522f475Smrg
357d522f475Smrg    if (enable) {
358d522f475Smrg	if (screen->hide_pointer) {
359d522f475Smrg	    screen->hide_pointer = False;
3608f44fb3bSmrg	    xtermDisplayPointer(xw);
361d522f475Smrg	    switch (screen->send_mouse_pos) {
362d522f475Smrg	    case ANY_EVENT_MOUSE:
363d522f475Smrg		break;
364d522f475Smrg	    default:
365d522f475Smrg		MotionOff(screen, xw);
366d522f475Smrg		break;
367d522f475Smrg	    }
368d522f475Smrg	}
369d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
370d522f475Smrg	if (screen->hidden_cursor == 0) {
371d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
372d522f475Smrg	}
373d522f475Smrg	if (screen->hidden_cursor == 0) {
374d522f475Smrg	    tried = 1;
375d522f475Smrg	} else {
376d522f475Smrg	    tried = 0;
377d522f475Smrg	    screen->hide_pointer = True;
3788f44fb3bSmrg	    xtermDisplayPointer(xw);
379d522f475Smrg	    MotionOn(screen, xw);
380d522f475Smrg	}
381d522f475Smrg    }
382d522f475Smrg}
383d522f475Smrg
3843367019cSmrg/* true if p contains q */
3853367019cSmrg#define ExposeContains(p,q) \
3863367019cSmrg	    ((p)->y <= (q)->y \
3873367019cSmrg	  && (p)->x <= (q)->x \
3883367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
3893367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
3903367019cSmrg
3913367019cSmrgstatic XtInputMask
3929a64e1c5SmrgmergeExposeEvents(XEvent *target)
3933367019cSmrg{
3943367019cSmrg    XEvent next_event;
395037a25ddSmrg    XExposeEvent *p;
3963367019cSmrg
3973367019cSmrg    XtAppNextEvent(app_con, target);
3983367019cSmrg    p = (XExposeEvent *) target;
3993367019cSmrg
4003367019cSmrg    while (XtAppPending(app_con)
4013367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4023367019cSmrg	   && next_event.type == Expose) {
4033367019cSmrg	Boolean merge_this = False;
404d4fba8b9Smrg	XExposeEvent *q = (XExposeEvent *) (&next_event);
4053367019cSmrg
4063367019cSmrg	XtAppNextEvent(app_con, &next_event);
407190d7dceSmrg	TRACE_EVENT("pending", &next_event, (String *) 0, NULL);
4083367019cSmrg
4093367019cSmrg	/*
4103367019cSmrg	 * If either window is contained within the other, merge the events.
4113367019cSmrg	 * The traces show that there are also cases where a full repaint of
4123367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4133367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4143367019cSmrg	 * to skim several events ahead.
4153367019cSmrg	 */
4163367019cSmrg	if (p->window == q->window) {
4173367019cSmrg	    if (ExposeContains(p, q)) {
4183367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4193367019cSmrg		merge_this = True;
4203367019cSmrg		next_event = *target;
4213367019cSmrg	    } else if (ExposeContains(q, p)) {
4223367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4233367019cSmrg		merge_this = True;
4243367019cSmrg	    }
4253367019cSmrg	}
4263367019cSmrg	if (!merge_this) {
4273367019cSmrg	    XtDispatchEvent(target);
4283367019cSmrg	}
4293367019cSmrg	*target = next_event;
4303367019cSmrg    }
4313367019cSmrg    XtDispatchEvent(target);
4323367019cSmrg    return XtAppPending(app_con);
4333367019cSmrg}
4343367019cSmrg
4353367019cSmrg/*
4363367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4373367019cSmrg * event.  Remove that from the queue so we can look further.
4383367019cSmrg *
4393367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4403367019cSmrg * that.  If the adjacent events are for different windows, process the older
4413367019cSmrg * event and update the event used for comparing windows.  If they are for the
4423367019cSmrg * same window, only the newer event is of interest.
4433367019cSmrg *
4443367019cSmrg * Finally, process the (remaining) configure-notify event.
4453367019cSmrg */
4463367019cSmrgstatic XtInputMask
4479a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4483367019cSmrg{
4493367019cSmrg    XEvent next_event;
450037a25ddSmrg    XConfigureEvent *p;
4513367019cSmrg
4523367019cSmrg    XtAppNextEvent(app_con, target);
4533367019cSmrg    p = (XConfigureEvent *) target;
4543367019cSmrg
4553367019cSmrg    if (XtAppPending(app_con)
4563367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
4573367019cSmrg	&& next_event.type == ConfigureNotify) {
4583367019cSmrg	Boolean merge_this = False;
459d4fba8b9Smrg	XConfigureEvent *q = (XConfigureEvent *) (&next_event);
4603367019cSmrg
4613367019cSmrg	XtAppNextEvent(app_con, &next_event);
462190d7dceSmrg	TRACE_EVENT("pending", &next_event, (String *) 0, NULL);
4633367019cSmrg
4643367019cSmrg	if (p->window == q->window) {
4653367019cSmrg	    TRACE(("pending Configure...merged\n"));
4663367019cSmrg	    merge_this = True;
4673367019cSmrg	}
4683367019cSmrg	if (!merge_this) {
4693367019cSmrg	    TRACE(("pending Configure...skipped\n"));
4703367019cSmrg	    XtDispatchEvent(target);
4713367019cSmrg	}
4723367019cSmrg	*target = next_event;
4733367019cSmrg    }
4743367019cSmrg    XtDispatchEvent(target);
4753367019cSmrg    return XtAppPending(app_con);
4763367019cSmrg}
4773367019cSmrg
478d4fba8b9Smrg#define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
479d4fba8b9Smrg#define SameButtonEvent(a,b) ( \
480d4fba8b9Smrg	SAME(a,b,type) && \
481d4fba8b9Smrg	SAME(a,b,serial) && \
482d4fba8b9Smrg	SAME(a,b,send_event) && \
483d4fba8b9Smrg	SAME(a,b,display) && \
484d4fba8b9Smrg	SAME(a,b,window) && \
485d4fba8b9Smrg	SAME(a,b,root) && \
486d4fba8b9Smrg	SAME(a,b,subwindow) && \
487d4fba8b9Smrg	SAME(a,b,time) && \
488d4fba8b9Smrg	SAME(a,b,x) && \
489d4fba8b9Smrg	SAME(a,b,y) && \
490d4fba8b9Smrg	SAME(a,b,x_root) && \
491d4fba8b9Smrg	SAME(a,b,y_root) && \
492d4fba8b9Smrg	SAME(a,b,state) && \
493d4fba8b9Smrg	SAME(a,b,button) && \
494d4fba8b9Smrg	SAME(a,b,same_screen))
495d4fba8b9Smrg
496d4fba8b9Smrg/*
497d4fba8b9Smrg * Work around a bug in the X mouse code, which delivers duplicate events.
498d4fba8b9Smrg */
499d4fba8b9Smrgstatic XtInputMask
500d4fba8b9SmrgmergeButtonEvents(XEvent *target)
501d4fba8b9Smrg{
502d4fba8b9Smrg    XEvent next_event;
503d4fba8b9Smrg    XButtonEvent *p;
504d4fba8b9Smrg
505d4fba8b9Smrg    XtAppNextEvent(app_con, target);
506d4fba8b9Smrg    p = (XButtonEvent *) target;
507d4fba8b9Smrg
508d4fba8b9Smrg    if (XtAppPending(app_con)
509d4fba8b9Smrg	&& XtAppPeekEvent(app_con, &next_event)
510d4fba8b9Smrg	&& SameButtonEvent(target, &next_event)) {
511d4fba8b9Smrg	Boolean merge_this = False;
512d4fba8b9Smrg	XButtonEvent *q = (XButtonEvent *) (&next_event);
513d4fba8b9Smrg
514d4fba8b9Smrg	XtAppNextEvent(app_con, &next_event);
515190d7dceSmrg	TRACE_EVENT("pending", &next_event, (String *) 0, NULL);
516d4fba8b9Smrg
517d4fba8b9Smrg	if (p->window == q->window) {
518d4fba8b9Smrg	    TRACE(("pending ButtonEvent...merged\n"));
519d4fba8b9Smrg	    merge_this = True;
520d4fba8b9Smrg	}
521d4fba8b9Smrg	if (!merge_this) {
522d4fba8b9Smrg	    TRACE(("pending ButtonEvent...skipped\n"));
523d4fba8b9Smrg	    XtDispatchEvent(target);
524d4fba8b9Smrg	}
525d4fba8b9Smrg	*target = next_event;
526d4fba8b9Smrg    }
527d4fba8b9Smrg    XtDispatchEvent(target);
528d4fba8b9Smrg    return XtAppPending(app_con);
529d4fba8b9Smrg}
530d4fba8b9Smrg
5313367019cSmrg/*
5323367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5333367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5343367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5353367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5363367019cSmrg * point.
5373367019cSmrg *
5383367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5393367019cSmrg */
5403367019cSmrgXtInputMask
5413367019cSmrgxtermAppPending(void)
5423367019cSmrg{
5433367019cSmrg    XtInputMask result = XtAppPending(app_con);
5443367019cSmrg    XEvent this_event;
545037a25ddSmrg    Boolean found = False;
5463367019cSmrg
5473367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
548037a25ddSmrg	found = True;
549190d7dceSmrg	TRACE_EVENT("pending", &this_event, (String *) 0, NULL);
5503367019cSmrg	if (this_event.type == Expose) {
5513367019cSmrg	    result = mergeExposeEvents(&this_event);
5523367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5533367019cSmrg	    result = mergeConfigureEvents(&this_event);
554d4fba8b9Smrg	} else if (this_event.type == ButtonPress ||
555d4fba8b9Smrg		   this_event.type == ButtonRelease) {
556d4fba8b9Smrg	    result = mergeButtonEvents(&this_event);
5573367019cSmrg	} else {
5583367019cSmrg	    break;
5593367019cSmrg	}
5603367019cSmrg    }
561037a25ddSmrg
562037a25ddSmrg    /*
563037a25ddSmrg     * With NetBSD, closing a shell results in closing the X input event
564037a25ddSmrg     * stream, which interferes with the "-hold" option.  Wait a short time in
565037a25ddSmrg     * this case, to avoid max'ing the CPU.
566037a25ddSmrg     */
567037a25ddSmrg    if (hold_screen && caught_intr && !found) {
568d4fba8b9Smrg	Sleep(EVENT_DELAY);
569037a25ddSmrg    }
5703367019cSmrg    return result;
5713367019cSmrg}
5723367019cSmrg
573d522f475Smrgvoid
574d4fba8b9Smrgxevents(XtermWidget xw)
575d522f475Smrg{
576d522f475Smrg    TScreen *screen = TScreenOf(xw);
577d522f475Smrg    XEvent event;
578d522f475Smrg    XtInputMask input_mask;
579d522f475Smrg
580d522f475Smrg    if (need_cleanup)
5813367019cSmrg	NormalExit();
582d522f475Smrg
583d522f475Smrg    if (screen->scroll_amt)
584d522f475Smrg	FlushScroll(xw);
585d522f475Smrg    /*
586d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
587d522f475Smrg     * will process the timeout and return without blockng on the
588cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
589d522f475Smrg     * with select().
590d522f475Smrg     */
5913367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
592cd3331d0Smrg	if (input_mask & XtIMTimer)
593cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
594d522f475Smrg#if OPT_SESSION_MGT
595cd3331d0Smrg	/*
596cd3331d0Smrg	 * Session management events are alternative input events. Deal with
597cd3331d0Smrg	 * them in the same way.
598cd3331d0Smrg	 */
599cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
600cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
601d522f475Smrg#endif
602cd3331d0Smrg	else
603cd3331d0Smrg	    break;
604cd3331d0Smrg    }
605d522f475Smrg
606d522f475Smrg    /*
607d4fba8b9Smrg     * If there are no XEvents, don't wait around...
608d522f475Smrg     */
609d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
610d522f475Smrg	return;
611d522f475Smrg    do {
612d522f475Smrg	/*
613d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
614d522f475Smrg	 * We simply ignore all events except for those not passed down to
615d522f475Smrg	 * this function, e.g., those handled in in_put().
616d522f475Smrg	 */
617d522f475Smrg	if (screen->waitingForTrackInfo) {
618d4fba8b9Smrg	    Sleep(EVENT_DELAY);
619d522f475Smrg	    return;
620d522f475Smrg	}
621d522f475Smrg	XtAppNextEvent(app_con, &event);
622d522f475Smrg	/*
623d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
624d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
625d522f475Smrg	 * looking at the event ourselves we make sure that we can
626d522f475Smrg	 * do the right thing.
627d522f475Smrg	 */
628d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
629d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
630d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
631d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
632d4fba8b9Smrg	} else if (event.xany.type == MotionNotify
633d4fba8b9Smrg		   && event.xcrossing.window == XtWindow(xw)) {
634d4fba8b9Smrg	    switch (screen->send_mouse_pos) {
635d4fba8b9Smrg	    case ANY_EVENT_MOUSE:
636d522f475Smrg#if OPT_DEC_LOCATOR
637d4fba8b9Smrg	    case DEC_LOCATOR:
638d522f475Smrg#endif /* OPT_DEC_LOCATOR */
639d4fba8b9Smrg		SendMousePosition(xw, &event);
640d4fba8b9Smrg		xtermShowPointer(xw, True);
641d4fba8b9Smrg		continue;
642d4fba8b9Smrg	    case BTN_EVENT_MOUSE:
643d4fba8b9Smrg		SendMousePosition(xw, &event);
644d4fba8b9Smrg		xtermShowPointer(xw, True);
645d4fba8b9Smrg	    }
646d522f475Smrg	}
647d522f475Smrg
648cb4a1343Smrg	/*
649cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
650cb4a1343Smrg	 * mouse pointer back on.
651cb4a1343Smrg	 */
652cb4a1343Smrg	if (screen->hide_pointer) {
6533367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6543367019cSmrg		switch (event.xany.type) {
6553367019cSmrg		case MotionNotify:
6563367019cSmrg		    xtermShowPointer(xw, True);
6573367019cSmrg		    break;
6583367019cSmrg		}
6593367019cSmrg	    } else {
6603367019cSmrg		switch (event.xany.type) {
6613367019cSmrg		case KeyPress:
6623367019cSmrg		case KeyRelease:
6633367019cSmrg		case ButtonPress:
6643367019cSmrg		case ButtonRelease:
6653367019cSmrg		    /* also these... */
6663367019cSmrg		case Expose:
667037a25ddSmrg		case GraphicsExpose:
6683367019cSmrg		case NoExpose:
6693367019cSmrg		case PropertyNotify:
6703367019cSmrg		case ClientMessage:
6713367019cSmrg		    break;
6723367019cSmrg		default:
6733367019cSmrg		    xtermShowPointer(xw, True);
6743367019cSmrg		    break;
6753367019cSmrg		}
676cb4a1343Smrg	    }
677cb4a1343Smrg	}
678cb4a1343Smrg
679d522f475Smrg	if (!event.xany.send_event ||
680d522f475Smrg	    screen->allowSendEvents ||
681d522f475Smrg	    ((event.xany.type != KeyPress) &&
682d522f475Smrg	     (event.xany.type != KeyRelease) &&
683d522f475Smrg	     (event.xany.type != ButtonPress) &&
684d522f475Smrg	     (event.xany.type != ButtonRelease))) {
685d522f475Smrg
686d4fba8b9Smrg	    if (event.xany.type == MappingNotify) {
687d4fba8b9Smrg		XRefreshKeyboardMapping(&(event.xmapping));
688d4fba8b9Smrg		VTInitModifiers(xw);
689d4fba8b9Smrg	    }
690d522f475Smrg	    XtDispatchEvent(&event);
691d522f475Smrg	}
6923367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
693d522f475Smrg}
694d522f475Smrg
695d522f475Smrgstatic Cursor
696d522f475Smrgmake_hidden_cursor(XtermWidget xw)
697d522f475Smrg{
698d522f475Smrg    TScreen *screen = TScreenOf(xw);
699d522f475Smrg    Cursor c;
700d522f475Smrg    Display *dpy = screen->display;
701d522f475Smrg    XFontStruct *fn;
702d522f475Smrg
703d522f475Smrg    static XColor dummy;
704d522f475Smrg
705d522f475Smrg    /*
706d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
707d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
708b7c89284Ssnj     * server insists on drawing _something_.
709d522f475Smrg     */
710d522f475Smrg    TRACE(("Ask for nil2 font\n"));
711190d7dceSmrg    if ((fn = xtermLoadQueryFont(xw, "nil2")) == NULL) {
712d522f475Smrg	TRACE(("...Ask for fixed font\n"));
7138f44fb3bSmrg	fn = xtermLoadQueryFont(xw, DEFFONT);
714d522f475Smrg    }
715d522f475Smrg
716190d7dceSmrg    if (fn != NULL) {
717d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
718d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
719d522f475Smrg	XFreeFont(dpy, fn);
720d522f475Smrg    } else {
721d4fba8b9Smrg	c = None;
722d522f475Smrg    }
723d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
724d4fba8b9Smrg    return c;
725d522f475Smrg}
726d522f475Smrg
727fa3f02f3Smrg/*
728fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
729fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
730fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
731fa3f02f3Smrg * until the window is initialized.
732fa3f02f3Smrg */
7338f44fb3bSmrg#ifdef HAVE_LIB_XCURSOR
734fa3f02f3Smrgvoid
735037a25ddSmrginit_colored_cursor(Display *dpy)
736fa3f02f3Smrg{
73794644356Smrg    static const char theme[] = "index.theme";
738d1603babSmrg    static const char pattern[] = "xtermXXXXXXXX";
739fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
740fa3f02f3Smrg
741190d7dceSmrg    xterm_cursor_theme = NULL;
742037a25ddSmrg    /*
743037a25ddSmrg     * The environment variable overrides a (possible) resource Xcursor.theme
744037a25ddSmrg     */
745fa3f02f3Smrg    if (IsEmpty(env)) {
746037a25ddSmrg	env = XGetDefault(dpy, "Xcursor", "theme");
7478f44fb3bSmrg	TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
7488f44fb3bSmrg    } else {
7498f44fb3bSmrg	TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
750037a25ddSmrg    }
7518f44fb3bSmrg
752037a25ddSmrg    /*
753037a25ddSmrg     * If neither found, provide our own default theme.
754037a25ddSmrg     */
755037a25ddSmrg    if (IsEmpty(env)) {
756037a25ddSmrg	const char *tmp_dir;
757037a25ddSmrg	char *filename;
758037a25ddSmrg	size_t needed;
759037a25ddSmrg
7608f44fb3bSmrg	TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
7618f44fb3bSmrg
762190d7dceSmrg	if ((tmp_dir = getenv("TMPDIR")) == NULL) {
763fa3f02f3Smrg	    tmp_dir = P_tmpdir;
764fa3f02f3Smrg	}
765fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
766190d7dceSmrg	if ((filename = malloc(needed)) != NULL) {
767fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
768fa3f02f3Smrg
769fa3f02f3Smrg#ifdef HAVE_MKDTEMP
770fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
771fa3f02f3Smrg#else
772d1603babSmrg	    if (MakeTemp(filename) != 0
773fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
774fa3f02f3Smrg		xterm_cursor_theme = filename;
775fa3f02f3Smrg	    }
776fa3f02f3Smrg#endif
777d4fba8b9Smrg	    if (xterm_cursor_theme != filename)
778d4fba8b9Smrg		free(filename);
779fa3f02f3Smrg	    /*
780fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
781fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
782fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
783fa3f02f3Smrg	     */
784190d7dceSmrg	    if (xterm_cursor_theme != NULL) {
785fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
786037a25ddSmrg		FILE *fp;
787037a25ddSmrg
788fa3f02f3Smrg		strcat(leaf, "/");
789fa3f02f3Smrg		strcat(leaf, theme);
7908f44fb3bSmrg
791190d7dceSmrg		if ((fp = fopen(xterm_cursor_theme, "w")) != NULL) {
792fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
793fa3f02f3Smrg		    fclose(fp);
794fa3f02f3Smrg		    *leaf = '\0';
795fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
796fa3f02f3Smrg		    *leaf = '/';
7978f44fb3bSmrg
7988f44fb3bSmrg		    TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
7998f44fb3bSmrg			   xterm_cursor_theme));
8008f44fb3bSmrg		    atexit(cleanup_colored_cursor);
8018f44fb3bSmrg		} else {
8028f44fb3bSmrg		    FreeAndNull(xterm_cursor_theme);
803fa3f02f3Smrg		}
804fa3f02f3Smrg	    }
805fa3f02f3Smrg	}
806fa3f02f3Smrg    }
807fa3f02f3Smrg}
8088f44fb3bSmrg#endif /* HAVE_LIB_XCURSOR */
809fa3f02f3Smrg
810fa3f02f3Smrg/*
811fa3f02f3Smrg * Once done, discard the file and directory holding it.
812fa3f02f3Smrg */
813fa3f02f3Smrgvoid
814fa3f02f3Smrgcleanup_colored_cursor(void)
815fa3f02f3Smrg{
816fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
817190d7dceSmrg    if (xterm_cursor_theme != NULL) {
818fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
819fa3f02f3Smrg	struct stat sb;
820fa3f02f3Smrg	if (!IsEmpty(my_path)
821fa3f02f3Smrg	    && stat(my_path, &sb) == 0
822fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
823fa3f02f3Smrg	    unlink(xterm_cursor_theme);
824fa3f02f3Smrg	    rmdir(my_path);
825fa3f02f3Smrg	}
8268f44fb3bSmrg	FreeAndNull(xterm_cursor_theme);
827fa3f02f3Smrg    }
828fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
829fa3f02f3Smrg}
830fa3f02f3Smrg
831d522f475SmrgCursor
832d4fba8b9Smrgmake_colored_cursor(unsigned c_index,		/* index into font */
833d522f475Smrg		    unsigned long fg,	/* pixel value */
834d522f475Smrg		    unsigned long bg)	/* pixel value */
835d522f475Smrg{
836d522f475Smrg    TScreen *screen = TScreenOf(term);
837d4fba8b9Smrg    Cursor c = None;
838d522f475Smrg    Display *dpy = screen->display;
839d522f475Smrg
840d4fba8b9Smrg    TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
841d4fba8b9Smrg    if (!IsEmpty(screen->cursor_font_name)) {
842d4fba8b9Smrg	static XTermFonts myFont;
843d4fba8b9Smrg
844d4fba8b9Smrg	/* adapted from XCreateFontCursor(), which hardcodes the font name */
845d4fba8b9Smrg	TRACE(("loading cursor from alternate cursor font\n"));
8468f44fb3bSmrg	myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
8478f44fb3bSmrg	if (myFont.fs != NULL) {
848d4fba8b9Smrg	    if (!xtermMissingChar(c_index, &myFont)
849d4fba8b9Smrg		&& !xtermMissingChar(c_index + 1, &myFont)) {
850d4fba8b9Smrg#define DATA(c) { 0UL, c, c, c, 0, 0 }
851d4fba8b9Smrg		static XColor foreground = DATA(0);
852d4fba8b9Smrg		static XColor background = DATA(65535);
853d4fba8b9Smrg#undef DATA
854d4fba8b9Smrg
855d4fba8b9Smrg		/*
856d4fba8b9Smrg		 * Cursor fonts follow each shape glyph with a mask glyph; so
857d4fba8b9Smrg		 * that character position 0 contains a shape, 1 the mask for
858d4fba8b9Smrg		 * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
859d4fba8b9Smrg		 * contains defined names for each shape.
860d4fba8b9Smrg		 */
861d4fba8b9Smrg		c = XCreateGlyphCursor(dpy,
862d4fba8b9Smrg				       myFont.fs->fid,	/* source_font */
863d4fba8b9Smrg				       myFont.fs->fid,	/* mask_font */
864d4fba8b9Smrg				       c_index + 0,	/* source_char */
865d4fba8b9Smrg				       c_index + 1,	/* mask_char */
866d4fba8b9Smrg				       &foreground,
867d4fba8b9Smrg				       &background);
868d4fba8b9Smrg	    }
869d4fba8b9Smrg	    XFreeFont(dpy, myFont.fs);
870d4fba8b9Smrg	}
871d4fba8b9Smrg	if (c == None) {
872d4fba8b9Smrg	    xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
873d4fba8b9Smrg			 c_index, screen->cursor_font_name);
874d4fba8b9Smrg	}
875d4fba8b9Smrg    }
876d4fba8b9Smrg    if (c == None)
877d4fba8b9Smrg	c = XCreateFontCursor(dpy, c_index);
878d4fba8b9Smrg
879d522f475Smrg    if (c != None) {
880d522f475Smrg	recolor_cursor(screen, c, fg, bg);
881d522f475Smrg    }
882d4fba8b9Smrg    return c;
883d522f475Smrg}
884d522f475Smrg
8858f44fb3bSmrg/* adapted from <X11/cursorfont.h> */
8868f44fb3bSmrgstatic int
8878f44fb3bSmrgLookupCursorShape(const char *name)
8888f44fb3bSmrg{
8898f44fb3bSmrg#define DATA(name) { XC_##name, #name }
8908f44fb3bSmrg    static struct {
8918f44fb3bSmrg	int code;
8928f44fb3bSmrg	const char name[25];
8938f44fb3bSmrg    } table[] = {
8948f44fb3bSmrg	DATA(X_cursor),
8958f44fb3bSmrg	    DATA(arrow),
8968f44fb3bSmrg	    DATA(based_arrow_down),
8978f44fb3bSmrg	    DATA(based_arrow_up),
8988f44fb3bSmrg	    DATA(boat),
8998f44fb3bSmrg	    DATA(bogosity),
9008f44fb3bSmrg	    DATA(bottom_left_corner),
9018f44fb3bSmrg	    DATA(bottom_right_corner),
9028f44fb3bSmrg	    DATA(bottom_side),
9038f44fb3bSmrg	    DATA(bottom_tee),
9048f44fb3bSmrg	    DATA(box_spiral),
9058f44fb3bSmrg	    DATA(center_ptr),
9068f44fb3bSmrg	    DATA(circle),
9078f44fb3bSmrg	    DATA(clock),
9088f44fb3bSmrg	    DATA(coffee_mug),
9098f44fb3bSmrg	    DATA(cross),
9108f44fb3bSmrg	    DATA(cross_reverse),
9118f44fb3bSmrg	    DATA(crosshair),
9128f44fb3bSmrg	    DATA(diamond_cross),
9138f44fb3bSmrg	    DATA(dot),
9148f44fb3bSmrg	    DATA(dotbox),
9158f44fb3bSmrg	    DATA(double_arrow),
9168f44fb3bSmrg	    DATA(draft_large),
9178f44fb3bSmrg	    DATA(draft_small),
9188f44fb3bSmrg	    DATA(draped_box),
9198f44fb3bSmrg	    DATA(exchange),
9208f44fb3bSmrg	    DATA(fleur),
9218f44fb3bSmrg	    DATA(gobbler),
9228f44fb3bSmrg	    DATA(gumby),
9238f44fb3bSmrg	    DATA(hand1),
9248f44fb3bSmrg	    DATA(hand2),
9258f44fb3bSmrg	    DATA(heart),
9268f44fb3bSmrg	    DATA(icon),
9278f44fb3bSmrg	    DATA(iron_cross),
9288f44fb3bSmrg	    DATA(left_ptr),
9298f44fb3bSmrg	    DATA(left_side),
9308f44fb3bSmrg	    DATA(left_tee),
9318f44fb3bSmrg	    DATA(leftbutton),
9328f44fb3bSmrg	    DATA(ll_angle),
9338f44fb3bSmrg	    DATA(lr_angle),
9348f44fb3bSmrg	    DATA(man),
9358f44fb3bSmrg	    DATA(middlebutton),
9368f44fb3bSmrg	    DATA(mouse),
9378f44fb3bSmrg	    DATA(pencil),
9388f44fb3bSmrg	    DATA(pirate),
9398f44fb3bSmrg	    DATA(plus),
9408f44fb3bSmrg	    DATA(question_arrow),
9418f44fb3bSmrg	    DATA(right_ptr),
9428f44fb3bSmrg	    DATA(right_side),
9438f44fb3bSmrg	    DATA(right_tee),
9448f44fb3bSmrg	    DATA(rightbutton),
9458f44fb3bSmrg	    DATA(rtl_logo),
9468f44fb3bSmrg	    DATA(sailboat),
9478f44fb3bSmrg	    DATA(sb_down_arrow),
9488f44fb3bSmrg	    DATA(sb_h_double_arrow),
9498f44fb3bSmrg	    DATA(sb_left_arrow),
9508f44fb3bSmrg	    DATA(sb_right_arrow),
9518f44fb3bSmrg	    DATA(sb_up_arrow),
9528f44fb3bSmrg	    DATA(sb_v_double_arrow),
9538f44fb3bSmrg	    DATA(shuttle),
9548f44fb3bSmrg	    DATA(sizing),
9558f44fb3bSmrg	    DATA(spider),
9568f44fb3bSmrg	    DATA(spraycan),
9578f44fb3bSmrg	    DATA(star),
9588f44fb3bSmrg	    DATA(target),
9598f44fb3bSmrg	    DATA(tcross),
9608f44fb3bSmrg	    DATA(top_left_arrow),
9618f44fb3bSmrg	    DATA(top_left_corner),
9628f44fb3bSmrg	    DATA(top_right_corner),
9638f44fb3bSmrg	    DATA(top_side),
9648f44fb3bSmrg	    DATA(top_tee),
9658f44fb3bSmrg	    DATA(trek),
9668f44fb3bSmrg	    DATA(ul_angle),
9678f44fb3bSmrg	    DATA(umbrella),
9688f44fb3bSmrg	    DATA(ur_angle),
9698f44fb3bSmrg	    DATA(watch),
9708f44fb3bSmrg	    DATA(xterm),
9718f44fb3bSmrg    };
9728f44fb3bSmrg#undef DATA
9738f44fb3bSmrg    Cardinal j;
9748f44fb3bSmrg    int result = -1;
9758f44fb3bSmrg    if (!IsEmpty(name)) {
9768f44fb3bSmrg	for (j = 0; j < XtNumber(table); ++j) {
9778f44fb3bSmrg	    if (!strcmp(name, table[j].name)) {
9788f44fb3bSmrg		result = table[j].code;
9798f44fb3bSmrg		break;
9808f44fb3bSmrg	    }
9818f44fb3bSmrg	}
9828f44fb3bSmrg    }
9838f44fb3bSmrg    return result;
9848f44fb3bSmrg}
9858f44fb3bSmrg
9868f44fb3bSmrgvoid
9878f44fb3bSmrgxtermSetupPointer(XtermWidget xw, const char *theShape)
9888f44fb3bSmrg{
9898f44fb3bSmrg    TScreen *screen = TScreenOf(xw);
9908f44fb3bSmrg    unsigned shape = XC_xterm;
9918f44fb3bSmrg    int other = LookupCursorShape(theShape);
9928f44fb3bSmrg    unsigned which;
9938f44fb3bSmrg
9948f44fb3bSmrg    if (other >= 0 && other < XC_num_glyphs)
9958f44fb3bSmrg	shape = (unsigned) other;
9968f44fb3bSmrg
9978f44fb3bSmrg    TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
9988f44fb3bSmrg	   NonNull(theShape)));
9998f44fb3bSmrg
10008f44fb3bSmrg    which = (unsigned) (shape / 2);
10018f44fb3bSmrg    if (xw->work.pointer_cursors[which] == None) {
10028f44fb3bSmrg	TRACE(("creating text pointer cursor from shape %d\n", shape));
10038f44fb3bSmrg	xw->work.pointer_cursors[which] =
10048f44fb3bSmrg	    make_colored_cursor(shape,
10058f44fb3bSmrg				T_COLOR(screen, MOUSE_FG),
10068f44fb3bSmrg				T_COLOR(screen, MOUSE_BG));
10078f44fb3bSmrg    } else {
10088f44fb3bSmrg	TRACE(("updating text pointer cursor for shape %d\n", shape));
10098f44fb3bSmrg	recolor_cursor(screen,
10108f44fb3bSmrg		       screen->pointer_cursor,
10118f44fb3bSmrg		       T_COLOR(screen, MOUSE_FG),
10128f44fb3bSmrg		       T_COLOR(screen, MOUSE_BG));
10138f44fb3bSmrg    }
10148f44fb3bSmrg    if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
10158f44fb3bSmrg	screen->pointer_cursor = xw->work.pointer_cursors[which];
10168f44fb3bSmrg	TRACE(("defining text pointer cursor with shape %d\n", shape));
10178f44fb3bSmrg	XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
10188f44fb3bSmrg	if (XtIsRealized((Widget) xw)) {
10198f44fb3bSmrg	    /* briefly override pointerMode after changing the pointer */
10208f44fb3bSmrg	    if (screen->pointer_mode != pNever)
10218f44fb3bSmrg		screen->hide_pointer = True;
10228f44fb3bSmrg	    xtermShowPointer(xw, True);
10238f44fb3bSmrg	}
10248f44fb3bSmrg    }
10258f44fb3bSmrg}
10268f44fb3bSmrg
1027d522f475Smrg/* ARGSUSED */
1028d522f475Smrgvoid
1029d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
10309a64e1c5Smrg		 XEvent *event,
1031fa3f02f3Smrg		 String *params GCC_UNUSED,
1032d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
1033d522f475Smrg{
1034cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
1035cd3331d0Smrg    Input(term, &event->xkey, False);
1036d522f475Smrg}
1037d522f475Smrg
1038d522f475Smrg/* ARGSUSED */
1039d522f475Smrgvoid
1040d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
10419a64e1c5Smrg			 XEvent *event,
1042fa3f02f3Smrg			 String *params GCC_UNUSED,
1043d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
1044d522f475Smrg{
1045cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
1046cd3331d0Smrg    Input(term, &event->xkey, True);
1047d522f475Smrg}
1048d522f475Smrg
1049d522f475Smrg/* ARGSUSED */
1050d522f475Smrgvoid
1051d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
10529a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1053fa3f02f3Smrg		  String *params,
1054d522f475Smrg		  Cardinal *nparams)
1055d522f475Smrg{
1056d522f475Smrg
1057d522f475Smrg    if (*nparams != 1)
1058d522f475Smrg	return;
1059d522f475Smrg
1060d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
10610d92cbfdSchristos	const char *abcdef = "ABCDEF";
10620d92cbfdSchristos	const char *xxxxxx;
1063cd3331d0Smrg	Char c;
1064cd3331d0Smrg	UString p;
10650d92cbfdSchristos	unsigned value = 0;
10660d92cbfdSchristos
1067cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
10680d92cbfdSchristos	     '\0'; p++) {
10690d92cbfdSchristos	    value *= 16;
1070d522f475Smrg	    if (c >= '0' && c <= '9')
10710d92cbfdSchristos		value += (unsigned) (c - '0');
1072190d7dceSmrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != NULL)
10730d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
1074d522f475Smrg	    else
1075d522f475Smrg		break;
1076d522f475Smrg	}
10770d92cbfdSchristos	if (c == '\0') {
10780d92cbfdSchristos	    Char hexval[2];
10790d92cbfdSchristos	    hexval[0] = (Char) value;
10800d92cbfdSchristos	    hexval[1] = 0;
1081b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
10820d92cbfdSchristos	}
1083d522f475Smrg    } else {
1084cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
1085d522f475Smrg    }
1086d522f475Smrg}
1087d522f475Smrg
1088d522f475Smrg#if OPT_EXEC_XTERM
1089d522f475Smrg
1090d522f475Smrg#ifndef PROCFS_ROOT
1091d522f475Smrg#define PROCFS_ROOT "/proc"
1092d522f475Smrg#endif
1093d522f475Smrg
1094037a25ddSmrg/*
1095037a25ddSmrg * Determine the current working directory of the child so that we can
1096037a25ddSmrg * spawn a new terminal in the same directory.
1097037a25ddSmrg *
1098037a25ddSmrg * If we cannot get the CWD of the child, just use our own.
1099037a25ddSmrg */
1100037a25ddSmrgchar *
1101037a25ddSmrgProcGetCWD(pid_t pid)
1102037a25ddSmrg{
1103037a25ddSmrg    char *child_cwd = NULL;
1104037a25ddSmrg
1105037a25ddSmrg    if (pid) {
1106037a25ddSmrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
1107037a25ddSmrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
1108037a25ddSmrg	child_cwd = Readlink(child_cwd_link);
1109037a25ddSmrg    }
1110037a25ddSmrg    return child_cwd;
1111037a25ddSmrg}
1112037a25ddSmrg
1113d522f475Smrg/* ARGSUSED */
1114d522f475Smrgvoid
1115d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
11169a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1117fa3f02f3Smrg		    String *params,
1118d522f475Smrg		    Cardinal *nparams)
1119d522f475Smrg{
1120cd3331d0Smrg    TScreen *screen = TScreenOf(term);
1121d522f475Smrg    char *child_cwd = NULL;
1122d522f475Smrg    char *child_exe;
1123d522f475Smrg    pid_t pid;
1124d522f475Smrg
1125d522f475Smrg    /*
1126d522f475Smrg     * Try to find the actual program which is running in the child process.
1127d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
1128d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
1129d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
1130d522f475Smrg     */
1131d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
1132d522f475Smrg    if (!child_exe) {
1133cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
1134cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
1135d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
1136d522f475Smrg	} else {
11373367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
1138d522f475Smrg	}
1139d522f475Smrg	if (child_exe == 0)
1140d522f475Smrg	    return;
1141d522f475Smrg    }
1142d522f475Smrg
1143037a25ddSmrg    child_cwd = ProcGetCWD(screen->pid);
1144d522f475Smrg
1145d522f475Smrg    /* The reaper will take care of cleaning up the child */
1146d522f475Smrg    pid = fork();
1147d522f475Smrg    if (pid == -1) {
11483367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
1149d522f475Smrg    } else if (!pid) {
1150d522f475Smrg	/* We are the child */
1151d522f475Smrg	if (child_cwd) {
1152cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
1153d522f475Smrg	}
1154d522f475Smrg
1155d522f475Smrg	if (setuid(screen->uid) == -1
1156d522f475Smrg	    || setgid(screen->gid) == -1) {
11573367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
1158d522f475Smrg	} else {
11590d92cbfdSchristos	    unsigned myargc = *nparams + 1;
1160d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
1161d522f475Smrg
116294644356Smrg	    if (myargv != 0) {
116394644356Smrg		unsigned n = 0;
1164d522f475Smrg
116594644356Smrg		myargv[n++] = child_exe;
1166d522f475Smrg
116794644356Smrg		while (n < myargc) {
116894644356Smrg		    myargv[n++] = (char *) *params++;
116994644356Smrg		}
117094644356Smrg
117194644356Smrg		myargv[n] = 0;
117294644356Smrg		execv(child_exe, myargv);
117394644356Smrg	    }
1174d522f475Smrg
1175d522f475Smrg	    /* If we get here, we've failed */
11763367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
1177d522f475Smrg	}
1178d522f475Smrg	_exit(0);
1179d522f475Smrg    }
11803367019cSmrg
11813367019cSmrg    /* We are the parent; clean up */
1182d4fba8b9Smrg    free(child_cwd);
11833367019cSmrg    free(child_exe);
1184d522f475Smrg}
1185d522f475Smrg#endif /* OPT_EXEC_XTERM */
1186d522f475Smrg
1187d522f475Smrg/*
1188d522f475Smrg * Rather than sending characters to the host, put them directly into our
1189d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
1190d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
1191d522f475Smrg *
1192d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
1193d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
1194d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
1195d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
1196d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
1197d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
1198d522f475Smrg */
1199d522f475Smrg/* ARGSUSED */
1200d522f475Smrgvoid
1201d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
12029a64e1c5Smrg		XEvent *event GCC_UNUSED,
1203fa3f02f3Smrg		String *params,
1204d522f475Smrg		Cardinal *param_count)
1205d522f475Smrg{
1206d522f475Smrg    if (*param_count == 1) {
1207cd3331d0Smrg	const char *value = params[0];
1208d1603babSmrg	size_t need = strlen(value);
1209d1603babSmrg	size_t used = (size_t) (VTbuffer->next - VTbuffer->buffer);
1210d1603babSmrg	size_t have = (size_t) (VTbuffer->last - VTbuffer->buffer);
1211d522f475Smrg
1212d1603babSmrg	if ((have - used) + need < (size_t) BUF_SIZE) {
1213d522f475Smrg
1214d1603babSmrg	    fillPtyData(term, VTbuffer, value, strlen(value));
1215d522f475Smrg
1216d522f475Smrg	    TRACE(("Interpret %s\n", value));
1217d522f475Smrg	    VTbuffer->update++;
1218d522f475Smrg	}
1219d522f475Smrg    }
1220d522f475Smrg}
1221d522f475Smrg
1222d522f475Smrg/*ARGSUSED*/
1223d522f475Smrgvoid
1224d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
1225d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12269a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1227fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1228d522f475Smrg{
1229d522f475Smrg    /* NOP since we handled it above */
1230d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
1231cd3331d0Smrg    TRACE_FOCUS(w, event);
1232d522f475Smrg}
1233d522f475Smrg
1234d522f475Smrg/*ARGSUSED*/
1235d522f475Smrgvoid
1236d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
1237d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12389a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1239fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1240d522f475Smrg{
1241d522f475Smrg    /* NOP since we handled it above */
1242d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1243cd3331d0Smrg    TRACE_FOCUS(w, event);
1244d522f475Smrg}
1245d522f475Smrg
1246d522f475Smrg/*ARGSUSED*/
1247d522f475Smrgvoid
1248d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1249d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12509a64e1c5Smrg		  XEvent *ev,
1251fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1252d522f475Smrg{
1253d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1254d522f475Smrg    XtermWidget xw = term;
1255d522f475Smrg    TScreen *screen = TScreenOf(xw);
1256d522f475Smrg
12573367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1258d522f475Smrg	   visibleEventType(event->type),
12593367019cSmrg	   visibleNotifyMode(event->mode),
12603367019cSmrg	   visibleNotifyDetail(event->detail)));
1261cd3331d0Smrg    TRACE_FOCUS(xw, event);
1262d522f475Smrg
1263d522f475Smrg    if (screen->quiet_grab
1264d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1265c219fbebSmrg	/* EMPTY */ ;
1266d522f475Smrg    } else if (event->type == FocusIn) {
126794644356Smrg	if (event->detail != NotifyPointer) {
126894644356Smrg	    setXUrgency(xw, False);
126994644356Smrg	}
1270d522f475Smrg
1271d522f475Smrg	/*
1272d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1273d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1274d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1275d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1276d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1277d522f475Smrg	 * delivered to the obscured window.
1278d522f475Smrg	 */
1279d522f475Smrg	if (event->detail == NotifyNonlinear
1280d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
12813367019cSmrg	    unselectwindow(xw, INWINDOW);
1282d522f475Smrg	}
12833367019cSmrg	selectwindow(xw,
1284d522f475Smrg		     ((event->detail == NotifyPointer)
1285d522f475Smrg		      ? INWINDOW
1286d522f475Smrg		      : FOCUS));
1287d522f475Smrg	SendFocusButton(xw, event);
1288d522f475Smrg    } else {
1289d522f475Smrg#if OPT_FOCUS_EVENT
1290d522f475Smrg	if (event->type == FocusOut) {
1291d522f475Smrg	    SendFocusButton(xw, event);
1292d522f475Smrg	}
1293d522f475Smrg#endif
1294d522f475Smrg	/*
1295d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1296d522f475Smrg	 * ignore.
1297d522f475Smrg	 */
1298d522f475Smrg	if (event->mode != NotifyGrab) {
12993367019cSmrg	    unselectwindow(xw,
1300d522f475Smrg			   ((event->detail == NotifyPointer)
1301d522f475Smrg			    ? INWINDOW
1302d522f475Smrg			    : FOCUS));
1303d522f475Smrg	}
1304d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1305cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1306d522f475Smrg	    ReverseVideo(xw);
1307d522f475Smrg	    screen->grabbedKbd = False;
1308d522f475Smrg	    update_securekbd();
1309d522f475Smrg	}
1310d522f475Smrg    }
1311d522f475Smrg}
1312d522f475Smrg
1313d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1314d522f475Smrg
1315b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1316b7c89284Ssnjstatic Atom
1317b7c89284SsnjAtomBell(XtermWidget xw, int which)
1318b7c89284Ssnj{
1319b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1320b7c89284Ssnj    static struct {
1321b7c89284Ssnj	int value;
1322b7c89284Ssnj	const char *name;
1323b7c89284Ssnj    } table[] = {
1324b7c89284Ssnj	DATA(Info),
1325b7c89284Ssnj	    DATA(MarginBell),
1326b7c89284Ssnj	    DATA(MinorError),
1327b7c89284Ssnj	    DATA(TerminalBell)
1328b7c89284Ssnj    };
1329d4fba8b9Smrg#undef DATA
1330b7c89284Ssnj    Cardinal n;
1331b7c89284Ssnj    Atom result = None;
1332b7c89284Ssnj
1333b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1334b7c89284Ssnj	if (table[n].value == which) {
1335980988aeSmrg	    result = CachedInternAtom(XtDisplay(xw), table[n].name);
1336b7c89284Ssnj	    break;
1337b7c89284Ssnj	}
1338b7c89284Ssnj    }
1339b7c89284Ssnj    return result;
1340b7c89284Ssnj}
1341b7c89284Ssnj#endif
1342b7c89284Ssnj
1343d522f475Smrgvoid
1344b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1345d522f475Smrg{
1346b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1347b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1348b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1349cd3331d0Smrg#endif
1350cd3331d0Smrg
1351cd3331d0Smrg    switch (which) {
1352cd3331d0Smrg    case XkbBI_Info:
1353cd3331d0Smrg    case XkbBI_MinorError:
1354cd3331d0Smrg    case XkbBI_MajorError:
1355cd3331d0Smrg    case XkbBI_TerminalBell:
1356cd3331d0Smrg	switch (screen->warningVolume) {
1357cd3331d0Smrg	case bvOff:
1358cd3331d0Smrg	    percent = -100;
1359cd3331d0Smrg	    break;
1360cd3331d0Smrg	case bvLow:
1361cd3331d0Smrg	    break;
1362cd3331d0Smrg	case bvHigh:
1363cd3331d0Smrg	    percent = 100;
1364cd3331d0Smrg	    break;
1365cd3331d0Smrg	}
1366cd3331d0Smrg	break;
1367cd3331d0Smrg    case XkbBI_MarginBell:
1368cd3331d0Smrg	switch (screen->marginVolume) {
1369cd3331d0Smrg	case bvOff:
1370cd3331d0Smrg	    percent = -100;
1371cd3331d0Smrg	    break;
1372cd3331d0Smrg	case bvLow:
1373cd3331d0Smrg	    break;
1374cd3331d0Smrg	case bvHigh:
1375cd3331d0Smrg	    percent = 100;
1376cd3331d0Smrg	    break;
1377cd3331d0Smrg	}
1378cd3331d0Smrg	break;
1379cd3331d0Smrg    default:
1380cd3331d0Smrg	break;
1381cd3331d0Smrg    }
1382cd3331d0Smrg
1383cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1384b7c89284Ssnj    if (tony != None) {
1385c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1386b7c89284Ssnj    } else
1387b7c89284Ssnj#endif
1388b7c89284Ssnj	XBell(screen->display, percent);
1389b7c89284Ssnj}
1390b7c89284Ssnj
1391b7c89284Ssnjvoid
1392cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1393b7c89284Ssnj{
1394b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1395d522f475Smrg    struct timeval curtime;
1396d522f475Smrg
1397b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1398b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1399d522f475Smrg	return;
1400d522f475Smrg    }
1401d522f475Smrg
1402c219fbebSmrg    setXUrgency(xw, True);
1403d522f475Smrg
1404d522f475Smrg    /* has enough time gone by that we are allowed to ring
1405d522f475Smrg       the bell again? */
1406d522f475Smrg    if (screen->bellSuppressTime) {
1407037a25ddSmrg	long now_msecs;
1408037a25ddSmrg
1409d522f475Smrg	if (screen->bellInProgress) {
1410d4fba8b9Smrg	    do_xevents(xw);
1411d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1412d522f475Smrg		return;
1413d522f475Smrg	    }
1414d522f475Smrg	}
1415d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1416d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1417d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1418d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1419d522f475Smrg	    return;
1420d522f475Smrg	}
1421d522f475Smrg	lastBellTime = now_msecs;
1422d522f475Smrg    }
1423d522f475Smrg
1424d522f475Smrg    if (screen->visualbell) {
1425d522f475Smrg	VisualBell();
1426d522f475Smrg    } else {
1427b7c89284Ssnj	xtermBell(xw, which, percent);
1428d522f475Smrg    }
1429d522f475Smrg
1430d522f475Smrg    if (screen->poponbell)
1431c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1432d522f475Smrg
1433d522f475Smrg    if (screen->bellSuppressTime) {
1434d522f475Smrg	/* now we change a property and wait for the notify event to come
1435d522f475Smrg	   back.  If the server is suspending operations while the bell
1436d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1437d522f475Smrg	   know when the previous bell has finished */
1438d522f475Smrg	Widget w = CURRENT_EMU();
1439d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1440d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1441d522f475Smrg	screen->bellInProgress = True;
1442d522f475Smrg    }
1443d522f475Smrg}
1444d522f475Smrg
1445d522f475Smrgstatic void
1446fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1447d522f475Smrg{
14483367019cSmrg    int y = 0;
14493367019cSmrg    int x = 0;
14503367019cSmrg
14513367019cSmrg    if (screen->flash_line) {
14523367019cSmrg	y = CursorY(screen, screen->cur_row);
14533367019cSmrg	height = (unsigned) FontHeight(screen);
14543367019cSmrg    }
14553367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1456d522f475Smrg    XFlush(screen->display);
1457d522f475Smrg    Sleep(VB_DELAY);
14583367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1459d522f475Smrg}
1460d522f475Smrg
1461d522f475Smrgvoid
1462d522f475SmrgVisualBell(void)
1463d522f475Smrg{
1464d4fba8b9Smrg    XtermWidget xw = term;
1465d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1466d522f475Smrg
1467d522f475Smrg    if (VB_DELAY > 0) {
1468d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1469d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1470d522f475Smrg	XGCValues gcval;
1471d522f475Smrg	GC visualGC;
1472d522f475Smrg
1473d522f475Smrg	gcval.function = GXxor;
1474d522f475Smrg	gcval.foreground = xorPixel;
1475d4fba8b9Smrg	visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
1476d522f475Smrg#if OPT_TEK4014
1477d4fba8b9Smrg	if (TEK4014_ACTIVE(xw)) {
1478cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1479d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1480d522f475Smrg			TFullWidth(tekscr),
1481d522f475Smrg			TFullHeight(tekscr));
1482d522f475Smrg	} else
1483d522f475Smrg#endif
1484d522f475Smrg	{
1485d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1486d522f475Smrg			FullWidth(screen),
1487d522f475Smrg			FullHeight(screen));
1488d522f475Smrg	}
1489d4fba8b9Smrg	XtReleaseGC((Widget) xw, visualGC);
1490d522f475Smrg    }
1491d522f475Smrg}
1492d522f475Smrg
1493d522f475Smrg/* ARGSUSED */
1494d522f475Smrgvoid
1495d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1496d522f475Smrg			 XtPointer data GCC_UNUSED,
14979a64e1c5Smrg			 XEvent *ev,
1498fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1499d522f475Smrg{
1500d522f475Smrg    TScreen *screen = TScreenOf(term);
1501d522f475Smrg
1502d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1503d522f475Smrg	screen->bellInProgress = False;
1504d522f475Smrg    }
1505d522f475Smrg}
1506d522f475Smrg
15073367019cSmrgvoid
1508d4fba8b9SmrgxtermWarning(const char *fmt, ...)
15093367019cSmrg{
15103367019cSmrg    int save_err = errno;
15113367019cSmrg    va_list ap;
15123367019cSmrg
1513dfb07bc7Smrg    fflush(stdout);
1514d4fba8b9Smrg
1515d4fba8b9Smrg#if OPT_TRACE
1516d4fba8b9Smrg    va_start(ap, fmt);
1517d4fba8b9Smrg    Trace("xtermWarning: ");
1518d4fba8b9Smrg    TraceVA(fmt, ap);
1519d4fba8b9Smrg    va_end(ap);
1520d4fba8b9Smrg#endif
1521d4fba8b9Smrg
15223367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15233367019cSmrg    va_start(ap, fmt);
15243367019cSmrg    vfprintf(stderr, fmt, ap);
15253367019cSmrg    (void) fflush(stderr);
15263367019cSmrg
15273367019cSmrg    va_end(ap);
15283367019cSmrg    errno = save_err;
15293367019cSmrg}
15303367019cSmrg
15313367019cSmrgvoid
1532d4fba8b9SmrgxtermPerror(const char *fmt, ...)
15333367019cSmrg{
15343367019cSmrg    int save_err = errno;
1535d4fba8b9Smrg    const char *msg = strerror(errno);
15363367019cSmrg    va_list ap;
15373367019cSmrg
1538dfb07bc7Smrg    fflush(stdout);
1539d4fba8b9Smrg
1540d4fba8b9Smrg#if OPT_TRACE
1541d4fba8b9Smrg    va_start(ap, fmt);
1542d4fba8b9Smrg    Trace("xtermPerror: ");
1543d4fba8b9Smrg    TraceVA(fmt, ap);
1544d4fba8b9Smrg    va_end(ap);
1545d4fba8b9Smrg#endif
1546d4fba8b9Smrg
15473367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15483367019cSmrg    va_start(ap, fmt);
15493367019cSmrg    vfprintf(stderr, fmt, ap);
15503367019cSmrg    fprintf(stderr, ": %s\n", msg);
15513367019cSmrg    (void) fflush(stderr);
15523367019cSmrg
15533367019cSmrg    va_end(ap);
15543367019cSmrg    errno = save_err;
15553367019cSmrg}
15563367019cSmrg
1557d522f475SmrgWindow
1558c219fbebSmrgWMFrameWindow(XtermWidget xw)
1559d522f475Smrg{
1560d522f475Smrg    Window win_root, win_current, *children;
1561d522f475Smrg    Window win_parent = 0;
1562d522f475Smrg    unsigned int nchildren;
1563d522f475Smrg
1564c219fbebSmrg    win_current = XtWindow(xw);
1565d522f475Smrg
1566d522f475Smrg    /* find the parent which is child of root */
1567d522f475Smrg    do {
1568d522f475Smrg	if (win_parent)
1569d522f475Smrg	    win_current = win_parent;
1570c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1571d522f475Smrg		   win_current,
1572d522f475Smrg		   &win_root,
1573d522f475Smrg		   &win_parent,
1574d522f475Smrg		   &children,
1575d522f475Smrg		   &nchildren);
1576d522f475Smrg	XFree(children);
1577d522f475Smrg    } while (win_root != win_parent);
1578d522f475Smrg
1579d522f475Smrg    return win_current;
1580d522f475Smrg}
1581d522f475Smrg
1582d522f475Smrg#if OPT_DABBREV
1583d522f475Smrg/*
1584d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1585d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1586d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1587d522f475Smrg * expansions and ignores one of them if they are identical.
1588d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1589d522f475Smrg */
1590d522f475Smrg
1591d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1592d522f475Smrg
1593d522f475Smrgstatic int
1594fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1595d522f475Smrg{
1596b7c89284Ssnj    int result = -1;
1597b7c89284Ssnj    int firstLine = -(screen->savedlines);
1598d522f475Smrg
1599b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1600b7c89284Ssnj    while (cell->row >= firstLine) {
1601b7c89284Ssnj	if (--(cell->col) >= 0) {
1602b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1603b7c89284Ssnj	    break;
1604b7c89284Ssnj	}
1605b7c89284Ssnj	if (--(cell->row) < firstLine)
1606b7c89284Ssnj	    break;		/* ...there is no previous line */
1607b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1608b7c89284Ssnj	cell->col = MaxCols(screen);
1609b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1610b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1611d522f475Smrg	    break;
1612b7c89284Ssnj	}
1613d522f475Smrg    }
1614b7c89284Ssnj    return result;
1615d522f475Smrg}
1616d522f475Smrg
1617d522f475Smrgstatic char *
16189a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1619d522f475Smrg{
16209a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1621d522f475Smrg    char *abword;
1622d522f475Smrg    int c;
16239a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1624190d7dceSmrg    char *result = NULL;
1625d522f475Smrg
1626b7c89284Ssnj    abword = ab_end;
1627d522f475Smrg    *abword = '\0';		/* end of string marker */
1628d522f475Smrg
1629b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1630b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
16319a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1632b7c89284Ssnj	    *(--abword) = (char) c;
1633d522f475Smrg    }
1634d522f475Smrg
1635b7c89284Ssnj    if (c >= 0) {
1636b7c89284Ssnj	result = abword;
1637b7c89284Ssnj    } else if (abword != ab_end) {
1638b7c89284Ssnj	result = abword;
1639b7c89284Ssnj    }
1640b7c89284Ssnj
1641190d7dceSmrg    if (result != NULL) {
1642b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1643b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1644b7c89284Ssnj	    ;			/* skip preceding spaces */
1645b7c89284Ssnj	}
1646b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1647b7c89284Ssnj    }
1648b7c89284Ssnj    return result;
1649d522f475Smrg}
1650d522f475Smrg
1651d522f475Smrgstatic int
16529a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1653d522f475Smrg{
16549a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1655d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1656d522f475Smrg
1657b7c89284Ssnj    static CELL cell;
1658190d7dceSmrg    static char *dabbrev_hint = NULL, *lastexpansion = NULL;
1659d522f475Smrg    static unsigned int expansions;
1660d522f475Smrg
1661d522f475Smrg    char *expansion;
1662d522f475Smrg    size_t hint_len;
1663b7c89284Ssnj    int result = 0;
1664b7c89284Ssnj    LineData *ld;
1665d522f475Smrg
1666d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1667d522f475Smrg	expansions = 0;
1668b7c89284Ssnj	cell.col = screen->cur_col;
1669b7c89284Ssnj	cell.row = screen->cur_row;
1670b7c89284Ssnj
1671d4fba8b9Smrg	free(dabbrev_hint);
1672b7c89284Ssnj
1673190d7dceSmrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != NULL) {
1674b7c89284Ssnj
1675d4fba8b9Smrg	    free(lastexpansion);
1676b7c89284Ssnj
1677190d7dceSmrg	    if ((lastexpansion = strdup(dabbrev_hint)) != NULL) {
1678b7c89284Ssnj
1679b7c89284Ssnj		/* make own copy */
1680190d7dceSmrg		if ((dabbrev_hint = strdup(dabbrev_hint)) != NULL) {
1681b7c89284Ssnj		    screen->dabbrev_working = True;
1682b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1683b7c89284Ssnj		}
1684cd3331d0Smrg	    } else {
1685cd3331d0Smrg		return result;
1686b7c89284Ssnj	    }
1687cd3331d0Smrg	} else {
1688cd3331d0Smrg	    return result;
1689d522f475Smrg	}
1690b7c89284Ssnj	if (!screen->dabbrev_working) {
1691d4fba8b9Smrg	    free(lastexpansion);
1692190d7dceSmrg	    lastexpansion = NULL;
1693b7c89284Ssnj	    return result;
1694b7c89284Ssnj	}
1695d522f475Smrg    }
1696d522f475Smrg
1697190d7dceSmrg    if (dabbrev_hint == NULL)
1698cd3331d0Smrg	return result;
1699cd3331d0Smrg
1700d522f475Smrg    hint_len = strlen(dabbrev_hint);
1701d522f475Smrg    for (;;) {
1702190d7dceSmrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == NULL) {
1703d522f475Smrg	    if (expansions >= 2) {
1704d522f475Smrg		expansions = 0;
1705b7c89284Ssnj		cell.col = screen->cur_col;
1706b7c89284Ssnj		cell.row = screen->cur_row;
1707d522f475Smrg		continue;
1708d522f475Smrg	    }
1709d522f475Smrg	    break;
1710d522f475Smrg	}
1711d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1712d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1713d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1714d522f475Smrg	    break;
1715d522f475Smrg    }
1716d522f475Smrg
1717190d7dceSmrg    if (expansion != NULL) {
1718037a25ddSmrg	Char *copybuffer;
1719037a25ddSmrg	size_t del_cnt = strlen(lastexpansion) - hint_len;
1720037a25ddSmrg	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1721b7c89284Ssnj
1722190d7dceSmrg	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != NULL) {
1723b7c89284Ssnj	    /* delete previous expansion */
1724b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1725b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1726b7c89284Ssnj		    expansion + hint_len,
1727b7c89284Ssnj		    strlen(expansion) - hint_len);
1728d1603babSmrg	    v_write(pty, copybuffer, buf_cnt);
1729b7c89284Ssnj	    /* v_write() just reset our flag */
1730b7c89284Ssnj	    screen->dabbrev_working = True;
1731b7c89284Ssnj	    free(copybuffer);
1732b7c89284Ssnj
1733b7c89284Ssnj	    free(lastexpansion);
1734b7c89284Ssnj
1735190d7dceSmrg	    if ((lastexpansion = strdup(expansion)) != NULL) {
1736b7c89284Ssnj		result = 1;
1737b7c89284Ssnj		expansions++;
1738b7c89284Ssnj	    }
1739b7c89284Ssnj	}
1740b7c89284Ssnj    }
1741b7c89284Ssnj
1742b7c89284Ssnj    return result;
1743d522f475Smrg}
1744d522f475Smrg
1745d522f475Smrg/*ARGSUSED*/
1746d522f475Smrgvoid
1747b7c89284SsnjHandleDabbrevExpand(Widget w,
17489a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1749fa3f02f3Smrg		    String *params GCC_UNUSED,
1750d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1751d522f475Smrg{
1752b7c89284Ssnj    XtermWidget xw;
1753b7c89284Ssnj
1754cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1755190d7dceSmrg    if ((xw = getXtermWidget(w)) != NULL) {
17569a64e1c5Smrg	if (!dabbrev_expand(xw))
1757cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1758d522f475Smrg    }
1759d522f475Smrg}
1760d522f475Smrg#endif /* OPT_DABBREV */
1761d522f475Smrg
1762d4fba8b9Smrgvoid
1763d4fba8b9SmrgxtermDeiconify(XtermWidget xw)
1764d4fba8b9Smrg{
1765d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1766d4fba8b9Smrg    Display *dpy = screen->display;
1767d4fba8b9Smrg    Window target = VShellWindow(xw);
1768d4fba8b9Smrg    XEvent e;
1769980988aeSmrg    Atom atom_state = CachedInternAtom(dpy, "_NET_ACTIVE_WINDOW");
1770d4fba8b9Smrg
1771d4fba8b9Smrg    if (xtermIsIconified(xw)) {
1772d4fba8b9Smrg	TRACE(("...de-iconify window %#lx\n", target));
1773980988aeSmrg	ResetHiddenHint(xw);
1774d4fba8b9Smrg	XMapWindow(dpy, target);
1775d4fba8b9Smrg
1776d4fba8b9Smrg	memset(&e, 0, sizeof(e));
1777d4fba8b9Smrg	e.xclient.type = ClientMessage;
1778d4fba8b9Smrg	e.xclient.message_type = atom_state;
1779d4fba8b9Smrg	e.xclient.display = dpy;
1780d4fba8b9Smrg	e.xclient.window = target;
1781d4fba8b9Smrg	e.xclient.format = 32;
1782d4fba8b9Smrg	e.xclient.data.l[0] = 1;
1783d4fba8b9Smrg	e.xclient.data.l[1] = CurrentTime;
1784d4fba8b9Smrg
1785d4fba8b9Smrg	XSendEvent(dpy, DefaultRootWindow(dpy), False,
1786d4fba8b9Smrg		   SubstructureRedirectMask | SubstructureNotifyMask, &e);
1787d4fba8b9Smrg	xevents(xw);
1788d4fba8b9Smrg    }
1789d4fba8b9Smrg}
1790d4fba8b9Smrg
1791d4fba8b9Smrgvoid
1792d4fba8b9SmrgxtermIconify(XtermWidget xw)
1793d4fba8b9Smrg{
1794d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1795d4fba8b9Smrg    Window target = VShellWindow(xw);
1796d4fba8b9Smrg
1797d4fba8b9Smrg    if (!xtermIsIconified(xw)) {
1798d4fba8b9Smrg	TRACE(("...iconify window %#lx\n", target));
1799d4fba8b9Smrg	XIconifyWindow(screen->display,
1800d4fba8b9Smrg		       target,
1801d4fba8b9Smrg		       DefaultScreen(screen->display));
1802d4fba8b9Smrg	xevents(xw);
1803d4fba8b9Smrg    }
1804d4fba8b9Smrg}
1805d4fba8b9Smrg
1806d4fba8b9SmrgBoolean
1807d4fba8b9SmrgxtermIsIconified(XtermWidget xw)
1808d4fba8b9Smrg{
1809d4fba8b9Smrg    XWindowAttributes win_attrs;
1810d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1811d4fba8b9Smrg    Window target = VShellWindow(xw);
1812d4fba8b9Smrg    Display *dpy = screen->display;
1813d4fba8b9Smrg    Boolean result = False;
1814d4fba8b9Smrg
1815d4fba8b9Smrg    if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
1816d4fba8b9Smrg	Atom actual_return_type;
1817d4fba8b9Smrg	int actual_format_return = 0;
1818d4fba8b9Smrg	unsigned long nitems_return = 0;
1819d4fba8b9Smrg	unsigned long bytes_after_return = 0;
1820190d7dceSmrg	unsigned char *prop_return = NULL;
1821d4fba8b9Smrg	long long_length = 1024;
1822d4fba8b9Smrg	Atom requested_type = XA_ATOM;
1823980988aeSmrg	Atom is_hidden = CachedInternAtom(dpy, "_NET_WM_STATE_HIDDEN");
1824980988aeSmrg	Atom wm_state = CachedInternAtom(dpy, "_NET_WM_STATE");
1825d4fba8b9Smrg
1826d4fba8b9Smrg	/* this works with non-EWMH */
1827d4fba8b9Smrg	result = (win_attrs.map_state != IsViewable) ? True : False;
1828d4fba8b9Smrg
1829d4fba8b9Smrg	/* this is a convention used by some EWMH applications */
1830d4fba8b9Smrg	if (xtermGetWinProp(dpy,
1831d4fba8b9Smrg			    target,
1832d4fba8b9Smrg			    wm_state,
1833d4fba8b9Smrg			    0L,
1834d4fba8b9Smrg			    long_length,
1835d4fba8b9Smrg			    requested_type,
1836d4fba8b9Smrg			    &actual_return_type,
1837d4fba8b9Smrg			    &actual_format_return,
1838d4fba8b9Smrg			    &nitems_return,
1839d4fba8b9Smrg			    &bytes_after_return,
184050027b5bSmrg			    &prop_return)) {
1841190d7dceSmrg	    if (prop_return != NULL
184250027b5bSmrg		&& actual_return_type == requested_type
184350027b5bSmrg		&& actual_format_return == 32) {
184450027b5bSmrg		unsigned long n;
184550027b5bSmrg		for (n = 0; n < nitems_return; ++n) {
184650027b5bSmrg		    unsigned long check = (((unsigned long *)
184750027b5bSmrg					    (void *) prop_return)[n]);
184850027b5bSmrg		    if (check == is_hidden) {
184950027b5bSmrg			result = True;
185050027b5bSmrg			break;
185150027b5bSmrg		    }
1852d4fba8b9Smrg		}
185350027b5bSmrg		XFree(prop_return);
1854d4fba8b9Smrg	    }
1855d4fba8b9Smrg	}
1856d4fba8b9Smrg    }
1857d4fba8b9Smrg    TRACE(("...window %#lx is%s iconified\n",
1858d4fba8b9Smrg	   target,
1859d4fba8b9Smrg	   result ? "" : " not"));
1860d4fba8b9Smrg    return result;
1861d4fba8b9Smrg}
1862d4fba8b9Smrg
1863d522f475Smrg#if OPT_MAXIMIZE
1864d522f475Smrg/*ARGSUSED*/
1865d522f475Smrgvoid
1866b7c89284SsnjHandleDeIconify(Widget w,
18679a64e1c5Smrg		XEvent *event GCC_UNUSED,
1868fa3f02f3Smrg		String *params GCC_UNUSED,
1869d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1870d522f475Smrg{
1871b7c89284Ssnj    XtermWidget xw;
1872b7c89284Ssnj
1873190d7dceSmrg    if ((xw = getXtermWidget(w)) != NULL) {
1874d4fba8b9Smrg	xtermDeiconify(xw);
1875d522f475Smrg    }
1876d522f475Smrg}
1877d522f475Smrg
1878d522f475Smrg/*ARGSUSED*/
1879d522f475Smrgvoid
1880b7c89284SsnjHandleIconify(Widget w,
18819a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1882fa3f02f3Smrg	      String *params GCC_UNUSED,
1883d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1884d522f475Smrg{
1885b7c89284Ssnj    XtermWidget xw;
1886b7c89284Ssnj
1887190d7dceSmrg    if ((xw = getXtermWidget(w)) != NULL) {
1888d4fba8b9Smrg	xtermIconify(xw);
1889d522f475Smrg    }
1890d522f475Smrg}
1891d522f475Smrg
1892d522f475Smrgint
1893c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1894d522f475Smrg{
1895c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1896d522f475Smrg    XSizeHints hints;
1897d522f475Smrg    long supp = 0;
1898d522f475Smrg    Window root_win;
1899d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1900d522f475Smrg    int root_y = -1;
1901d522f475Smrg    unsigned root_border;
1902d522f475Smrg    unsigned root_depth;
19033367019cSmrg    int code;
1904d522f475Smrg
1905d522f475Smrg    if (XGetGeometry(screen->display,
1906c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1907d522f475Smrg		     &root_win,
1908d522f475Smrg		     &root_x,
1909d522f475Smrg		     &root_y,
1910d522f475Smrg		     width,
1911d522f475Smrg		     height,
1912d522f475Smrg		     &root_border,
1913d522f475Smrg		     &root_depth)) {
1914d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1915d522f475Smrg	       root_x,
1916d522f475Smrg	       root_y,
1917d522f475Smrg	       *width,
1918d522f475Smrg	       *height,
1919d522f475Smrg	       root_border));
1920d522f475Smrg
1921d522f475Smrg	*width -= (root_border * 2);
1922d522f475Smrg	*height -= (root_border * 2);
1923d522f475Smrg
1924d522f475Smrg	hints.flags = PMaxSize;
1925d522f475Smrg	if (XGetWMNormalHints(screen->display,
1926c219fbebSmrg			      VShellWindow(xw),
1927d522f475Smrg			      &hints,
1928d522f475Smrg			      &supp)
1929d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1930d522f475Smrg
1931d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1932d522f475Smrg		   hints.max_width,
1933d522f475Smrg		   hints.max_height));
1934d522f475Smrg
1935d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1936b7c89284Ssnj		*width = (unsigned) hints.max_width;
1937d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1938b7c89284Ssnj		*height = (unsigned) hints.max_height;
1939d522f475Smrg	}
19403367019cSmrg	code = 1;
19413367019cSmrg    } else {
19423367019cSmrg	*width = 0;
19433367019cSmrg	*height = 0;
19443367019cSmrg	code = 0;
1945d522f475Smrg    }
19463367019cSmrg    return code;
1947d522f475Smrg}
1948d522f475Smrg
1949d522f475Smrgvoid
1950c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1951d522f475Smrg{
1952c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1953d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1954d4fba8b9Smrg    unsigned root_width = 0, root_height = 0;
19553367019cSmrg    Boolean success = False;
1956d522f475Smrg
19573367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
19583367019cSmrg	   maximize,
19593367019cSmrg	   (maximize
19603367019cSmrg	    ? "maximize"
19613367019cSmrg	    : "restore")));
1962d522f475Smrg
19633367019cSmrg    /*
19643367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
19653367019cSmrg     * as well as the estimated root-window size.
19663367019cSmrg     */
19673367019cSmrg    if (maximize
19683367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
19693367019cSmrg	&& xtermGetWinAttrs(screen->display,
19703367019cSmrg			    WMFrameWindow(xw),
19713367019cSmrg			    &wm_attrs)
19723367019cSmrg	&& xtermGetWinAttrs(screen->display,
19733367019cSmrg			    VShellWindow(xw),
19743367019cSmrg			    &vshell_attrs)) {
19753367019cSmrg
19763367019cSmrg	if (screen->restore_data != True
19773367019cSmrg	    || screen->restore_width != root_width
19783367019cSmrg	    || screen->restore_height != root_height) {
19793367019cSmrg	    screen->restore_data = True;
1980d4fba8b9Smrg	    screen->restore_x = wm_attrs.x;
1981d4fba8b9Smrg	    screen->restore_y = wm_attrs.y;
19823367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
19833367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
19843367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1985d522f475Smrg		   screen->restore_x,
1986d522f475Smrg		   screen->restore_y,
1987d522f475Smrg		   screen->restore_width,
1988d522f475Smrg		   screen->restore_height));
19893367019cSmrg	}
1990d522f475Smrg
19913367019cSmrg	/* subtract wm decoration dimensions */
1992d4fba8b9Smrg	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
1993d4fba8b9Smrg	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
19943367019cSmrg	success = True;
19953367019cSmrg    } else if (screen->restore_data) {
19963367019cSmrg	success = True;
19973367019cSmrg	maximize = 0;
19983367019cSmrg    }
19993367019cSmrg
20003367019cSmrg    if (success) {
20013367019cSmrg	switch (maximize) {
20023367019cSmrg	case 3:
20033367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
20043367019cSmrg	    break;
20053367019cSmrg	case 2:
20063367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
20073367019cSmrg	    break;
20083367019cSmrg	case 1:
20093367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
2010d4fba8b9Smrg	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
2011d4fba8b9Smrg		   0,
2012d4fba8b9Smrg		   0,
2013d4fba8b9Smrg		   root_width,
2014d4fba8b9Smrg		   root_height));
20153367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
2016d4fba8b9Smrg			      0,	/* x */
2017d4fba8b9Smrg			      0,	/* y */
20183367019cSmrg			      root_width,
20193367019cSmrg			      root_height);
20203367019cSmrg	    break;
20213367019cSmrg
20223367019cSmrg	default:
20233367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
20243367019cSmrg	    if (screen->restore_data) {
20253367019cSmrg		screen->restore_data = False;
20263367019cSmrg
2027d4fba8b9Smrg		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
20283367019cSmrg		       screen->restore_x,
20293367019cSmrg		       screen->restore_y,
20303367019cSmrg		       screen->restore_width,
20313367019cSmrg		       screen->restore_height));
20323367019cSmrg
20333367019cSmrg		XMoveResizeWindow(screen->display,
20343367019cSmrg				  VShellWindow(xw),
20353367019cSmrg				  screen->restore_x,
20363367019cSmrg				  screen->restore_y,
20373367019cSmrg				  screen->restore_width,
20383367019cSmrg				  screen->restore_height);
20393367019cSmrg	    }
20403367019cSmrg	    break;
2041d522f475Smrg	}
2042d522f475Smrg    }
2043d522f475Smrg}
2044d522f475Smrg
2045d522f475Smrg/*ARGSUSED*/
2046d522f475Smrgvoid
2047b7c89284SsnjHandleMaximize(Widget w,
20489a64e1c5Smrg	       XEvent *event GCC_UNUSED,
2049fa3f02f3Smrg	       String *params GCC_UNUSED,
2050d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
2051d522f475Smrg{
2052b7c89284Ssnj    XtermWidget xw;
2053b7c89284Ssnj
2054190d7dceSmrg    if ((xw = getXtermWidget(w)) != NULL) {
2055b7c89284Ssnj	RequestMaximize(xw, 1);
2056d522f475Smrg    }
2057d522f475Smrg}
2058d522f475Smrg
2059d522f475Smrg/*ARGSUSED*/
2060d522f475Smrgvoid
2061b7c89284SsnjHandleRestoreSize(Widget w,
20629a64e1c5Smrg		  XEvent *event GCC_UNUSED,
2063fa3f02f3Smrg		  String *params GCC_UNUSED,
2064d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
2065d522f475Smrg{
2066b7c89284Ssnj    XtermWidget xw;
2067b7c89284Ssnj
2068190d7dceSmrg    if ((xw = getXtermWidget(w)) != NULL) {
2069b7c89284Ssnj	RequestMaximize(xw, 0);
2070d522f475Smrg    }
2071d522f475Smrg}
2072d522f475Smrg#endif /* OPT_MAXIMIZE */
2073d522f475Smrg
2074d522f475Smrgvoid
2075d522f475SmrgRedraw(void)
2076d522f475Smrg{
2077d4fba8b9Smrg    XtermWidget xw = term;
2078d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2079d522f475Smrg    XExposeEvent event;
2080d522f475Smrg
2081d522f475Smrg    TRACE(("Redraw\n"));
2082d522f475Smrg
2083d522f475Smrg    event.type = Expose;
2084d522f475Smrg    event.display = screen->display;
2085d522f475Smrg    event.x = 0;
2086d522f475Smrg    event.y = 0;
2087d522f475Smrg    event.count = 0;
2088d522f475Smrg
2089d522f475Smrg    if (VWindow(screen)) {
2090d522f475Smrg	event.window = VWindow(screen);
2091d4fba8b9Smrg	event.width = xw->core.width;
2092d4fba8b9Smrg	event.height = xw->core.height;
2093d4fba8b9Smrg	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
2094d4fba8b9Smrg						     (XEvent *) &event,
2095d4fba8b9Smrg						     NULL);
2096d522f475Smrg	if (ScrollbarWidth(screen)) {
2097d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
20989a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
2099d522f475Smrg	}
2100d522f475Smrg    }
2101d522f475Smrg#if OPT_TEK4014
2102d4fba8b9Smrg    if (TEK4014_SHOWN(xw)) {
2103cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
2104d522f475Smrg	event.window = TWindow(tekscr);
2105d522f475Smrg	event.width = tekWidget->core.width;
2106d522f475Smrg	event.height = tekWidget->core.height;
21079a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
2108d522f475Smrg    }
2109d522f475Smrg#endif
2110d522f475Smrg}
2111d522f475Smrg
2112d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
2113d522f475Smrg
2114d522f475Smrgvoid
2115d522f475Smrgtimestamp_filename(char *dst, const char *src)
2116d522f475Smrg{
2117d522f475Smrg    time_t tstamp;
2118d522f475Smrg    struct tm *tstruct;
2119d522f475Smrg
2120d522f475Smrg    tstamp = time((time_t *) 0);
2121d522f475Smrg    tstruct = localtime(&tstamp);
2122d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
2123d522f475Smrg	    src,
21243367019cSmrg	    (int) tstruct->tm_year + 1900,
2125d522f475Smrg	    tstruct->tm_mon + 1,
2126d522f475Smrg	    tstruct->tm_mday,
2127d522f475Smrg	    tstruct->tm_hour,
2128d522f475Smrg	    tstruct->tm_min,
2129d522f475Smrg	    tstruct->tm_sec);
2130d522f475Smrg}
2131d522f475Smrg
2132d1603babSmrg#if OPT_SCREEN_DUMPS
2133d4fba8b9SmrgFILE *
2134d4fba8b9Smrgcreate_printfile(XtermWidget xw, const char *suffix)
2135d4fba8b9Smrg{
2136d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2137d4fba8b9Smrg    char fname[1024];
2138d4fba8b9Smrg    int fd;
2139d4fba8b9Smrg    FILE *fp;
2140d4fba8b9Smrg
2141190d7dceSmrg#if defined(HAVE_STRFTIME)
2142d4fba8b9Smrg    {
2143d4fba8b9Smrg	char format[1024];
2144d4fba8b9Smrg	time_t now;
2145d4fba8b9Smrg	struct tm *ltm;
2146d4fba8b9Smrg
2147d4fba8b9Smrg	now = time((time_t *) 0);
2148d4fba8b9Smrg	ltm = localtime(&now);
2149d4fba8b9Smrg
2150d4fba8b9Smrg	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2151d4fba8b9Smrg	if (strftime(fname, sizeof fname, format, ltm) == 0) {
2152d4fba8b9Smrg	    sprintf(fname, "xterm%s", suffix);
2153d4fba8b9Smrg	}
2154d4fba8b9Smrg    }
2155d4fba8b9Smrg#else
2156d4fba8b9Smrg    sprintf(fname, "xterm%s", suffix);
2157d4fba8b9Smrg#endif
2158d4fba8b9Smrg    fd = open_userfile(screen->uid, screen->gid, fname, False);
2159d4fba8b9Smrg    fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2160d4fba8b9Smrg    return fp;
2161d4fba8b9Smrg}
2162d1603babSmrg#endif /* OPT_SCREEN_DUMPS */
2163d4fba8b9Smrg
2164d1603babSmrg#if OPT_SCREEN_DUMPS || defined(ALLOWLOGGING)
2165d522f475Smrgint
2166d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2167d522f475Smrg{
2168d522f475Smrg    int fd;
2169d522f475Smrg    struct stat sb;
2170d522f475Smrg
2171d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
2172d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
2173d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2174d522f475Smrg	int the_error = errno;
21753367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
21763367019cSmrg		     path,
21773367019cSmrg		     the_error,
21783367019cSmrg		     SysErrorMsg(the_error));
2179d522f475Smrg	return -1;
2180d522f475Smrg    }
2181d522f475Smrg
2182d522f475Smrg    /*
2183d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
2184d522f475Smrg     * we do any damage, and that it is not world-writable.
2185d522f475Smrg     */
2186d522f475Smrg    if (fstat(fd, &sb) < 0
2187d522f475Smrg	|| sb.st_uid != uid
2188d522f475Smrg	|| (sb.st_mode & 022) != 0) {
21893367019cSmrg	xtermWarning("you do not own %s\n", path);
2190d522f475Smrg	close(fd);
2191d522f475Smrg	return -1;
2192d522f475Smrg    }
2193d522f475Smrg    return fd;
2194d522f475Smrg}
2195d522f475Smrg
2196d522f475Smrg/*
2197d522f475Smrg * Create a file only if we could with the permissions of the real user id.
2198d522f475Smrg * We could emulate this with careful use of access() and following
2199d522f475Smrg * symbolic links, but that is messy and has race conditions.
2200d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2201d522f475Smrg * being available.
2202d522f475Smrg *
2203d522f475Smrg * Note: When called for user logging, we have ensured that the real and
2204d522f475Smrg * effective user ids are the same, so this remains as a convenience function
2205d522f475Smrg * for the debug logs.
2206d522f475Smrg *
2207d522f475Smrg * Returns
2208d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
2209d522f475Smrg *	-1 on error, e.g., cannot fork
2210d522f475Smrg *	 0 otherwise.
2211d522f475Smrg */
2212d522f475Smrgint
2213712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2214d522f475Smrg{
2215d522f475Smrg    int fd;
2216d522f475Smrg    pid_t pid;
2217d522f475Smrg    int retval = 0;
2218d522f475Smrg    int childstat = 0;
2219d522f475Smrg#ifndef HAVE_WAITPID
2220d522f475Smrg    int waited;
22213367019cSmrg    void (*chldfunc) (int);
2222d522f475Smrg
2223d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
2224d522f475Smrg#endif /* HAVE_WAITPID */
2225d522f475Smrg
2226d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2227d522f475Smrg	   (int) uid, (int) geteuid(),
2228d522f475Smrg	   (int) gid, (int) getegid(),
2229d522f475Smrg	   append,
2230d522f475Smrg	   pathname,
2231d522f475Smrg	   mode));
2232d522f475Smrg
2233d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
2234d522f475Smrg	fd = open(pathname,
2235d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2236d522f475Smrg		  mode);
2237d522f475Smrg	if (fd >= 0)
2238d522f475Smrg	    close(fd);
2239d522f475Smrg	return (fd >= 0);
2240d522f475Smrg    }
2241d522f475Smrg
2242d522f475Smrg    pid = fork();
2243d522f475Smrg    switch (pid) {
2244d522f475Smrg    case 0:			/* child */
2245d522f475Smrg	if (setgid(gid) == -1
2246d522f475Smrg	    || setuid(uid) == -1) {
2247d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
2248d522f475Smrg	    retval = 1;
2249d522f475Smrg	} else {
2250d522f475Smrg	    fd = open(pathname,
2251d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2252d522f475Smrg		      mode);
2253d522f475Smrg	    if (fd >= 0) {
2254d522f475Smrg		close(fd);
2255d522f475Smrg		retval = 0;
2256d522f475Smrg	    } else {
2257d522f475Smrg		retval = 1;
2258d522f475Smrg	    }
2259d522f475Smrg	}
2260d522f475Smrg	_exit(retval);
2261d522f475Smrg	/* NOTREACHED */
2262d522f475Smrg    case -1:			/* error */
2263d522f475Smrg	return retval;
2264d522f475Smrg    default:			/* parent */
2265d522f475Smrg#ifdef HAVE_WAITPID
2266d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
2267d522f475Smrg#ifdef EINTR
2268d522f475Smrg	    if (errno == EINTR)
2269d522f475Smrg		continue;
2270d522f475Smrg#endif /* EINTR */
2271d522f475Smrg#ifdef ERESTARTSYS
2272d522f475Smrg	    if (errno == ERESTARTSYS)
2273d522f475Smrg		continue;
2274d522f475Smrg#endif /* ERESTARTSYS */
2275d522f475Smrg	    break;
2276d522f475Smrg	}
2277d522f475Smrg#else /* HAVE_WAITPID */
2278d522f475Smrg	waited = wait(&childstat);
2279d522f475Smrg	signal(SIGCHLD, chldfunc);
2280d522f475Smrg	/*
2281d522f475Smrg	   Since we had the signal handler uninstalled for a while,
2282d522f475Smrg	   we might have missed the termination of our screen child.
2283d522f475Smrg	   If we can check for this possibility without hanging, do so.
2284d522f475Smrg	 */
2285d522f475Smrg	do
2286cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
22873367019cSmrg		NormalExit();
2288d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
2289d522f475Smrg#endif /* HAVE_WAITPID */
2290d522f475Smrg#ifndef WIFEXITED
2291d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
2292d522f475Smrg#endif
2293d522f475Smrg	if (WIFEXITED(childstat))
2294d522f475Smrg	    retval = 1;
2295d522f475Smrg	return retval;
2296d522f475Smrg    }
2297d522f475Smrg}
2298d1603babSmrg#endif /* OPT_SCREEN_DUMPS || defined(ALLOWLOGGING) */
2299d522f475Smrg
2300d522f475Smrgint
2301fa3f02f3SmrgxtermResetIds(TScreen *screen)
2302d522f475Smrg{
2303d522f475Smrg    int result = 0;
2304d522f475Smrg    if (setgid(screen->gid) == -1) {
23053367019cSmrg	xtermWarning("unable to reset group-id\n");
2306d522f475Smrg	result = -1;
2307d522f475Smrg    }
2308d522f475Smrg    if (setuid(screen->uid) == -1) {
23093367019cSmrg	xtermWarning("unable to reset user-id\n");
2310d522f475Smrg	result = -1;
2311d522f475Smrg    }
2312d522f475Smrg    return result;
2313d522f475Smrg}
2314d522f475Smrg
2315d522f475Smrg#ifdef ALLOWLOGGING
2316d522f475Smrg
2317d522f475Smrg/*
2318d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
2319d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
2320d522f475Smrg */
2321d522f475Smrg
2322d522f475Smrg#ifdef ALLOWLOGFILEEXEC
23233367019cSmrgstatic void
2324d4fba8b9Smrghandle_SIGPIPE(int sig GCC_UNUSED)
2325d522f475Smrg{
2326cd3331d0Smrg    XtermWidget xw = term;
2327cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2328d522f475Smrg
23293367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2330d522f475Smrg#ifdef SYSV
2331d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2332d522f475Smrg#endif /* SYSV */
2333d522f475Smrg    if (screen->logging)
2334cd3331d0Smrg	CloseLog(xw);
2335d522f475Smrg}
2336d4fba8b9Smrg
2337d4fba8b9Smrg/*
2338d4fba8b9Smrg * Open a command to pipe log data to it.
2339d4fba8b9Smrg * Warning, enabling this "feature" allows arbitrary programs
2340d4fba8b9Smrg * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2341d4fba8b9Smrg * done through escape sequences....  You have been warned.
2342d4fba8b9Smrg */
2343d4fba8b9Smrgstatic void
2344d4fba8b9SmrgStartLogExec(TScreen *screen)
2345d4fba8b9Smrg{
2346d4fba8b9Smrg    int pid;
2347d4fba8b9Smrg    int p[2];
2348d4fba8b9Smrg    static char *shell;
2349d4fba8b9Smrg    struct passwd pw;
2350d4fba8b9Smrg
2351d4fba8b9Smrg    if ((shell = x_getenv("SHELL")) == NULL) {
2352d4fba8b9Smrg
2353d4fba8b9Smrg	if (x_getpwuid(screen->uid, &pw)) {
2354d4fba8b9Smrg	    char *name = x_getlogin(screen->uid, &pw);
2355d4fba8b9Smrg	    if (*(pw.pw_shell)) {
2356d4fba8b9Smrg		shell = pw.pw_shell;
2357d4fba8b9Smrg	    }
2358d4fba8b9Smrg	    free(name);
2359d4fba8b9Smrg	}
2360d4fba8b9Smrg    }
2361d4fba8b9Smrg
2362190d7dceSmrg    if (shell == NULL) {
2363d4fba8b9Smrg	static char dummy[] = "/bin/sh";
2364d4fba8b9Smrg	shell = dummy;
2365d4fba8b9Smrg    }
2366d4fba8b9Smrg
2367d4fba8b9Smrg    if (access(shell, X_OK) != 0) {
2368d4fba8b9Smrg	xtermPerror("Can't execute `%s'\n", shell);
2369d4fba8b9Smrg	return;
2370d4fba8b9Smrg    }
2371d4fba8b9Smrg
2372d4fba8b9Smrg    if (pipe(p) < 0) {
2373d4fba8b9Smrg	xtermPerror("Can't make a pipe connection\n");
2374d4fba8b9Smrg	return;
2375d4fba8b9Smrg    } else if ((pid = fork()) < 0) {
2376d4fba8b9Smrg	xtermPerror("Can't fork...\n");
2377d4fba8b9Smrg	return;
2378d4fba8b9Smrg    }
2379d4fba8b9Smrg    if (pid == 0) {		/* child */
2380d4fba8b9Smrg	/*
2381d4fba8b9Smrg	 * Close our output (we won't be talking back to the
2382d4fba8b9Smrg	 * parent), and redirect our child's output to the
2383d4fba8b9Smrg	 * original stderr.
2384d4fba8b9Smrg	 */
2385d4fba8b9Smrg	close(p[1]);
2386d4fba8b9Smrg	dup2(p[0], 0);
2387d4fba8b9Smrg	close(p[0]);
2388d4fba8b9Smrg	dup2(fileno(stderr), 1);
2389d4fba8b9Smrg	dup2(fileno(stderr), 2);
2390d4fba8b9Smrg
2391d4fba8b9Smrg	close(fileno(stderr));
2392d4fba8b9Smrg	close(ConnectionNumber(screen->display));
2393d4fba8b9Smrg	close(screen->respond);
2394d4fba8b9Smrg
2395d4fba8b9Smrg	signal(SIGHUP, SIG_DFL);
2396d4fba8b9Smrg	signal(SIGCHLD, SIG_DFL);
2397d4fba8b9Smrg
2398d4fba8b9Smrg	/* (this is redundant) */
2399d4fba8b9Smrg	if (xtermResetIds(screen) < 0)
2400d4fba8b9Smrg	    exit(ERROR_SETUID);
2401d4fba8b9Smrg
2402d4fba8b9Smrg	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2403d4fba8b9Smrg	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2404d4fba8b9Smrg	exit(ERROR_LOGEXEC);
2405d4fba8b9Smrg    }
2406d4fba8b9Smrg    close(p[0]);
2407d4fba8b9Smrg    screen->logfd = p[1];
2408d4fba8b9Smrg    signal(SIGPIPE, handle_SIGPIPE);
2409d4fba8b9Smrg}
2410d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2411d522f475Smrg
2412d4fba8b9Smrg/*
2413d4fba8b9Smrg * Generate a path for a logfile if no default path is given.
2414d4fba8b9Smrg */
2415d4fba8b9Smrgstatic char *
2416d4fba8b9SmrgGenerateLogPath(void)
2417d4fba8b9Smrg{
2418d4fba8b9Smrg    static char *log_default = NULL;
2419d4fba8b9Smrg
2420d4fba8b9Smrg    /* once opened we just reuse the same log name */
2421d4fba8b9Smrg    if (log_default)
2422d4fba8b9Smrg	return (log_default);
2423d4fba8b9Smrg
2424d4fba8b9Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2425d4fba8b9Smrg    {
2426d4fba8b9Smrg#define LEN_HOSTNAME 255
2427d4fba8b9Smrg	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
2428d4fba8b9Smrg	 * the total length of a domain name (i.e., label octets and label
2429d4fba8b9Smrg	 * length octets) is restricted to 255 octets or less.''
2430d4fba8b9Smrg	 */
2431d4fba8b9Smrg#define LEN_GETPID 9
2432d4fba8b9Smrg	/*
2433d4fba8b9Smrg	 * This is arbitrary...
2434d4fba8b9Smrg	 */
2435d4fba8b9Smrg	const char form[] = "Xterm.log.%s%s.%lu";
2436d4fba8b9Smrg	char where[LEN_HOSTNAME + 1];
2437d4fba8b9Smrg	char when[LEN_TIMESTAMP];
2438d4fba8b9Smrg	time_t now = time((time_t *) 0);
2439d4fba8b9Smrg	struct tm *ltm = (struct tm *) localtime(&now);
2440d4fba8b9Smrg
2441d4fba8b9Smrg	if ((gethostname(where, sizeof(where)) == 0) &&
2442d4fba8b9Smrg	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2443d4fba8b9Smrg	    ((log_default = (char *) malloc((sizeof(form)
2444d4fba8b9Smrg					     + strlen(where)
2445d4fba8b9Smrg					     + strlen(when)
2446d4fba8b9Smrg					     + LEN_GETPID))) != NULL)) {
2447d4fba8b9Smrg	    (void) sprintf(log_default,
2448d4fba8b9Smrg			   form,
2449d4fba8b9Smrg			   where, when,
2450d4fba8b9Smrg			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
2451d4fba8b9Smrg	}
2452d4fba8b9Smrg    }
2453d4fba8b9Smrg#else
2454980988aeSmrg    {
2455980988aeSmrg	static const char log_def_name[] = "XtermLog.XXXXXX";
2456980988aeSmrg	if ((log_default = x_strdup(log_def_name)) != NULL) {
2457980988aeSmrg	    MakeTemp(log_default);
2458980988aeSmrg	}
2459d4fba8b9Smrg    }
2460d4fba8b9Smrg#endif
2461d4fba8b9Smrg
2462d4fba8b9Smrg    return (log_default);
2463d4fba8b9Smrg}
2464d4fba8b9Smrg
2465d522f475Smrgvoid
2466cd3331d0SmrgStartLog(XtermWidget xw)
2467d522f475Smrg{
2468cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2469d522f475Smrg
2470d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2471d522f475Smrg	return;
24723367019cSmrg
2473d4fba8b9Smrg    /* if we weren't supplied with a logfile path, generate one */
2474d4fba8b9Smrg    if (IsEmpty(screen->logfile))
2475d4fba8b9Smrg	screen->logfile = GenerateLogPath();
24763367019cSmrg
2477d4fba8b9Smrg    /* give up if we were unable to allocate the filename */
2478d4fba8b9Smrg    if (!screen->logfile)
2479d4fba8b9Smrg	return;
2480d522f475Smrg
2481d4fba8b9Smrg    if (*screen->logfile == '|') {	/* exec command */
2482d4fba8b9Smrg#ifdef ALLOWLOGFILEEXEC
2483d4fba8b9Smrg	StartLogExec(screen);
2484d522f475Smrg#else
2485cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2486cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2487d522f475Smrg	return;
2488d522f475Smrg#endif
2489d4fba8b9Smrg    } else if (strcmp(screen->logfile, "-") == 0) {
2490d4fba8b9Smrg	screen->logfd = STDOUT_FILENO;
2491d522f475Smrg    } else {
2492d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2493d522f475Smrg					   screen->gid,
2494d522f475Smrg					   screen->logfile,
2495d4fba8b9Smrg					   True)) < 0)
2496d522f475Smrg	    return;
2497d522f475Smrg    }
2498d522f475Smrg    screen->logstart = VTbuffer->next;
2499d522f475Smrg    screen->logging = True;
2500d522f475Smrg    update_logging();
2501d522f475Smrg}
2502d522f475Smrg
2503d522f475Smrgvoid
2504cd3331d0SmrgCloseLog(XtermWidget xw)
2505d522f475Smrg{
2506cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2507cd3331d0Smrg
2508d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2509d522f475Smrg	return;
2510cd3331d0Smrg    FlushLog(xw);
2511d522f475Smrg    close(screen->logfd);
2512d522f475Smrg    screen->logging = False;
2513d522f475Smrg    update_logging();
2514d522f475Smrg}
2515d522f475Smrg
2516d522f475Smrgvoid
2517cd3331d0SmrgFlushLog(XtermWidget xw)
2518d522f475Smrg{
2519cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2520cd3331d0Smrg
2521d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2522d522f475Smrg	Char *cp;
2523d1603babSmrg	size_t i;
2524d522f475Smrg
2525d522f475Smrg	cp = VTbuffer->next;
2526190d7dceSmrg	if (screen->logstart != NULL
2527d1603babSmrg	    && (i = (size_t) (cp - screen->logstart)) > 0) {
2528d1603babSmrg	    IGNORE_RC(write(screen->logfd, screen->logstart, i));
2529d522f475Smrg	}
2530d522f475Smrg	screen->logstart = VTbuffer->next;
2531d522f475Smrg    }
2532d522f475Smrg}
2533d522f475Smrg
2534d522f475Smrg#endif /* ALLOWLOGGING */
2535d522f475Smrg
2536d522f475Smrg/***====================================================================***/
2537d522f475Smrg
2538d4fba8b9Smrgstatic unsigned
2539d4fba8b9SmrgmaskToShift(unsigned long mask)
2540d4fba8b9Smrg{
2541d4fba8b9Smrg    unsigned result = 0;
2542d4fba8b9Smrg    if (mask != 0) {
2543d4fba8b9Smrg	while ((mask & 1) == 0) {
2544d4fba8b9Smrg	    mask >>= 1;
2545d4fba8b9Smrg	    ++result;
2546d4fba8b9Smrg	}
2547d4fba8b9Smrg    }
2548d4fba8b9Smrg    return result;
2549d4fba8b9Smrg}
2550d4fba8b9Smrg
2551d4fba8b9Smrgstatic unsigned
2552d4fba8b9SmrgmaskToWidth(unsigned long mask)
2553d4fba8b9Smrg{
2554d4fba8b9Smrg    unsigned result = 0;
2555d4fba8b9Smrg    while (mask != 0) {
2556d4fba8b9Smrg	if ((mask & 1) != 0)
2557d4fba8b9Smrg	    ++result;
2558d4fba8b9Smrg	mask >>= 1;
2559d4fba8b9Smrg    }
2560d4fba8b9Smrg    return result;
2561d4fba8b9Smrg}
2562d4fba8b9Smrg
2563c48a5815SmrgXVisualInfo *
2564fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2565fa3f02f3Smrg{
2566fa3f02f3Smrg#define MYFMT "getVisualInfo \
2567fa3f02f3Smrgdepth %d, \
2568fa3f02f3Smrgtype %d (%s), \
2569fa3f02f3Smrgsize %d \
2570fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2571fa3f02f3Smrg#define MYARG \
2572fa3f02f3Smrg       vi->depth,\
2573fa3f02f3Smrg       vi->class,\
2574fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2575fa3f02f3Smrg       vi->colormap_size,\
2576fa3f02f3Smrg       vi->red_mask,\
2577fa3f02f3Smrg       vi->green_mask,\
2578fa3f02f3Smrg       vi->blue_mask
2579d522f475Smrg
2580fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2581fa3f02f3Smrg    Display *dpy = screen->display;
2582fa3f02f3Smrg    XVisualInfo myTemplate;
2583fa3f02f3Smrg
2584190d7dceSmrg    if (xw->visInfo == NULL && xw->numVisuals == 0) {
2585fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2586fa3f02f3Smrg								XDefaultScreen(dpy)));
2587fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2588fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2589fa3f02f3Smrg
2590190d7dceSmrg	if ((xw->visInfo != NULL) && (xw->numVisuals > 0)) {
2591fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2592d4fba8b9Smrg	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2593d4fba8b9Smrg	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2594d4fba8b9Smrg	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2595d4fba8b9Smrg	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2596d4fba8b9Smrg	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2597d4fba8b9Smrg	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2598d4fba8b9Smrg
2599d4fba8b9Smrg	    xw->has_rgb = ((vi->red_mask != 0) &&
2600d4fba8b9Smrg			   (vi->green_mask != 0) &&
2601d4fba8b9Smrg			   (vi->blue_mask != 0) &&
2602d4fba8b9Smrg			   ((vi->red_mask & vi->green_mask) == 0) &&
2603d4fba8b9Smrg			   ((vi->green_mask & vi->blue_mask) == 0) &&
2604c48a5815Smrg			   ((vi->blue_mask & vi->red_mask) == 0) &&
2605d1603babSmrg			   xw->rgb_widths[0] <= (unsigned) vi->bits_per_rgb &&
2606d1603babSmrg			   xw->rgb_widths[1] <= (unsigned) vi->bits_per_rgb &&
2607d1603babSmrg			   xw->rgb_widths[2] <= (unsigned) vi->bits_per_rgb &&
2608c48a5815Smrg			   (vi->class == TrueColor
2609c48a5815Smrg			    || vi->class == DirectColor));
2610d4fba8b9Smrg
2611fa3f02f3Smrg	    if (resource.reportColors) {
2612fa3f02f3Smrg		printf(MYFMT, MYARG);
2613fa3f02f3Smrg	    }
2614fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2615d4fba8b9Smrg	    TRACE(("...shifts %u/%u/%u\n",
2616d4fba8b9Smrg		   xw->rgb_shifts[0],
2617d4fba8b9Smrg		   xw->rgb_shifts[1],
2618d4fba8b9Smrg		   xw->rgb_shifts[2]));
2619c48a5815Smrg	    TRACE(("...widths %u/%u/%u\n",
2620c48a5815Smrg		   xw->rgb_widths[0],
2621c48a5815Smrg		   xw->rgb_widths[1],
2622c48a5815Smrg		   xw->rgb_widths[2]));
2623fa3f02f3Smrg	}
2624fa3f02f3Smrg    }
2625190d7dceSmrg    return (xw->visInfo != NULL) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
2626fa3f02f3Smrg#undef MYFMT
2627fa3f02f3Smrg#undef MYARG
2628fa3f02f3Smrg}
26293367019cSmrg
26309a64e1c5Smrg#if OPT_ISO_COLORS
2631d4fba8b9Smrgstatic Bool
2632d4fba8b9SmrgReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
26339a64e1c5Smrg{
2634d4fba8b9Smrg    Bool result = False;
2635d4fba8b9Smrg
26369a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
26379a64e1c5Smrg	XColor color;
26389a64e1c5Smrg	char buffer[80];
26399a64e1c5Smrg
26409a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
26419a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2642c48a5815Smrg	(void) QueryOneColor(xw, &color);
2643d4fba8b9Smrg	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2644d4fba8b9Smrg		opcode,
2645d4fba8b9Smrg		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
26469a64e1c5Smrg		color.red,
26479a64e1c5Smrg		color.green,
26489a64e1c5Smrg		color.blue);
26499a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
26509a64e1c5Smrg	unparseputs(xw, buffer);
26519a64e1c5Smrg	unparseputc1(xw, final);
2652d4fba8b9Smrg	result = True;
26539a64e1c5Smrg    }
2654d4fba8b9Smrg    return result;
26559a64e1c5Smrg}
26569a64e1c5Smrg
2657fa3f02f3Smrgstatic void
2658fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2659fa3f02f3Smrg{
2660fa3f02f3Smrg    if (getVisualInfo(xw)) {
2661fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2662fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2663fa3f02f3Smrg    } else {
2664fa3f02f3Smrg	*typep = 0;
2665fa3f02f3Smrg	*sizep = 0;
2666fa3f02f3Smrg    }
26673367019cSmrg}
26683367019cSmrg
26693367019cSmrg#define MAX_COLORTABLE 4096
26703367019cSmrg
26713367019cSmrg/*
26723367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
26733367019cSmrg */
26743367019cSmrgstatic Boolean
26753367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
26763367019cSmrg{
26773367019cSmrg    Colormap cmap = xw->core.colormap;
26783367019cSmrg    TScreen *screen = TScreenOf(xw);
2679190d7dceSmrg    Boolean result = (screen->cmap_data != NULL);
26803367019cSmrg
2681fa3f02f3Smrg    if (!result
26823367019cSmrg	&& length != 0
26833367019cSmrg	&& length < MAX_COLORTABLE) {
26843367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2685037a25ddSmrg
2686190d7dceSmrg	if (screen->cmap_data != NULL) {
2687037a25ddSmrg	    unsigned i;
2688d4fba8b9Smrg	    unsigned shift;
2689d4fba8b9Smrg
2690d4fba8b9Smrg	    if (getVisualInfo(xw))
2691d4fba8b9Smrg		shift = xw->rgb_shifts[2];
2692d4fba8b9Smrg	    else
2693d4fba8b9Smrg		shift = 0;
2694037a25ddSmrg
26953367019cSmrg	    screen->cmap_size = length;
26963367019cSmrg
26973367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
2698d4fba8b9Smrg		screen->cmap_data[i].pixel = (unsigned long) i << shift;
26993367019cSmrg	    }
27003367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
27013367019cSmrg					     cmap,
27023367019cSmrg					     screen->cmap_data,
27033367019cSmrg					     (int) screen->cmap_size) != 0);
27043367019cSmrg	}
27053367019cSmrg    }
2706d522f475Smrg    return result;
2707d522f475Smrg}
2708d522f475Smrg
2709c48a5815Smrg/***====================================================================***/
2710c48a5815Smrg
2711c48a5815Smrg/*
2712c48a5815Smrg * Call this function with def->{red,green,blue} initialized, to obtain a pixel
2713c48a5815Smrg * value.
2714c48a5815Smrg */
2715c48a5815SmrgBoolean
2716c48a5815SmrgAllocOneColor(XtermWidget xw, XColor *def)
2717c48a5815Smrg{
2718c48a5815Smrg    TScreen *screen = TScreenOf(xw);
2719c48a5815Smrg    Boolean result = True;
2720c48a5815Smrg
2721c48a5815Smrg#define MaskIt(name,nn) \
2722c48a5815Smrg	((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
2723c48a5815Smrg	             << xw->rgb_shifts[nn]) \
2724c48a5815Smrg	 & xw->visInfo->name ##_mask)
2725c48a5815Smrg
2726d1603babSmrg#define VisualIsRGB(xw) (getVisualInfo(xw) != NULL && xw->has_rgb && xw->visInfo->bits_per_rgb <= 8)
2727d1603babSmrg
2728d1603babSmrg    if (VisualIsRGB(xw)) {
2729c48a5815Smrg	def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
2730c48a5815Smrg    } else {
2731c48a5815Smrg	Display *dpy = screen->display;
2732c48a5815Smrg	if (!XAllocColor(dpy, xw->core.colormap, def)) {
2733c48a5815Smrg	    /*
2734c48a5815Smrg	     * Decide between foreground and background by a grayscale
2735c48a5815Smrg	     * approximation.
2736c48a5815Smrg	     */
2737c48a5815Smrg	    int bright = def->red * 3 + def->green * 10 + def->blue;
2738c48a5815Smrg	    int levels = 14 * 0x8000;
2739c48a5815Smrg	    def->pixel = ((bright >= levels)
2740c48a5815Smrg			  ? xw->dft_background
2741c48a5815Smrg			  : xw->dft_foreground);
2742d1603babSmrg	    TRACE(("XAllocColor failed, for %04x/%04x/%04x: choose %08lx (%d vs %d)\n",
2743d1603babSmrg		   def->red, def->green, def->blue,
2744d1603babSmrg		   def->pixel, bright, levels));
2745c48a5815Smrg	    result = False;
2746c48a5815Smrg	}
2747c48a5815Smrg    }
2748c48a5815Smrg    return result;
2749c48a5815Smrg}
2750c48a5815Smrg
2751c48a5815Smrg/***====================================================================***/
2752c48a5815Smrg
2753c48a5815Smrg/*
2754c48a5815Smrg * Call this function with def->pixel set to the color that we want to convert
2755c48a5815Smrg * to separate red/green/blue.
2756c48a5815Smrg */
2757c48a5815SmrgBoolean
2758c48a5815SmrgQueryOneColor(XtermWidget xw, XColor *def)
2759c48a5815Smrg{
2760c48a5815Smrg    Boolean result = True;
2761c48a5815Smrg
2762c48a5815Smrg#define UnMaskIt(name,nn) \
2763c48a5815Smrg	((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
2764c48a5815Smrg#define UnMaskIt2(name,nn) \
2765c48a5815Smrg	(unsigned short)((((UnMaskIt(name,nn) << 8) \
2766c48a5815Smrg			   |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
2767c48a5815Smrg
2768d1603babSmrg    if (VisualIsRGB(xw)) {
2769c48a5815Smrg	/* *INDENT-EQLS* */
2770c48a5815Smrg	def->red   = UnMaskIt2(red, 0);
2771c48a5815Smrg	def->green = UnMaskIt2(green, 1);
2772c48a5815Smrg	def->blue  = UnMaskIt2(blue, 2);
2773d1603babSmrg    } else {
2774d1603babSmrg	Display *dpy = TScreenOf(xw)->display;
2775d1603babSmrg	if (!XQueryColor(dpy, xw->core.colormap, def)) {
2776d1603babSmrg	    TRACE(("XQueryColor failed, given %08lx\n", def->pixel));
2777d1603babSmrg	    result     = False;
2778d1603babSmrg	}
2779c48a5815Smrg    }
2780c48a5815Smrg    return result;
2781c48a5815Smrg}
2782c48a5815Smrg
2783c48a5815Smrg/***====================================================================***/
2784c48a5815Smrg
2785d522f475Smrg/*
2786d522f475Smrg * Find closest color for "def" in "cmap".
2787d522f475Smrg * Set "def" to the resulting color.
2788d522f475Smrg *
2789d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2790d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2791d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2792d522f475Smrg *
2793d522f475Smrg * Return False if not able to find or allocate a color.
2794d522f475Smrg */
2795d522f475Smrgstatic Boolean
2796c48a5815SmrgallocateClosestRGB(XtermWidget xw, XColor *def)
2797d522f475Smrg{
27983367019cSmrg    TScreen *screen = TScreenOf(xw);
2799d522f475Smrg    Boolean result = False;
28003367019cSmrg    unsigned cmap_type;
2801d522f475Smrg    unsigned cmap_size;
2802d522f475Smrg
2803fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2804d522f475Smrg
28053367019cSmrg    if ((cmap_type & 1) != 0) {
28063367019cSmrg
28073367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2808037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2809d522f475Smrg
2810190d7dceSmrg	    if (tried != NULL) {
2811037a25ddSmrg		unsigned attempts;
2812d522f475Smrg
2813d522f475Smrg		/*
2814d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2815d522f475Smrg		 * approximation to the requested color.
2816d522f475Smrg		 */
2817d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2818d522f475Smrg		    Boolean first = True;
2819037a25ddSmrg		    double bestRGB = 0.0;
2820037a25ddSmrg		    unsigned bestInx = 0;
2821037a25ddSmrg		    unsigned i;
2822d522f475Smrg
2823d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2824d522f475Smrg			if (!tried[bestInx]) {
2825037a25ddSmrg			    double diff, thisRGB = 0.0;
2826037a25ddSmrg
2827d522f475Smrg			    /*
2828d522f475Smrg			     * Look for the best match based on luminance.
2829d522f475Smrg			     * Measure this by the least-squares difference of
2830d522f475Smrg			     * the weighted R/G/B components from the color map
2831d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2832d522f475Smrg			     * component of the YIQ color space model for
2833d522f475Smrg			     * weights that correspond to the luminance.
2834d522f475Smrg			     */
2835d522f475Smrg#define AddColorWeight(weight, color) \
28363367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2837037a25ddSmrg			    thisRGB += diff * diff
2838d522f475Smrg
2839d522f475Smrg			    AddColorWeight(0.30, red);
2840d522f475Smrg			    AddColorWeight(0.61, green);
2841d522f475Smrg			    AddColorWeight(0.11, blue);
2842d522f475Smrg
2843d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2844d522f475Smrg				first = False;
2845d522f475Smrg				bestInx = i;
2846d522f475Smrg				bestRGB = thisRGB;
2847d522f475Smrg			    }
2848d522f475Smrg			}
2849d522f475Smrg		    }
2850c48a5815Smrg		    if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
28513367019cSmrg			*def = screen->cmap_data[bestInx];
28523367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
28533367019cSmrg			       def->green, def->blue));
2854d522f475Smrg			result = True;
2855d522f475Smrg			break;
2856d522f475Smrg		    }
2857d522f475Smrg		    /*
2858d522f475Smrg		     * It failed - either the color map entry was readonly, or
2859d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2860d522f475Smrg		     * so we will ignore it
2861d522f475Smrg		     */
2862d522f475Smrg		    tried[bestInx] = True;
2863d522f475Smrg		}
2864d522f475Smrg		free(tried);
2865d522f475Smrg	    }
2866d522f475Smrg	}
2867d522f475Smrg    }
2868d522f475Smrg    return result;
2869d522f475Smrg}
2870d522f475Smrg
28713367019cSmrg#ifndef ULONG_MAX
28723367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
28733367019cSmrg#endif
28743367019cSmrg
2875d522f475Smrg/*
2876d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2877d522f475Smrg * to 256.
2878d522f475Smrg *
2879d522f475Smrg * Returns
2880d522f475Smrg *	-1 on error
2881d522f475Smrg *	0 on no change
2882d522f475Smrg *	1 if a new color was allocated.
2883d522f475Smrg */
2884d522f475Smrgstatic int
2885d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2886d522f475Smrg		  ColorRes * res,
2887cd3331d0Smrg		  const char *spec)
2888d522f475Smrg{
2889d522f475Smrg    int result;
2890d522f475Smrg    XColor def;
2891d522f475Smrg
28923367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2893c48a5815Smrg	if (res->mode == True &&
2894c48a5815Smrg	    EQL_COLOR_RES(res, def.pixel)) {
2895d522f475Smrg	    result = 0;
2896d522f475Smrg	} else {
2897d522f475Smrg	    result = 1;
2898d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
28993367019cSmrg	    res->red = def.red;
29003367019cSmrg	    res->green = def.green;
29013367019cSmrg	    res->blue = def.blue;
29023367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
29033367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
29043367019cSmrg		   def.red,
29053367019cSmrg		   def.green,
29063367019cSmrg		   def.blue,
29073367019cSmrg		   def.pixel));
2908d522f475Smrg	    if (!res->mode)
2909d522f475Smrg		result = 0;
2910d522f475Smrg	    res->mode = True;
2911d522f475Smrg	}
2912d522f475Smrg    } else {
2913d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2914d522f475Smrg	result = -1;
2915d522f475Smrg    }
2916d522f475Smrg    return (result);
2917d522f475Smrg}
2918d522f475Smrg
2919d522f475SmrgPixel
2920cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2921d522f475Smrg{
2922d522f475Smrg    Pixel result = 0;
2923d522f475Smrg
2924d522f475Smrg    if (res->mode) {
2925d522f475Smrg	result = res->value;
2926d522f475Smrg    } else {
2927d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2928cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2929d522f475Smrg
2930cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2931cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2932d522f475Smrg
2933cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2934cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2935d522f475Smrg		res->mode = -True;
29363367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
29373367019cSmrg			     NonNull(res->resource));
2938d522f475Smrg	    }
2939d522f475Smrg	    result = res->value;
2940d522f475Smrg	} else {
2941d522f475Smrg	    result = 0;
2942d522f475Smrg	}
2943d522f475Smrg    }
2944d522f475Smrg    return result;
2945d522f475Smrg}
2946d522f475Smrg
2947cd3331d0Smrgstatic int
2948cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2949cd3331d0Smrg{
2950cd3331d0Smrg    int code;
2951cd3331d0Smrg
2952cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2953cd3331d0Smrg	code = -1;
2954cd3331d0Smrg    } else {
2955cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2956cd3331d0Smrg
2957cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2958cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2959cd3331d0Smrg    }
2960cd3331d0Smrg    return code;
2961cd3331d0Smrg}
2962cd3331d0Smrg
2963cd3331d0Smrg/*
2964cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2965cd3331d0Smrg * values from the given buffer.
2966cd3331d0Smrg *
2967cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2968cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2969cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2970cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2971cd3331d0Smrg * 'first' set to the beginning of those indices.
2972cd3331d0Smrg *
2973cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2974cd3331d0Smrg */
2975d522f475Smrgstatic Bool
2976d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2977d4fba8b9Smrg		       int opcode,
2978d522f475Smrg		       char *buf,
2979cd3331d0Smrg		       int first,
2980d522f475Smrg		       int final)
2981d522f475Smrg{
2982d522f475Smrg    int repaint = False;
2983d522f475Smrg    int code;
2984cd3331d0Smrg    int last = (MAXCOLORS - first);
2985d4fba8b9Smrg    int queried = 0;
2986d522f475Smrg
2987d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2988d522f475Smrg
2989d522f475Smrg    while (buf && *buf) {
2990037a25ddSmrg	int color;
2991037a25ddSmrg	char *name = strchr(buf, ';');
2992037a25ddSmrg
2993d522f475Smrg	if (name == NULL)
2994d522f475Smrg	    break;
2995d522f475Smrg	*name = '\0';
2996d522f475Smrg	name++;
2997d522f475Smrg	color = atoi(buf);
2998cd3331d0Smrg	if (color < 0 || color >= last)
2999cd3331d0Smrg	    break;		/* quit on any error */
3000d522f475Smrg	buf = strchr(name, ';');
3001d522f475Smrg	if (buf) {
3002d522f475Smrg	    *buf = '\0';
3003d522f475Smrg	    buf++;
3004d522f475Smrg	}
3005cd3331d0Smrg	if (!strcmp(name, "?")) {
3006d4fba8b9Smrg	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3007d4fba8b9Smrg		++queried;
3008cd3331d0Smrg	} else {
3009cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
3010d522f475Smrg	    if (code < 0) {
3011d522f475Smrg		/* stop on any error */
3012d522f475Smrg		break;
3013d522f475Smrg	    } else if (code > 0) {
3014d522f475Smrg		repaint = True;
3015d522f475Smrg	    }
3016d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
3017d522f475Smrg	     * change style (dynamic colors).
3018d522f475Smrg	     */
3019d522f475Smrg	}
3020d522f475Smrg    }
3021d4fba8b9Smrg    if (queried)
3022d4fba8b9Smrg	unparse_end(xw);
3023d522f475Smrg
3024d522f475Smrg    return (repaint);
3025d522f475Smrg}
3026cd3331d0Smrg
3027cd3331d0Smrgstatic Bool
3028cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
3029cd3331d0Smrg{
3030cd3331d0Smrg    Bool repaint = False;
3031cd3331d0Smrg    int last = MAXCOLORS - start;
3032cd3331d0Smrg
3033cd3331d0Smrg    if (color >= 0 && color < last) {
3034cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3035cd3331d0Smrg
3036cd3331d0Smrg	if (res->mode) {
3037cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
3038cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3039cd3331d0Smrg		repaint = True;
3040cd3331d0Smrg	    }
3041cd3331d0Smrg	}
3042cd3331d0Smrg    }
3043cd3331d0Smrg    return repaint;
3044cd3331d0Smrg}
3045cd3331d0Smrg
3046cd3331d0Smrgint
3047cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3048cd3331d0Smrg{
3049cd3331d0Smrg    int repaint = 0;
3050cd3331d0Smrg    int color;
3051cd3331d0Smrg
3052cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3053cd3331d0Smrg    if (*buf != '\0') {
3054cd3331d0Smrg	/* reset specific colors */
3055cd3331d0Smrg	while (!IsEmpty(buf)) {
3056cd3331d0Smrg	    char *next;
3057cd3331d0Smrg
3058037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
3059037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
3060cd3331d0Smrg		break;		/* no number at all */
3061190d7dceSmrg	    if (next != NULL) {
3062190d7dceSmrg		if (strchr(";", *next) == NULL)
3063cd3331d0Smrg		    break;	/* unexpected delimiter */
3064cd3331d0Smrg		++next;
3065cd3331d0Smrg	    }
3066cd3331d0Smrg
3067cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3068cd3331d0Smrg		++repaint;
3069cd3331d0Smrg	    }
3070cd3331d0Smrg	    buf = next;
3071cd3331d0Smrg	}
3072cd3331d0Smrg    } else {
3073cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3074cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
3075cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3076cd3331d0Smrg		++repaint;
3077cd3331d0Smrg	    }
3078cd3331d0Smrg	}
3079cd3331d0Smrg    }
3080cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3081cd3331d0Smrg    return repaint;
3082cd3331d0Smrg}
3083d522f475Smrg#else
3084c48a5815Smrg#define allocateClosestRGB(xw, def) 0
3085d522f475Smrg#endif /* OPT_ISO_COLORS */
3086d522f475Smrg
3087fa3f02f3SmrgBoolean
30889a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
3089fa3f02f3Smrg{
3090c48a5815Smrg    (void) xw;
3091c48a5815Smrg    (void) def;
3092c48a5815Smrg    return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
3093fa3f02f3Smrg}
3094fa3f02f3Smrg
30953367019cSmrgstatic Boolean
30969a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
30973367019cSmrg{
30983367019cSmrg    Boolean result = False;
30993367019cSmrg    TScreen *screen = TScreenOf(xw);
31003367019cSmrg    Colormap cmap = xw->core.colormap;
31018f44fb3bSmrg    size_t have = strlen(spec);
31023367019cSmrg
31038f44fb3bSmrg    if (have == 0 || have > MAX_U_STRING) {
31048f44fb3bSmrg	if (resource.reportColors) {
3105c48a5815Smrg	    printf("color  (ignored, length %lu)\n", (unsigned long) have);
31068f44fb3bSmrg	}
31078f44fb3bSmrg    } else if (XParseColor(screen->display, cmap, spec, def)) {
3108fa3f02f3Smrg	XColor save_def = *def;
3109fa3f02f3Smrg	if (resource.reportColors) {
3110fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3111fa3f02f3Smrg		   def->red, def->green, def->blue,
3112fa3f02f3Smrg		   spec);
3113fa3f02f3Smrg	}
3114fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
3115fa3f02f3Smrg	    if (resource.reportColors) {
3116fa3f02f3Smrg		if (def->red != save_def.red ||
3117fa3f02f3Smrg		    def->green != save_def.green ||
3118fa3f02f3Smrg		    def->blue != save_def.blue) {
3119fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3120fa3f02f3Smrg			   def->red, def->green, def->blue,
3121fa3f02f3Smrg			   spec);
3122fa3f02f3Smrg		}
3123fa3f02f3Smrg	    }
3124fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3125fa3f02f3Smrg		   def->red, def->green, def->blue));
3126fa3f02f3Smrg	    result = True;
3127fa3f02f3Smrg	}
31283367019cSmrg    }
31293367019cSmrg    return result;
31303367019cSmrg}
31313367019cSmrg
31323367019cSmrg/*
31333367019cSmrg * This provides an approximation (the closest color from xterm's palette)
31343367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
31353367019cSmrg * because of the context in which it is used.
31363367019cSmrg */
31373367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
31383367019cSmrgint
31393367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
31403367019cSmrg{
31413367019cSmrg    int result = -1;
3142c48a5815Smrg#if OPT_ISO_COLORS
31433367019cSmrg    int n;
31443367019cSmrg    int best_index = -1;
31453367019cSmrg    unsigned long best_value = 0;
31463367019cSmrg    unsigned long this_value;
31473367019cSmrg    long diff_red, diff_green, diff_blue;
31483367019cSmrg
31493367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
31503367019cSmrg
31513367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
31523367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
31533367019cSmrg
31543367019cSmrg	/* ensure that we have a value for each of the colors */
31553367019cSmrg	if (!res->mode) {
31563367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
31573367019cSmrg	}
31583367019cSmrg
31593367019cSmrg	/* find the closest match */
31603367019cSmrg	if (res->mode == True) {
31613367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
31623367019cSmrg		    res->value, res->red, res->green, res->blue));
31633367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
31643367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
31653367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
31663367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
31673367019cSmrg					  + (diff_green * diff_green)
31683367019cSmrg					  + (diff_blue * diff_blue));
31693367019cSmrg	    if (best_index < 0 || this_value < best_value) {
31703367019cSmrg		best_index = n;
31713367019cSmrg		best_value = this_value;
31723367019cSmrg	    }
31733367019cSmrg	}
31743367019cSmrg    }
31753367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
31763367019cSmrg    result = best_index;
3177c48a5815Smrg
31783367019cSmrg#else
31793367019cSmrg    (void) xw;
31803367019cSmrg    (void) find_red;
31813367019cSmrg    (void) find_green;
31823367019cSmrg    (void) find_blue;
31833367019cSmrg#endif
31843367019cSmrg    return result;
31853367019cSmrg}
31863367019cSmrg
3187d4fba8b9Smrg#if OPT_DIRECT_COLOR
3188d4fba8b9Smrgint
3189d4fba8b9SmrggetDirectColor(XtermWidget xw, int red, int green, int blue)
3190d4fba8b9Smrg{
3191c48a5815Smrg    Pixel result = 0;
3192c48a5815Smrg
3193c48a5815Smrg#define getRGB(name,shift) \
3194c48a5815Smrg    do { \
3195c48a5815Smrg	Pixel value = (Pixel) name & 0xff; \
3196c48a5815Smrg	if (xw->rgb_widths[shift] < 8) { \
3197c48a5815Smrg	    value >>= (int) (8 - xw->rgb_widths[shift]); \
3198c48a5815Smrg	} \
3199c48a5815Smrg	value <<= xw->rgb_shifts[shift]; \
3200c48a5815Smrg	value &= xw->visInfo->name ##_mask; \
3201c48a5815Smrg	result |= value; \
3202c48a5815Smrg    } while (0)
3203c48a5815Smrg
3204c48a5815Smrg    getRGB(red, 0);
3205c48a5815Smrg    getRGB(green, 1);
3206c48a5815Smrg    getRGB(blue, 2);
3207c48a5815Smrg
3208c48a5815Smrg#undef getRGB
3209c48a5815Smrg
3210d4fba8b9Smrg    return (int) result;
3211d4fba8b9Smrg}
3212d4fba8b9Smrg
3213d4fba8b9Smrgstatic void
3214d4fba8b9SmrgformatDirectColor(char *target, XtermWidget xw, unsigned value)
3215d4fba8b9Smrg{
3216c48a5815Smrg    Pixel result[3];
3217c48a5815Smrg
3218c48a5815Smrg#define getRGB(name, shift) \
3219c48a5815Smrg    do { \
3220c48a5815Smrg	result[shift] = value & xw->visInfo->name ## _mask; \
3221c48a5815Smrg	result[shift] >>= xw->rgb_shifts[shift]; \
3222c48a5815Smrg	if (xw->rgb_widths[shift] < 8) \
3223c48a5815Smrg	    result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
3224c48a5815Smrg    } while(0)
3225c48a5815Smrg
3226c48a5815Smrg    getRGB(red, 0);
3227c48a5815Smrg    getRGB(green, 1);
3228c48a5815Smrg    getRGB(blue, 2);
3229c48a5815Smrg
3230c48a5815Smrg#undef getRGB
3231c48a5815Smrg
3232c48a5815Smrg    sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
3233d4fba8b9Smrg}
3234d4fba8b9Smrg#endif /* OPT_DIRECT_COLOR */
3235d4fba8b9Smrg
3236d4fba8b9Smrg#define fg2SGR(n) \
3237d4fba8b9Smrg		(n) >= 8 ? 9 : 3, \
3238d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3239d4fba8b9Smrg#define bg2SGR(n) \
3240d4fba8b9Smrg		(n) >= 8 ? 10 : 4, \
3241d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3242d4fba8b9Smrg
3243d4fba8b9Smrg#define EndOf(s) (s) + strlen(s)
3244d4fba8b9Smrg
3245d4fba8b9Smrgchar *
3246d4fba8b9SmrgxtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3247d4fba8b9Smrg{
3248d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
3249d4fba8b9Smrg    char *msg = target;
3250d4fba8b9Smrg
3251d4fba8b9Smrg    strcpy(target, "0");
3252d4fba8b9Smrg    if (attr & BOLD)
3253d4fba8b9Smrg	strcat(msg, ";1");
3254d4fba8b9Smrg    if (attr & UNDERLINE)
3255d4fba8b9Smrg	strcat(msg, ";4");
3256d4fba8b9Smrg    if (attr & BLINK)
3257d4fba8b9Smrg	strcat(msg, ";5");
3258d4fba8b9Smrg    if (attr & INVERSE)
3259d4fba8b9Smrg	strcat(msg, ";7");
3260d4fba8b9Smrg    if (attr & INVISIBLE)
3261d4fba8b9Smrg	strcat(msg, ";8");
3262d4fba8b9Smrg#if OPT_WIDE_ATTRS
3263d4fba8b9Smrg    if (attr & ATR_FAINT)
3264d4fba8b9Smrg	strcat(msg, ";2");
3265d4fba8b9Smrg    if (attr & ATR_ITALIC)
3266d4fba8b9Smrg	strcat(msg, ";3");
3267d4fba8b9Smrg    if (attr & ATR_STRIKEOUT)
3268d4fba8b9Smrg	strcat(msg, ";9");
3269d4fba8b9Smrg    if (attr & ATR_DBL_UNDER)
3270d4fba8b9Smrg	strcat(msg, ";21");
3271d4fba8b9Smrg#endif
3272d4fba8b9Smrg#if OPT_256_COLORS || OPT_88_COLORS
3273d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3274d4fba8b9Smrg	if (attr & FG_COLOR) {
3275d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3276d4fba8b9Smrg		strcat(msg, ";38:2::");
3277d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3278d4fba8b9Smrg	    }) if (fg >= 16) {
3279d4fba8b9Smrg		sprintf(EndOf(msg), ";38:5:%d", fg);
3280d4fba8b9Smrg	    } else {
3281d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3282d4fba8b9Smrg	    }
3283d4fba8b9Smrg	}
3284d4fba8b9Smrg	if (attr & BG_COLOR) {
3285d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3286d4fba8b9Smrg		strcat(msg, ";48:2::");
3287d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3288d4fba8b9Smrg	    }) if (bg >= 16) {
3289d4fba8b9Smrg		sprintf(EndOf(msg), ";48:5:%d", bg);
3290d4fba8b9Smrg	    } else {
3291d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3292d4fba8b9Smrg	    }
3293d4fba8b9Smrg	}
3294d4fba8b9Smrg    });
3295d4fba8b9Smrg#elif OPT_ISO_COLORS
3296d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3297d4fba8b9Smrg	if (attr & FG_COLOR) {
3298d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3299d4fba8b9Smrg	}
3300d4fba8b9Smrg	if (attr & BG_COLOR) {
3301d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3302d4fba8b9Smrg	}
3303d4fba8b9Smrg    });
3304d4fba8b9Smrg#else
3305d4fba8b9Smrg    (void) screen;
3306d4fba8b9Smrg    (void) fg;
3307d4fba8b9Smrg    (void) bg;
3308d4fba8b9Smrg#endif
3309d4fba8b9Smrg    return target;
3310d4fba8b9Smrg}
3311d4fba8b9Smrg
3312d522f475Smrg#if OPT_PASTE64
3313d522f475Smrgstatic void
3314fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3315d522f475Smrg{
3316d522f475Smrg#define PDATA(a,b) { a, #b }
3317d522f475Smrg    static struct {
3318d522f475Smrg	char given;
3319cd3331d0Smrg	String result;
3320d522f475Smrg    } table[] = {
3321d522f475Smrg	PDATA('s', SELECT),
3322d522f475Smrg	    PDATA('p', PRIMARY),
3323d4fba8b9Smrg	    PDATA('q', SECONDARY),
3324d522f475Smrg	    PDATA('c', CLIPBOARD),
3325d522f475Smrg	    PDATA('0', CUT_BUFFER0),
3326d522f475Smrg	    PDATA('1', CUT_BUFFER1),
3327d522f475Smrg	    PDATA('2', CUT_BUFFER2),
3328d522f475Smrg	    PDATA('3', CUT_BUFFER3),
3329d522f475Smrg	    PDATA('4', CUT_BUFFER4),
3330d522f475Smrg	    PDATA('5', CUT_BUFFER5),
3331d522f475Smrg	    PDATA('6', CUT_BUFFER6),
3332d522f475Smrg	    PDATA('7', CUT_BUFFER7),
3333d522f475Smrg    };
3334d1603babSmrg    char target_used[XtNumber(table)];
3335d522f475Smrg
3336cd3331d0Smrg    const char *base = buf;
3337d1603babSmrg    Cardinal j;
3338d1603babSmrg    Cardinal num_targets = 0;
3339d522f475Smrg
3340d522f475Smrg    TRACE(("Manipulate selection data\n"));
3341d522f475Smrg
3342d1603babSmrg    memset(target_used, 0, sizeof(target_used));
3343d522f475Smrg    while (*buf != ';' && *buf != '\0') {
3344d522f475Smrg	++buf;
3345d522f475Smrg    }
3346d522f475Smrg
3347d522f475Smrg    if (*buf == ';') {
3348190d7dceSmrg	char select_code[XtNumber(table) + 1];
3349190d7dceSmrg	String select_args[XtNumber(table) + 1];
3350037a25ddSmrg
3351d522f475Smrg	*buf++ = '\0';
3352d522f475Smrg	if (*base == '\0')
3353d522f475Smrg	    base = "s0";
3354d522f475Smrg
3355d1603babSmrg	while (*base != '\0') {
3356d1603babSmrg	    for (j = 0; j < XtNumber(table); ++j) {
3357d1603babSmrg		if (*base == table[j].given) {
3358d1603babSmrg		    if (!target_used[j]) {
3359d1603babSmrg			target_used[j] = 1;
3360d1603babSmrg			select_code[num_targets] = *base;
3361d1603babSmrg			select_args[num_targets++] = table[j].result;
3362d1603babSmrg			TRACE(("atom[%d] %s\n", num_targets, table[j].result));
33633367019cSmrg		    }
3364d1603babSmrg		    break;
33653367019cSmrg		}
3366d1603babSmrg	    }
3367d1603babSmrg	    ++base;
3368d1603babSmrg	}
3369d1603babSmrg	select_code[num_targets] = '\0';
3370d1603babSmrg
3371d1603babSmrg	if (!strcmp(buf, "?")) {
3372d1603babSmrg	    if (AllowWindowOps(xw, ewGetSelection)) {
3373d1603babSmrg		TRACE(("Getting selection\n"));
3374d1603babSmrg		unparseputc1(xw, ANSI_OSC);
3375d1603babSmrg		unparseputs(xw, "52");
3376d1603babSmrg		unparseputc(xw, ';');
3377d1603babSmrg
3378d1603babSmrg		unparseputs(xw, select_code);
3379d1603babSmrg		unparseputc(xw, ';');
3380d1603babSmrg
3381d1603babSmrg		/* Tell xtermGetSelection data is base64 encoded */
3382d1603babSmrg		screen->base64_paste = num_targets;
3383d1603babSmrg		screen->base64_final = final;
3384d1603babSmrg
3385d1603babSmrg		screen->selection_time =
3386d1603babSmrg		    XtLastTimestampProcessed(TScreenOf(xw)->display);
3387d1603babSmrg
3388d1603babSmrg		/* terminator will be written in this call */
3389d1603babSmrg		xtermGetSelection((Widget) xw,
3390d1603babSmrg				  screen->selection_time,
3391d1603babSmrg				  select_args, num_targets,
3392d1603babSmrg				  NULL);
3393d1603babSmrg	    }
3394d1603babSmrg	} else {
3395d1603babSmrg	    if (AllowWindowOps(xw, ewSetSelection)) {
3396d1603babSmrg		char *old = buf;
3397d1603babSmrg
3398d1603babSmrg		TRACE(("Setting selection(%s) with %s\n", select_code, buf));
3399d1603babSmrg		screen->selection_time =
3400d1603babSmrg		    XtLastTimestampProcessed(TScreenOf(xw)->display);
3401d1603babSmrg
3402d1603babSmrg		for (j = 0; j < num_targets; ++j) {
3403d1603babSmrg		    buf = old;
3404d1603babSmrg		    ClearSelectionBuffer(screen, select_args[j]);
3405d1603babSmrg		    while (*buf != '\0') {
3406d1603babSmrg			AppendToSelectionBuffer(screen,
3407d1603babSmrg						CharOf(*buf++),
3408d1603babSmrg						select_args[j]);
34093367019cSmrg		    }
34103367019cSmrg		}
3411d1603babSmrg		CompleteSelection(xw, select_args, num_targets);
3412cd3331d0Smrg	    }
3413d522f475Smrg	}
3414d522f475Smrg    }
3415d522f475Smrg}
3416d522f475Smrg#endif /* OPT_PASTE64 */
3417d522f475Smrg
3418d522f475Smrg/***====================================================================***/
3419d522f475Smrg
3420d522f475Smrgstatic Bool
3421fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3422d522f475Smrg{
3423cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3424d522f475Smrg    Bool result = False;
3425d522f475Smrg    Char *cp = *bufp;
3426d522f475Smrg    Char *next = cp;
3427d522f475Smrg
3428d522f475Smrg    (void) screen;
3429d522f475Smrg    (void) last;
3430d522f475Smrg
3431d522f475Smrg#if OPT_WIDE_CHARS
3432cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3433d522f475Smrg	PtyData data;
3434d522f475Smrg
34359a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3436980988aeSmrg	    if (!is_UCS_SPECIAL(data.utf_data)
3437d522f475Smrg		&& (data.utf_data >= 128 ||
3438d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3439d522f475Smrg		next += (data.utf_size - 1);
3440d522f475Smrg		result = True;
3441d522f475Smrg	    } else {
3442d522f475Smrg		result = False;
3443d522f475Smrg	    }
3444d522f475Smrg	} else {
3445d522f475Smrg	    result = False;
3446d522f475Smrg	}
3447d522f475Smrg    } else
3448d522f475Smrg#endif
3449d522f475Smrg#if OPT_C1_PRINT
3450d522f475Smrg	if (screen->c1_printable
3451d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3452d522f475Smrg	result = True;
3453d522f475Smrg    } else
3454d522f475Smrg#endif
3455d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3456d522f475Smrg	result = True;
3457d522f475Smrg    }
3458d522f475Smrg    *bufp = next;
3459d522f475Smrg    return result;
3460d522f475Smrg}
3461d522f475Smrg
3462d522f475Smrg/***====================================================================***/
3463d522f475Smrg
3464d522f475Smrg/*
3465d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3466cd3331d0Smrg * array indices.  Compare with TermColors.
3467d522f475Smrg */
3468d522f475Smrgtypedef enum {
3469d522f475Smrg    OSC_TEXT_FG = 10
3470d522f475Smrg    ,OSC_TEXT_BG
3471d522f475Smrg    ,OSC_TEXT_CURSOR
3472d522f475Smrg    ,OSC_MOUSE_FG
3473d522f475Smrg    ,OSC_MOUSE_BG
3474d522f475Smrg#if OPT_TEK4014
3475d522f475Smrg    ,OSC_TEK_FG = 15
3476d522f475Smrg    ,OSC_TEK_BG
3477d522f475Smrg#endif
3478d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3479d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3480d522f475Smrg#endif
3481d522f475Smrg#if OPT_TEK4014
3482d522f475Smrg    ,OSC_TEK_CURSOR = 18
3483d522f475Smrg#endif
3484d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3485d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3486d522f475Smrg#endif
3487d522f475Smrg    ,OSC_NCOLORS
3488d522f475Smrg} OscTextColors;
3489d522f475Smrg
3490cd3331d0Smrg/*
3491cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3492cd3331d0Smrg */
3493cd3331d0Smrg#define OSC_RESET 100
3494cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3495cd3331d0Smrg
3496d1603babSmrg/*
3497d1603babSmrg * Other (non-color) OSC controls
3498d1603babSmrg */
3499d1603babSmrgtypedef enum {
3500d1603babSmrg    OSC_IconBoth = 0
3501d1603babSmrg    ,OSC_IconOnly = 1
3502d1603babSmrg    ,OSC_TitleOnly = 2
3503d1603babSmrg    ,OSC_X_Property = 3
3504d1603babSmrg    ,OSC_SetAnsiColor = 4
3505d1603babSmrg    ,OSC_GetAnsiColors = 5
3506d1603babSmrg    ,OSC_ColorMode = 6
3507d1603babSmrg    ,OSC_SetupPointer = 22
3508d1603babSmrg    ,OSC_Unused_30 = 30		/* Konsole (unused) */
3509d1603babSmrg    ,OSC_Unused_31 = 31		/* Konsole (unused) */
3510d1603babSmrg    ,OSC_NewLogFile = 46
3511d1603babSmrg    ,OSC_FontOps = 50
3512d1603babSmrg    ,OSC_Unused_51		/* Emacs (unused) */
3513d1603babSmrg    ,OSC_SelectionData = 52
3514d1603babSmrg    ,OSC_AllowedOps = 60
3515d1603babSmrg    ,OSC_DisallowedOps = 61
3516d1603babSmrg} OscMiscOps;
3517d1603babSmrg
3518d522f475Smrgstatic Bool
3519d522f475SmrgGetOldColors(XtermWidget xw)
3520d522f475Smrg{
35219a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3522037a25ddSmrg	int i;
3523037a25ddSmrg
35249a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
35259a64e1c5Smrg	if (xw->work.oldColors == NULL) {
35263367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3527d522f475Smrg	    return (False);
3528d522f475Smrg	}
35299a64e1c5Smrg	xw->work.oldColors->which = 0;
3530d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
35319a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
35329a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3533d522f475Smrg	}
35349a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3535d522f475Smrg    }
3536d522f475Smrg    return (True);
3537d522f475Smrg}
3538d522f475Smrg
3539d522f475Smrgstatic int
3540d4fba8b9SmrgoppositeColor(XtermWidget xw, int n)
3541d522f475Smrg{
3542d4fba8b9Smrg    Boolean reversed = (xw->misc.re_verse);
3543d4fba8b9Smrg
3544d522f475Smrg    switch (n) {
3545d522f475Smrg    case TEXT_FG:
3546d4fba8b9Smrg	n = reversed ? TEXT_FG : TEXT_BG;
3547d522f475Smrg	break;
3548d522f475Smrg    case TEXT_BG:
3549d4fba8b9Smrg	n = reversed ? TEXT_BG : TEXT_FG;
3550d522f475Smrg	break;
3551d522f475Smrg    case MOUSE_FG:
3552d522f475Smrg	n = MOUSE_BG;
3553d522f475Smrg	break;
3554d522f475Smrg    case MOUSE_BG:
3555d522f475Smrg	n = MOUSE_FG;
3556d522f475Smrg	break;
3557d522f475Smrg#if OPT_TEK4014
3558d522f475Smrg    case TEK_FG:
3559d4fba8b9Smrg	n = reversed ? TEK_FG : TEK_BG;
3560d522f475Smrg	break;
3561d522f475Smrg    case TEK_BG:
3562d4fba8b9Smrg	n = reversed ? TEK_BG : TEK_FG;
3563d522f475Smrg	break;
3564d522f475Smrg#endif
3565d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3566d522f475Smrg    case HIGHLIGHT_FG:
3567d522f475Smrg	n = HIGHLIGHT_BG;
3568d522f475Smrg	break;
3569d522f475Smrg    case HIGHLIGHT_BG:
3570d522f475Smrg	n = HIGHLIGHT_FG;
3571d522f475Smrg	break;
3572d522f475Smrg#endif
3573d522f475Smrg    default:
3574d522f475Smrg	break;
3575d522f475Smrg    }
3576d522f475Smrg    return n;
3577d522f475Smrg}
3578d522f475Smrg
3579d4fba8b9Smrgstatic Bool
3580d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3581d522f475Smrg{
3582d4fba8b9Smrg    Bool result = False;
3583d4fba8b9Smrg
3584cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3585cd3331d0Smrg	XColor color;
3586cd3331d0Smrg	char buffer[80];
3587d522f475Smrg
3588cd3331d0Smrg	/*
3589cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3590cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3591cd3331d0Smrg	 * reporting the opposite color which would be used.
3592cd3331d0Smrg	 */
3593d4fba8b9Smrg	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3594cd3331d0Smrg
3595cd3331d0Smrg	GetOldColors(xw);
35969a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3597c48a5815Smrg	(void) QueryOneColor(xw, &color);
3598cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3599cd3331d0Smrg		color.red,
3600cd3331d0Smrg		color.green,
3601cd3331d0Smrg		color.blue);
3602712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
36039a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3604cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3605cd3331d0Smrg	unparseputs(xw, buffer);
3606cd3331d0Smrg	unparseputc1(xw, final);
3607d4fba8b9Smrg	result = True;
3608cd3331d0Smrg    }
3609d4fba8b9Smrg    return result;
3610d522f475Smrg}
3611d522f475Smrg
3612d522f475Smrgstatic Bool
3613d4fba8b9SmrgUpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3614d522f475Smrg{
3615d522f475Smrg    int i;
3616d522f475Smrg
3617d522f475Smrg    /* if we were going to free old colors, this would be the place to
3618d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3619d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3620d522f475Smrg     * we could save some overhead this way.   The only case in which this
3621d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3622d522f475Smrg     * which case they can restart xterm
3623d522f475Smrg     */
3624d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3625d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
36269a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
36279a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
36289a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3629d522f475Smrg	    }
3630d522f475Smrg	    if (pNew->names[i]) {
36319a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3632d522f475Smrg	    }
36339a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3634d522f475Smrg	}
3635d522f475Smrg    }
3636d522f475Smrg    return (True);
3637d522f475Smrg}
3638d522f475Smrg
3639d522f475Smrg/*
3640d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3641d522f475Smrg * xterm is compiled.
3642d522f475Smrg */
3643d522f475Smrgstatic int
3644d522f475SmrgOscToColorIndex(OscTextColors mode)
3645d522f475Smrg{
3646d522f475Smrg    int result = 0;
3647d522f475Smrg
3648d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3649d522f475Smrg    switch (mode) {
3650d522f475Smrg	CASE(TEXT_FG);
3651d522f475Smrg	CASE(TEXT_BG);
3652d522f475Smrg	CASE(TEXT_CURSOR);
3653d522f475Smrg	CASE(MOUSE_FG);
3654d522f475Smrg	CASE(MOUSE_BG);
3655d522f475Smrg#if OPT_TEK4014
3656d522f475Smrg	CASE(TEK_FG);
3657d522f475Smrg	CASE(TEK_BG);
3658d522f475Smrg#endif
3659d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3660d522f475Smrg	CASE(HIGHLIGHT_BG);
3661d522f475Smrg	CASE(HIGHLIGHT_FG);
3662d522f475Smrg#endif
3663d522f475Smrg#if OPT_TEK4014
3664d522f475Smrg	CASE(TEK_CURSOR);
3665d522f475Smrg#endif
3666d522f475Smrg    case OSC_NCOLORS:
3667d522f475Smrg	break;
3668d522f475Smrg    }
3669d1603babSmrg#undef CASE
3670d522f475Smrg    return result;
3671d522f475Smrg}
3672d522f475Smrg
3673d522f475Smrgstatic Bool
3674d522f475SmrgChangeColorsRequest(XtermWidget xw,
3675d522f475Smrg		    int start,
3676d522f475Smrg		    char *names,
3677d522f475Smrg		    int final)
3678d522f475Smrg{
3679d522f475Smrg    Bool result = False;
3680d522f475Smrg    ScrnColors newColors;
3681d522f475Smrg
3682d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3683d522f475Smrg
3684d522f475Smrg    if (GetOldColors(xw)) {
3685037a25ddSmrg	int i;
3686d4fba8b9Smrg	int queried = 0;
3687037a25ddSmrg
3688d522f475Smrg	newColors.which = 0;
3689d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3690d522f475Smrg	    newColors.names[i] = NULL;
3691d522f475Smrg	}
3692d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3693037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3694d522f475Smrg	    if (xw->misc.re_verse)
3695d4fba8b9Smrg		ndx = oppositeColor(xw, ndx);
3696d522f475Smrg
3697cd3331d0Smrg	    if (IsEmpty(names)) {
3698d522f475Smrg		newColors.names[ndx] = NULL;
3699d522f475Smrg	    } else {
3700037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3701037a25ddSmrg
3702d522f475Smrg		names = strchr(names, ';');
3703d522f475Smrg		if (names != NULL) {
3704d522f475Smrg		    *names++ = '\0';
3705d522f475Smrg		}
3706190d7dceSmrg		if (thisName != NULL) {
3707fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3708d4fba8b9Smrg			if (ReportColorRequest(xw, ndx, final))
3709d4fba8b9Smrg			    ++queried;
37109a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
37119a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3712fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3713fa3f02f3Smrg		    }
3714d522f475Smrg		}
3715d522f475Smrg	    }
3716d522f475Smrg	}
3717d522f475Smrg
3718d522f475Smrg	if (newColors.which != 0) {
3719d522f475Smrg	    ChangeColors(xw, &newColors);
3720d522f475Smrg	    UpdateOldColors(xw, &newColors);
3721d4fba8b9Smrg	} else if (queried) {
3722d4fba8b9Smrg	    unparse_end(xw);
3723d522f475Smrg	}
3724d522f475Smrg	result = True;
3725d522f475Smrg    }
3726d522f475Smrg    return result;
3727d522f475Smrg}
3728d522f475Smrg
3729cd3331d0Smrgstatic Bool
3730cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3731cd3331d0Smrg		   int code)
3732cd3331d0Smrg{
3733cd3331d0Smrg    Bool result = False;
3734cd3331d0Smrg
3735dfb07bc7Smrg    (void) xw;
3736dfb07bc7Smrg    (void) code;
3737dfb07bc7Smrg
3738cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3739cd3331d0Smrg    if (GetOldColors(xw)) {
3740037a25ddSmrg	ScrnColors newColors;
3741037a25ddSmrg	const char *thisName;
3742037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3743037a25ddSmrg
3744cd3331d0Smrg	if (xw->misc.re_verse)
3745d4fba8b9Smrg	    ndx = oppositeColor(xw, ndx);
3746cd3331d0Smrg
3747cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3748cd3331d0Smrg
3749cd3331d0Smrg	newColors.which = 0;
3750cd3331d0Smrg	newColors.names[ndx] = NULL;
3751cd3331d0Smrg
3752190d7dceSmrg	if (thisName != NULL
3753190d7dceSmrg	    && xw->work.oldColors->names[ndx] != NULL
37549a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3755cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3756cd3331d0Smrg
3757cd3331d0Smrg	    if (newColors.which != 0) {
3758cd3331d0Smrg		ChangeColors(xw, &newColors);
3759cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3760cd3331d0Smrg	    }
3761cd3331d0Smrg	}
3762cd3331d0Smrg	result = True;
3763cd3331d0Smrg    }
3764cd3331d0Smrg    return result;
3765cd3331d0Smrg}
3766cd3331d0Smrg
3767cd3331d0Smrg#if OPT_SHIFT_FONTS
3768cd3331d0Smrg/*
3769cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3770cd3331d0Smrg *
3771cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3772cd3331d0Smrg * the corresponding menu font entry.
3773cd3331d0Smrg */
3774cd3331d0Smrgstatic int
3775fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3776cd3331d0Smrg{
3777cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3778cd3331d0Smrg    int num = screen->menu_font_number;
3779cd3331d0Smrg    int rel = 0;
3780cd3331d0Smrg
3781cd3331d0Smrg    if (*++source == '+') {
3782cd3331d0Smrg	rel = 1;
3783cd3331d0Smrg	source++;
3784cd3331d0Smrg    } else if (*source == '-') {
3785cd3331d0Smrg	rel = -1;
3786cd3331d0Smrg	source++;
3787cd3331d0Smrg    }
3788cd3331d0Smrg
3789cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3790cd3331d0Smrg	int val = atoi(source);
3791cd3331d0Smrg	if (rel > 0)
3792cd3331d0Smrg	    rel = val;
3793cd3331d0Smrg	else if (rel < 0)
3794cd3331d0Smrg	    rel = -val;
3795cd3331d0Smrg	else
3796cd3331d0Smrg	    num = val;
3797cd3331d0Smrg    }
3798cd3331d0Smrg
3799cd3331d0Smrg    if (rel != 0) {
3800cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3801cd3331d0Smrg				     screen->menu_font_number, rel);
3802cd3331d0Smrg
3803cd3331d0Smrg    }
3804cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3805cd3331d0Smrg    *target = source;
3806cd3331d0Smrg    return num;
3807cd3331d0Smrg}
3808cd3331d0Smrg
3809cd3331d0Smrgstatic void
3810cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3811cd3331d0Smrg{
3812cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3813cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3814cd3331d0Smrg	Bool success = True;
3815cd3331d0Smrg	int num;
3816cb4a1343Smrg	String base = buf + 1;
3817190d7dceSmrg	const char *name = NULL;
3818cd3331d0Smrg
3819cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3820cd3331d0Smrg	if (num < 0
3821cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3822cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3823cd3331d0Smrg	    success = False;
3824cd3331d0Smrg	} else {
3825cd3331d0Smrg#if OPT_RENDERFONT
3826cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3827cd3331d0Smrg		name = getFaceName(xw, False);
3828cd3331d0Smrg	    } else
3829cd3331d0Smrg#endif
3830190d7dceSmrg	    if ((name = screen->MenuFontName(num)) == NULL) {
3831cd3331d0Smrg		success = False;
3832cd3331d0Smrg	    }
3833cd3331d0Smrg	}
3834cd3331d0Smrg
3835cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3836cd3331d0Smrg	unparseputs(xw, "50");
3837cd3331d0Smrg
3838cd3331d0Smrg	if (success) {
3839cd3331d0Smrg	    unparseputc(xw, ';');
3840cd3331d0Smrg	    if (buf >= base) {
3841cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3842cd3331d0Smrg		if (*buf != '\0') {
3843037a25ddSmrg		    char temp[10];
3844037a25ddSmrg
3845cd3331d0Smrg		    unparseputc(xw, '#');
3846cd3331d0Smrg		    sprintf(temp, "%d", num);
3847cd3331d0Smrg		    unparseputs(xw, temp);
3848cd3331d0Smrg		    if (*name != '\0')
3849cd3331d0Smrg			unparseputc(xw, ' ');
3850cd3331d0Smrg		}
3851cd3331d0Smrg	    }
3852cd3331d0Smrg	    unparseputs(xw, name);
3853cd3331d0Smrg	}
3854cd3331d0Smrg
3855cd3331d0Smrg	unparseputc1(xw, final);
3856cd3331d0Smrg	unparse_end(xw);
3857cd3331d0Smrg    }
3858cd3331d0Smrg}
3859cd3331d0Smrg
3860cd3331d0Smrgstatic void
3861cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3862cd3331d0Smrg{
3863cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3864cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3865cd3331d0Smrg	Bool success = True;
3866cd3331d0Smrg	int num;
3867cd3331d0Smrg	VTFontNames fonts;
3868cd3331d0Smrg	char *name;
3869cd3331d0Smrg
3870cd3331d0Smrg	/*
3871cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3872cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3873cd3331d0Smrg	 *
3874cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3875cd3331d0Smrg	 * to load the font entry.
3876cd3331d0Smrg	 */
3877cd3331d0Smrg	if (*buf == '#') {
3878cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3879cd3331d0Smrg
3880cd3331d0Smrg	    if (num < 0
3881cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3882cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3883cd3331d0Smrg		success = False;
3884cd3331d0Smrg	    } else {
3885cd3331d0Smrg		/*
3886cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3887cd3331d0Smrg		 * for a font specification within the control.
3888cd3331d0Smrg		 */
3889cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3890cd3331d0Smrg		    ++buf;
3891cd3331d0Smrg		}
3892cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3893cd3331d0Smrg		    ++buf;
3894cd3331d0Smrg		}
3895cd3331d0Smrg#if OPT_RENDERFONT
3896cd3331d0Smrg		if (UsingRenderFont(xw)) {
3897c219fbebSmrg		    /* EMPTY */
3898c219fbebSmrg		    /* there is only one font entry to load */
3899c219fbebSmrg		    ;
3900cd3331d0Smrg		} else
3901cd3331d0Smrg#endif
3902cd3331d0Smrg		{
3903cd3331d0Smrg		    /*
3904cd3331d0Smrg		     * Normally there is no font specified in the control.
3905cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3906cd3331d0Smrg		     */
3907cd3331d0Smrg		    if (*buf == '\0') {
3908190d7dceSmrg			if ((buf = screen->MenuFontName(num)) == NULL) {
3909cd3331d0Smrg			    success = False;
3910cd3331d0Smrg			}
3911cd3331d0Smrg		    }
3912cd3331d0Smrg		}
3913cd3331d0Smrg	    }
3914cd3331d0Smrg	} else {
3915cd3331d0Smrg	    num = screen->menu_font_number;
3916cd3331d0Smrg	}
3917cd3331d0Smrg	name = x_strtrim(buf);
391894644356Smrg	if (screen->EscapeFontName()) {
391994644356Smrg	    FREE_STRING(screen->EscapeFontName());
3920190d7dceSmrg	    screen->EscapeFontName() = NULL;
392194644356Smrg	}
3922cd3331d0Smrg	if (success && !IsEmpty(name)) {
3923cd3331d0Smrg#if OPT_RENDERFONT
3924cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3925cd3331d0Smrg		setFaceName(xw, name);
3926cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3927cd3331d0Smrg	    } else
3928cd3331d0Smrg#endif
3929cd3331d0Smrg	    {
3930cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3931cd3331d0Smrg		fonts.f_n = name;
3932d1603babSmrg		if (SetVTFont(xw, num, True, &fonts)
3933d1603babSmrg		    && num == screen->menu_font_number
3934d1603babSmrg		    && num != fontMenu_fontescape) {
393594644356Smrg		    screen->EscapeFontName() = x_strdup(name);
393694644356Smrg		}
3937cd3331d0Smrg	    }
3938cd3331d0Smrg	} else {
3939cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3940cd3331d0Smrg	}
394194644356Smrg	update_font_escape();
3942cd3331d0Smrg	free(name);
3943cd3331d0Smrg    }
3944cd3331d0Smrg}
3945cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3946cd3331d0Smrg
3947d522f475Smrg/***====================================================================***/
3948d522f475Smrg
3949d1603babSmrgstatic void
3950d1603babSmrgreport_allowed_ops(XtermWidget xw, int final)
3951d1603babSmrg{
3952d1603babSmrg    TScreen *screen = TScreenOf(xw);
3953d1603babSmrg    char delimiter = ';';
3954d1603babSmrg
3955d1603babSmrg    unparseputc1(xw, ANSI_OSC);
3956d1603babSmrg    unparseputn(xw, OSC_AllowedOps);
3957d1603babSmrg
3958d1603babSmrg#define CASE(name) \
3959d1603babSmrg    if (screen->name) { \
3960d1603babSmrg	unparseputc(xw, delimiter); \
3961d1603babSmrg	unparseputs(xw, #name); \
3962d1603babSmrg	delimiter = ','; \
3963d1603babSmrg    }
3964d1603babSmrg    CASE(allowColorOps);
3965d1603babSmrg    CASE(allowFontOps);
3966d1603babSmrg    CASE(allowMouseOps);
3967d1603babSmrg    CASE(allowPasteControls);
3968d1603babSmrg    CASE(allowTcapOps);
3969d1603babSmrg    CASE(allowTitleOps);
3970d1603babSmrg    CASE(allowWindowOps);
3971190d7dceSmrg    (void) delimiter;
3972d1603babSmrg#undef CASE
3973d1603babSmrg
3974d1603babSmrg    unparseputc1(xw, final);
3975d1603babSmrg}
3976d1603babSmrg
3977d1603babSmrgstatic void
3978d1603babSmrgreport_disallowed_ops(XtermWidget xw, char *value, int final)
3979d1603babSmrg{
3980d1603babSmrg    unparseputc1(xw, ANSI_OSC);
3981d1603babSmrg    unparseputn(xw, OSC_DisallowedOps);
3982d1603babSmrg    unparse_disallowed_ops(xw, value);
3983d1603babSmrg    unparseputc1(xw, final);
3984d1603babSmrg}
3985d1603babSmrg
3986d1603babSmrg/***====================================================================***/
3987d1603babSmrg
3988d522f475Smrgvoid
3989fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3990d522f475Smrg{
3991cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3992d522f475Smrg    int mode;
3993d522f475Smrg    Char *cp;
3994d522f475Smrg    int state = 0;
3995190d7dceSmrg    char *buf = NULL;
399650027b5bSmrg    char temp[20];
3997cd3331d0Smrg#if OPT_ISO_COLORS
3998cd3331d0Smrg    int ansi_colors = 0;
3999cd3331d0Smrg#endif
4000cd3331d0Smrg    Bool need_data = True;
4001fa3f02f3Smrg    Bool optional_data = False;
4002d522f475Smrg
4003d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
4004d522f475Smrg
4005712a7ff4Smrg    (void) screen;
4006712a7ff4Smrg
4007d522f475Smrg    /*
4008d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
4009d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
4010d522f475Smrg     * with the same final character as the application sends to make this
4011d522f475Smrg     * work better with shell scripts, which may have trouble reading an
4012d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
4013d522f475Smrg     */
4014d522f475Smrg    mode = 0;
4015d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
4016d522f475Smrg	switch (state) {
4017d522f475Smrg	case 0:
4018d522f475Smrg	    if (isdigit(*cp)) {
4019d522f475Smrg		mode = 10 * mode + (*cp - '0');
4020d522f475Smrg		if (mode > 65535) {
4021d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
4022d522f475Smrg		    return;
4023d522f475Smrg		}
4024d522f475Smrg		break;
4025d4fba8b9Smrg	    } else {
4026d4fba8b9Smrg		switch (*cp) {
4027d4fba8b9Smrg		case 'I':
4028d4fba8b9Smrg		    xtermLoadIcon(xw, (char *) ++cp);
4029d4fba8b9Smrg		    return;
4030d4fba8b9Smrg		case 'l':
4031d4fba8b9Smrg		    ChangeTitle(xw, (char *) ++cp);
4032d4fba8b9Smrg		    return;
4033d4fba8b9Smrg		case 'L':
4034d4fba8b9Smrg		    ChangeIconName(xw, (char *) ++cp);
4035d4fba8b9Smrg		    return;
4036d4fba8b9Smrg		}
4037d522f475Smrg	    }
4038d522f475Smrg	    /* FALLTHRU */
4039d522f475Smrg	case 1:
4040d522f475Smrg	    if (*cp != ';') {
4041d1603babSmrg		TRACE(("do_osc did not find semicolon offset %lu\n",
4042d1603babSmrg		       (unsigned long) (cp - oscbuf)));
4043d522f475Smrg		return;
4044d522f475Smrg	    }
4045d522f475Smrg	    state = 2;
4046d522f475Smrg	    break;
4047d522f475Smrg	case 2:
4048d522f475Smrg	    buf = (char *) cp;
4049d522f475Smrg	    state = 3;
4050d522f475Smrg	    /* FALLTHRU */
4051d522f475Smrg	default:
4052cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4053d522f475Smrg		switch (mode) {
4054d522f475Smrg		case 0:
4055d522f475Smrg		case 1:
4056d522f475Smrg		case 2:
4057d522f475Smrg		    break;
4058d522f475Smrg		default:
4059d1603babSmrg		    TRACE(("do_osc found nonprinting char %02X offset %lu\n",
4060d522f475Smrg			   CharOf(*cp),
4061d1603babSmrg			   (unsigned long) (cp - oscbuf)));
4062d522f475Smrg		    return;
4063d522f475Smrg		}
4064d522f475Smrg	    }
4065d522f475Smrg	}
4066d522f475Smrg    }
4067cd3331d0Smrg
40683367019cSmrg    /*
40693367019cSmrg     * Check if the palette changed and there are no more immediate changes
40703367019cSmrg     * that could be deferred to the next repaint.
40713367019cSmrg     */
4072dfb07bc7Smrg    if (xw->work.palette_changed) {
40733367019cSmrg	switch (mode) {
4074d1603babSmrg	case OSC_AllowedOps:
4075d1603babSmrg	case OSC_DisallowedOps:
4076d1603babSmrg	case OSC_FontOps:
4077d1603babSmrg	case OSC_NewLogFile:
4078d1603babSmrg	case OSC_SelectionData:
4079d1603babSmrg	case OSC_Unused_30:
4080d1603babSmrg	case OSC_Unused_31:
4081d1603babSmrg	case OSC_Unused_51:
4082d1603babSmrg	case OSC_X_Property:
40833367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
4084dfb07bc7Smrg	    xw->work.palette_changed = False;
40853367019cSmrg	    xtermRepaint(xw);
40863367019cSmrg	    break;
4087d4fba8b9Smrg	default:
4088d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
4089d4fba8b9Smrg	    break;
40903367019cSmrg	}
40913367019cSmrg    }
40923367019cSmrg
4093cd3331d0Smrg    /*
4094cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
4095cd3331d0Smrg     * a special case.
4096cd3331d0Smrg     */
4097cd3331d0Smrg    switch (mode) {
4098d1603babSmrg    case OSC_FontOps:
4099cd3331d0Smrg#if OPT_ISO_COLORS
4100d1603babSmrg    case OSC_Reset(OSC_SetAnsiColor):
4101d1603babSmrg    case OSC_Reset(OSC_GetAnsiColors):
4102fa3f02f3Smrg	need_data = False;
4103fa3f02f3Smrg	optional_data = True;
4104fa3f02f3Smrg	break;
4105cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4106cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4107cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4108cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4109cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4110cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4111cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4112cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4113cd3331d0Smrg#endif
4114cd3331d0Smrg#if OPT_TEK4014
4115cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4116cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4117cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4118cd3331d0Smrg#endif
4119d1603babSmrg    case OSC_AllowedOps:
4120cd3331d0Smrg	need_data = False;
4121cd3331d0Smrg	break;
4122cd3331d0Smrg#endif
4123cd3331d0Smrg    default:
4124cd3331d0Smrg	break;
4125cd3331d0Smrg    }
4126cd3331d0Smrg
4127cd3331d0Smrg    /*
4128cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
4129cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
4130cd3331d0Smrg     */
4131cd3331d0Smrg    if (IsEmpty(buf)) {
4132cd3331d0Smrg	if (need_data) {
413350027b5bSmrg	    switch (mode) {
413450027b5bSmrg	    case 0:
413550027b5bSmrg	    case 1:
413650027b5bSmrg	    case 2:
413750027b5bSmrg		buf = strcpy(temp, "xterm");
413850027b5bSmrg		break;
413950027b5bSmrg	    default:
414050027b5bSmrg		TRACE(("do_osc found no data\n"));
414150027b5bSmrg		return;
414250027b5bSmrg	    }
414350027b5bSmrg	} else {
414450027b5bSmrg	    temp[0] = '\0';
414550027b5bSmrg	    buf = temp;
4146cd3331d0Smrg	}
4147fa3f02f3Smrg    } else if (!need_data && !optional_data) {
4148fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
4149d522f475Smrg	return;
41500d92cbfdSchristos    }
4151d522f475Smrg
4152d522f475Smrg    switch (mode) {
4153d1603babSmrg    case OSC_IconBoth:		/* new icon name and title */
4154b7c89284Ssnj	ChangeIconName(xw, buf);
4155b7c89284Ssnj	ChangeTitle(xw, buf);
4156d522f475Smrg	break;
4157d522f475Smrg
4158d1603babSmrg    case OSC_IconOnly:		/* new icon name only */
4159b7c89284Ssnj	ChangeIconName(xw, buf);
4160d522f475Smrg	break;
4161d522f475Smrg
4162d1603babSmrg    case OSC_TitleOnly:	/* new title only */
4163b7c89284Ssnj	ChangeTitle(xw, buf);
4164d522f475Smrg	break;
4165d522f475Smrg
416622d8e007Schristos#ifdef notdef
4167d1603babSmrg    case OSC_X_Property:	/* change X property */
4168cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
41690d92cbfdSchristos	    ChangeXprop(buf);
4170d522f475Smrg	break;
417122d8e007Schristos#endif
4172d522f475Smrg#if OPT_ISO_COLORS
4173d1603babSmrg    case OSC_GetAnsiColors:
4174cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4175cd3331d0Smrg	/* FALLTHRU */
4176d1603babSmrg    case OSC_SetAnsiColor:
4177d4fba8b9Smrg	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4178dfb07bc7Smrg	    xw->work.palette_changed = True;
4179cd3331d0Smrg	break;
4180d1603babSmrg    case OSC_ColorMode:
418194644356Smrg	/* FALLTHRU */
4182d1603babSmrg    case OSC_Reset(OSC_ColorMode):
418394644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
418494644356Smrg	while (*buf != '\0') {
418594644356Smrg	    long which = 0;
418694644356Smrg	    long value = 0;
418794644356Smrg	    char *next;
418894644356Smrg	    if (*buf == ';') {
418994644356Smrg		++buf;
419094644356Smrg	    } else {
419194644356Smrg		which = strtol(buf, &next, 10);
4192037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
419394644356Smrg		    break;
419494644356Smrg		buf = next;
419594644356Smrg		if (*buf == ';')
419694644356Smrg		    ++buf;
419794644356Smrg	    }
419894644356Smrg	    if (*buf == ';') {
419994644356Smrg		++buf;
420094644356Smrg	    } else {
420194644356Smrg		value = strtol(buf, &next, 10);
4202dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
420394644356Smrg		    break;
420494644356Smrg		buf = next;
420594644356Smrg		if (*buf == ';')
420694644356Smrg		    ++buf;
420794644356Smrg	    }
420894644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
420994644356Smrg	    switch (which) {
421094644356Smrg	    case 0:
421194644356Smrg		screen->colorBDMode = (value != 0);
421294644356Smrg		break;
421394644356Smrg	    case 1:
421494644356Smrg		screen->colorULMode = (value != 0);
421594644356Smrg		break;
421694644356Smrg	    case 2:
421794644356Smrg		screen->colorBLMode = (value != 0);
421894644356Smrg		break;
421994644356Smrg	    case 3:
422094644356Smrg		screen->colorRVMode = (value != 0);
422194644356Smrg		break;
422294644356Smrg#if OPT_WIDE_ATTRS
422394644356Smrg	    case 4:
422494644356Smrg		screen->colorITMode = (value != 0);
422594644356Smrg		break;
422694644356Smrg#endif
422794644356Smrg	    default:
422894644356Smrg		TRACE(("...unknown colorXXMode\n"));
422994644356Smrg		break;
423094644356Smrg	    }
423194644356Smrg	}
423294644356Smrg	break;
4233d1603babSmrg    case OSC_Reset(OSC_GetAnsiColors):
4234cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4235cd3331d0Smrg	/* FALLTHRU */
4236d1603babSmrg    case OSC_Reset(OSC_SetAnsiColor):
4237cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4238dfb07bc7Smrg	    xw->work.palette_changed = True;
4239d522f475Smrg	break;
4240d522f475Smrg#endif
4241d522f475Smrg    case OSC_TEXT_FG:
4242d522f475Smrg    case OSC_TEXT_BG:
4243d522f475Smrg    case OSC_TEXT_CURSOR:
4244d522f475Smrg    case OSC_MOUSE_FG:
4245d522f475Smrg    case OSC_MOUSE_BG:
4246d522f475Smrg#if OPT_HIGHLIGHT_COLOR
4247d522f475Smrg    case OSC_HIGHLIGHT_BG:
4248cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
4249d522f475Smrg#endif
4250d522f475Smrg#if OPT_TEK4014
4251d522f475Smrg    case OSC_TEK_FG:
4252d522f475Smrg    case OSC_TEK_BG:
4253d522f475Smrg    case OSC_TEK_CURSOR:
4254d522f475Smrg#endif
4255cd3331d0Smrg	if (xw->misc.dynamicColors) {
4256d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
4257cd3331d0Smrg	}
4258cd3331d0Smrg	break;
4259cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4260cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4261cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4262cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4263cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4264cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4265cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4266cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4267cd3331d0Smrg#endif
4268cd3331d0Smrg#if OPT_TEK4014
4269cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4270cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4271cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4272cd3331d0Smrg#endif
4273cd3331d0Smrg	if (xw->misc.dynamicColors) {
4274cd3331d0Smrg	    ResetColorsRequest(xw, mode);
4275cd3331d0Smrg	}
4276d522f475Smrg	break;
4277d522f475Smrg
4278d1603babSmrg    case OSC_SetupPointer:
42798f44fb3bSmrg	xtermSetupPointer(xw, buf);
42808f44fb3bSmrg	break;
42818f44fb3bSmrg
4282d522f475Smrg#ifdef ALLOWLOGGING
4283d1603babSmrg    case OSC_NewLogFile:
4284d522f475Smrg#ifdef ALLOWLOGFILECHANGES
4285d522f475Smrg	/*
4286d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
4287d522f475Smrg	 * arbitrary files accessible to the person running xterm.
4288d522f475Smrg	 */
4289037a25ddSmrg	if (strcmp(buf, "?")) {
4290037a25ddSmrg	    char *bp;
4291dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
4292d4fba8b9Smrg		free(screen->logfile);
4293037a25ddSmrg		screen->logfile = bp;
4294037a25ddSmrg		break;
4295037a25ddSmrg	    }
4296d522f475Smrg	}
4297d522f475Smrg#endif
4298cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4299cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4300d522f475Smrg	break;
4301d522f475Smrg#endif /* ALLOWLOGGING */
4302d522f475Smrg
4303d1603babSmrg    case OSC_FontOps:
4304d522f475Smrg#if OPT_SHIFT_FONTS
4305cd3331d0Smrg	if (*buf == '?') {
4306cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
4307cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
4308cd3331d0Smrg	    ChangeFontRequest(xw, buf);
4309d522f475Smrg	}
4310d522f475Smrg#endif /* OPT_SHIFT_FONTS */
4311d522f475Smrg	break;
4312d522f475Smrg
4313d522f475Smrg#if OPT_PASTE64
4314d1603babSmrg    case OSC_SelectionData:
4315cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
4316d522f475Smrg	break;
4317d522f475Smrg#endif
4318d1603babSmrg
4319d1603babSmrg    case OSC_AllowedOps:	/* XTQALLOWED */
4320d1603babSmrg	report_allowed_ops(xw, final);
4321d1603babSmrg	break;
4322d1603babSmrg
4323d1603babSmrg    case OSC_DisallowedOps:	/* XTQDISALLOWED */
4324d1603babSmrg	report_disallowed_ops(xw, buf, final);
4325d1603babSmrg	break;
4326d1603babSmrg
4327d1603babSmrg    case OSC_Unused_30:
4328d1603babSmrg    case OSC_Unused_31:
4329d1603babSmrg    case OSC_Unused_51:
4330cd3331d0Smrg    default:
4331cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
4332cd3331d0Smrg	break;
4333d522f475Smrg    }
4334d522f475Smrg    unparse_end(xw);
4335d522f475Smrg}
4336d522f475Smrg
4337d522f475Smrg/*
4338d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
4339d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
4340d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4341d522f475Smrg * "real" terminals accept commas in the string definitions).
4342d522f475Smrg */
4343d522f475Smrgstatic int
4344cd3331d0Smrgudk_value(const char **cp)
4345d522f475Smrg{
4346cd3331d0Smrg    int result = -1;
4347d522f475Smrg
4348d522f475Smrg    for (;;) {
4349037a25ddSmrg	int c;
4350037a25ddSmrg
4351d522f475Smrg	if ((c = **cp) != '\0')
4352d522f475Smrg	    *cp = *cp + 1;
4353d522f475Smrg	if (c == ';' || c == '\0')
4354cd3331d0Smrg	    break;
4355cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
4356cd3331d0Smrg	    break;
4357d522f475Smrg    }
4358cd3331d0Smrg
4359cd3331d0Smrg    return result;
4360d522f475Smrg}
4361d522f475Smrg
4362d522f475Smrgvoid
43639a64e1c5Smrgreset_decudk(XtermWidget xw)
4364d522f475Smrg{
4365d522f475Smrg    int n;
4366d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
4367d4fba8b9Smrg	FreeAndNull(xw->work.user_keys[n].str);
4368d4fba8b9Smrg	xw->work.user_keys[n].len = 0;
4369d522f475Smrg    }
4370d522f475Smrg}
4371d522f475Smrg
4372d522f475Smrg/*
4373d522f475Smrg * Parse the data for DECUDK (user-defined keys).
4374d522f475Smrg */
4375d522f475Smrgstatic void
43769a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
4377d522f475Smrg{
4378d522f475Smrg    while (*cp) {
4379cd3331d0Smrg	const char *base = cp;
4380d4fba8b9Smrg	char *str = malloc(strlen(cp) + 3);
4381d522f475Smrg	unsigned key = 0;
4382d522f475Smrg	int len = 0;
4383d522f475Smrg
438494644356Smrg	if (str == NULL)
438594644356Smrg	    break;
438694644356Smrg
4387d522f475Smrg	while (isdigit(CharOf(*cp)))
43880d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
4389037a25ddSmrg
4390d522f475Smrg	if (*cp == '/') {
4391037a25ddSmrg	    int lo, hi;
4392037a25ddSmrg
4393d522f475Smrg	    cp++;
4394d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
4395d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
43960d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
4397d522f475Smrg	    }
4398d522f475Smrg	}
4399d522f475Smrg	if (len > 0 && key < MAX_UDK) {
44003367019cSmrg	    str[len] = '\0';
4401d4fba8b9Smrg	    free(xw->work.user_keys[key].str);
44029a64e1c5Smrg	    xw->work.user_keys[key].str = str;
44039a64e1c5Smrg	    xw->work.user_keys[key].len = len;
4404d4fba8b9Smrg	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4405d522f475Smrg	} else {
4406d522f475Smrg	    free(str);
4407d522f475Smrg	}
4408d522f475Smrg	if (*cp == ';')
4409d522f475Smrg	    cp++;
4410d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
4411d522f475Smrg	    break;
4412d522f475Smrg    }
4413d522f475Smrg}
4414d522f475Smrg
4415fa3f02f3Smrg/*
4416fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
4417fa3f02f3Smrg * interspersing with control characters, but have the string already.
4418fa3f02f3Smrg */
4419190d7dceSmrgvoid
4420fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
4421fa3f02f3Smrg{
4422fa3f02f3Smrg    const char *cp = *string;
4423fa3f02f3Smrg    ParmType nparam = 0;
4424fa3f02f3Smrg    int last_empty = 1;
4425fa3f02f3Smrg
4426fa3f02f3Smrg    memset(params, 0, sizeof(*params));
4427fa3f02f3Smrg    while (*cp != '\0') {
4428fa3f02f3Smrg	Char ch = CharOf(*cp++);
4429fa3f02f3Smrg
4430fa3f02f3Smrg	if (isdigit(ch)) {
4431fa3f02f3Smrg	    last_empty = 0;
4432fa3f02f3Smrg	    if (nparam < NPARAM) {
4433fa3f02f3Smrg		params->a_param[nparam] =
4434fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
4435fa3f02f3Smrg				+ (ch - '0'));
4436fa3f02f3Smrg	    }
4437fa3f02f3Smrg	} else if (ch == ';') {
4438fa3f02f3Smrg	    last_empty = 1;
4439fa3f02f3Smrg	    nparam++;
4440fa3f02f3Smrg	} else if (ch < 32) {
4441fa3f02f3Smrg	    /* EMPTY */ ;
4442fa3f02f3Smrg	} else {
4443fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
4444fa3f02f3Smrg	    params->a_final = ch;
4445fa3f02f3Smrg	    break;
4446fa3f02f3Smrg	}
4447fa3f02f3Smrg    }
4448fa3f02f3Smrg
4449fa3f02f3Smrg    *string = cp;
4450fa3f02f3Smrg    if (!last_empty)
4451fa3f02f3Smrg	nparam++;
4452fa3f02f3Smrg    if (nparam > NPARAM)
4453fa3f02f3Smrg	params->a_nparam = NPARAM;
4454fa3f02f3Smrg    else
4455fa3f02f3Smrg	params->a_nparam = nparam;
4456fa3f02f3Smrg}
4457fa3f02f3Smrg
4458d522f475Smrg#if OPT_TRACE
4459d522f475Smrg#define SOFT_WIDE 10
4460d522f475Smrg#define SOFT_HIGH 20
4461d522f475Smrg
4462d522f475Smrgstatic void
4463fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
4464d522f475Smrg{
4465d522f475Smrg    char DscsName[8];
4466d522f475Smrg    int len;
4467d522f475Smrg    int Pfn = params->a_param[0];
4468d522f475Smrg    int Pcn = params->a_param[1];
4469d522f475Smrg    int Pe = params->a_param[2];
4470d522f475Smrg    int Pcmw = params->a_param[3];
4471d522f475Smrg    int Pw = params->a_param[4];
4472d522f475Smrg    int Pt = params->a_param[5];
4473d522f475Smrg    int Pcmh = params->a_param[6];
4474d522f475Smrg    int Pcss = params->a_param[7];
4475d522f475Smrg
4476d522f475Smrg    int start_char = Pcn + 0x20;
4477d522f475Smrg    int char_wide = ((Pcmw == 0)
4478d522f475Smrg		     ? (Pcss ? 6 : 10)
4479d522f475Smrg		     : (Pcmw > 4
4480d522f475Smrg			? Pcmw
4481d522f475Smrg			: (Pcmw + 3)));
4482d522f475Smrg    int char_high = ((Pcmh == 0)
44833367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
4484d522f475Smrg			? 10
4485d522f475Smrg			: 20)
4486d522f475Smrg		     : Pcmh);
4487d522f475Smrg    Char ch;
4488d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
4489d522f475Smrg    Bool first = True;
4490d522f475Smrg    Bool prior = False;
4491d522f475Smrg    int row = 0, col = 0;
4492d522f475Smrg
4493d522f475Smrg    TRACE(("Parsing DECDLD\n"));
4494d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
4495d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
4496d522f475Smrg    TRACE(("  erase control %d\n", Pe));
4497d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
4498d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
4499d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
4500d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
4501d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
4502d522f475Smrg
4503d522f475Smrg    if (Pfn > 1
4504d522f475Smrg	|| Pcn > 95
4505d522f475Smrg	|| Pe > 2
4506d522f475Smrg	|| Pcmw > 10
4507d522f475Smrg	|| Pcmw == 1
4508d522f475Smrg	|| Pt > 2
4509d522f475Smrg	|| Pcmh > 20
4510d522f475Smrg	|| Pcss > 1
4511d522f475Smrg	|| char_wide > SOFT_WIDE
4512d522f475Smrg	|| char_high > SOFT_HIGH) {
4513d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4514d522f475Smrg	return;
4515d522f475Smrg    }
4516d522f475Smrg
4517d522f475Smrg    len = 0;
4518d522f475Smrg    while (*string != '\0') {
4519d522f475Smrg	ch = CharOf(*string++);
4520d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4521d522f475Smrg	    if (len < 2)
4522b7c89284Ssnj		DscsName[len++] = (char) ch;
4523d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4524b7c89284Ssnj	    DscsName[len++] = (char) ch;
4525d522f475Smrg	    break;
4526d522f475Smrg	}
4527d522f475Smrg    }
4528d522f475Smrg    DscsName[len] = 0;
4529d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4530d522f475Smrg
4531d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4532d522f475Smrg    while (*string != '\0') {
4533d522f475Smrg	if (first) {
4534d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4535d522f475Smrg	    if (prior) {
4536d522f475Smrg		for (row = 0; row < char_high; ++row) {
4537d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4538d522f475Smrg		}
4539d522f475Smrg	    }
4540d522f475Smrg	    prior = False;
4541d522f475Smrg	    first = False;
4542d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4543d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4544d522f475Smrg		    bits[row][col] = '.';
4545d522f475Smrg		}
4546d522f475Smrg	    }
4547d522f475Smrg	    row = col = 0;
4548d522f475Smrg	}
4549d522f475Smrg	ch = CharOf(*string++);
4550d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4551d522f475Smrg	    int n;
4552d522f475Smrg
4553b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4554d522f475Smrg	    for (n = 0; n < 6; ++n) {
4555190d7dceSmrg		bits[row + n][col] = CharOf((ch & xBIT(n)) ? '*' : '.');
4556d522f475Smrg	    }
4557d522f475Smrg	    col += 1;
4558d522f475Smrg	    prior = True;
4559d522f475Smrg	} else if (ch == '/') {
4560d522f475Smrg	    row += 6;
4561d522f475Smrg	    col = 0;
4562d522f475Smrg	} else if (ch == ';') {
4563d522f475Smrg	    first = True;
4564d522f475Smrg	    ++start_char;
4565d522f475Smrg	}
4566d522f475Smrg    }
4567d522f475Smrg}
4568d522f475Smrg#else
4569d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4570d522f475Smrg#endif
4571d522f475Smrg
4572d4fba8b9Smrgstatic const char *
4573d4fba8b9Smrgskip_params(const char *cp)
4574d4fba8b9Smrg{
4575d4fba8b9Smrg    while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4576d4fba8b9Smrg	++cp;
4577d4fba8b9Smrg    return cp;
4578d4fba8b9Smrg}
4579d4fba8b9Smrg
4580980988aeSmrg#if OPT_MOD_FKEYS || OPT_DEC_RECTOPS || (OPT_VT525_COLORS && OPT_ISO_COLORS)
4581d4fba8b9Smrgstatic int
4582d4fba8b9Smrgparse_int_param(const char **cp)
4583d4fba8b9Smrg{
4584980988aeSmrg    Boolean found = False;
4585d4fba8b9Smrg    int result = 0;
4586d4fba8b9Smrg    const char *s = *cp;
4587d4fba8b9Smrg    while (*s != '\0') {
4588d4fba8b9Smrg	if (*s == ';') {
4589d4fba8b9Smrg	    ++s;
4590d4fba8b9Smrg	    break;
4591d4fba8b9Smrg	} else if (*s >= '0' && *s <= '9') {
4592d4fba8b9Smrg	    result = (result * 10) + (*s++ - '0');
4593980988aeSmrg	    found = True;
4594d4fba8b9Smrg	} else {
4595d4fba8b9Smrg	    s += strlen(s);
4596d4fba8b9Smrg	}
4597d4fba8b9Smrg    }
4598980988aeSmrg    TRACE(("parse-int \"%s\" ->%d, %#x->\"%s\"\n", *cp, result, result, s));
4599d4fba8b9Smrg    *cp = s;
4600980988aeSmrg    return found ? result : -1;
4601d4fba8b9Smrg}
4602980988aeSmrg#endif
4603d4fba8b9Smrg
4604980988aeSmrg#if OPT_DEC_RECTOPS
4605d4fba8b9Smrgstatic int
4606d4fba8b9Smrgparse_chr_param(const char **cp)
4607d4fba8b9Smrg{
4608d4fba8b9Smrg    int result = 0;
4609d4fba8b9Smrg    const char *s = *cp;
4610d4fba8b9Smrg    if (*s != '\0') {
4611d4fba8b9Smrg	if ((result = CharOf(*s++)) != 0) {
4612d4fba8b9Smrg	    if (*s == ';') {
4613d4fba8b9Smrg		++s;
4614d4fba8b9Smrg	    } else if (*s != '\0') {
4615d4fba8b9Smrg		result = 0;
4616d4fba8b9Smrg	    }
4617d4fba8b9Smrg	}
4618d4fba8b9Smrg    }
4619980988aeSmrg    TRACE(("parse-chr %s ->%#x, %#x->%s\n", *cp, result, result, s));
4620d4fba8b9Smrg    *cp = s;
4621d4fba8b9Smrg    return result;
4622d4fba8b9Smrg}
4623d4fba8b9Smrg
4624980988aeSmrg#if OPT_TRACE
4625980988aeSmrg#define done_DECCIR()	do { TRACE(("...quit DECCIR @%d\n", __LINE__)); return; } while(0)
4626980988aeSmrg#else
4627980988aeSmrg#define done_DECCIR()	return
4628980988aeSmrg#endif
4629980988aeSmrg
4630d4fba8b9Smrgstatic void
4631d4fba8b9Smrgrestore_DECCIR(XtermWidget xw, const char *cp)
4632d4fba8b9Smrg{
4633d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
4634d4fba8b9Smrg    int value;
4635d4fba8b9Smrg
4636d4fba8b9Smrg    /* row */
4637d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4638980988aeSmrg	done_DECCIR();
4639d4fba8b9Smrg    screen->cur_row = (value - 1);
4640d4fba8b9Smrg
4641d4fba8b9Smrg    /* column */
4642d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4643980988aeSmrg	done_DECCIR();
4644d4fba8b9Smrg    screen->cur_col = (value - 1);
4645d4fba8b9Smrg
4646d4fba8b9Smrg    /* page */
4647d4fba8b9Smrg    if (parse_int_param(&cp) != 1)
4648980988aeSmrg	done_DECCIR();
4649d4fba8b9Smrg
4650d4fba8b9Smrg    /* rendition */
4651980988aeSmrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) {
4652980988aeSmrg	if (value & 0x10) {
4653980988aeSmrg	    /*
4654980988aeSmrg	     * VT420 is documented for bit 5 always reset; VT520/VT525 are not
4655980988aeSmrg	     * documented, but do use the bit for setting invisible mode.
4656980988aeSmrg	     */
4657980988aeSmrg	    if (screen->vtXX_level <= 4)
4658980988aeSmrg		done_DECCIR();
4659980988aeSmrg	} else if (!(value & 0x40)) {
4660980988aeSmrg	    done_DECCIR();
4661980988aeSmrg	}
4662980988aeSmrg    }
4663d4fba8b9Smrg    UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4664980988aeSmrg    xw->flags |= (value & 16) ? INVISIBLE : 0;
4665d4fba8b9Smrg    xw->flags |= (value & 8) ? INVERSE : 0;
4666d4fba8b9Smrg    xw->flags |= (value & 4) ? BLINK : 0;
4667d4fba8b9Smrg    xw->flags |= (value & 2) ? UNDERLINE : 0;
4668d4fba8b9Smrg    xw->flags |= (value & 1) ? BOLD : 0;
4669d4fba8b9Smrg
4670d4fba8b9Smrg    /* attributes */
4671d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4672980988aeSmrg	done_DECCIR();
4673d4fba8b9Smrg    screen->protected_mode &= ~DEC_PROTECT;
4674d4fba8b9Smrg    screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4675d4fba8b9Smrg
4676d4fba8b9Smrg    /* flags */
4677d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4678980988aeSmrg	done_DECCIR();
4679d4fba8b9Smrg    screen->do_wrap = (value & 8) ? True : False;
4680d4fba8b9Smrg    screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4681d4fba8b9Smrg    UIntClr(xw->flags, ORIGIN);
4682d4fba8b9Smrg    xw->flags |= (value & 1) ? ORIGIN : 0;
4683d4fba8b9Smrg
4684d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4685980988aeSmrg	done_DECCIR();
4686d4fba8b9Smrg    screen->curgl = (Char) value;
4687d4fba8b9Smrg
4688d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4689980988aeSmrg	done_DECCIR();
4690d4fba8b9Smrg    screen->curgr = (Char) value;
4691d4fba8b9Smrg
4692d4fba8b9Smrg    /* character-set size */
4693980988aeSmrg    if (parse_chr_param(&cp) == 0xffff)		/* FIXME: limit SCS? */
4694980988aeSmrg	done_DECCIR();
4695d4fba8b9Smrg
4696d4fba8b9Smrg    /* SCS designators */
4697d4fba8b9Smrg    for (value = 0; value < NUM_GSETS; ++value) {
4698980988aeSmrg	if (*cp == '\0') {
4699980988aeSmrg	    done_DECCIR();
4700980988aeSmrg	} else if (strchr("%&\"", *cp) != NULL) {
4701980988aeSmrg	    int prefix = *cp++;
4702980988aeSmrg	    xtermDecodeSCS(xw, value, 0, prefix, *cp);
4703d4fba8b9Smrg	} else {
4704980988aeSmrg	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4705d4fba8b9Smrg	}
4706d4fba8b9Smrg	cp++;
4707d4fba8b9Smrg    }
4708d4fba8b9Smrg
4709d4fba8b9Smrg    TRACE(("...done DECCIR\n"));
4710d4fba8b9Smrg}
4711d4fba8b9Smrg
4712d4fba8b9Smrgstatic void
4713d4fba8b9Smrgrestore_DECTABSR(XtermWidget xw, const char *cp)
4714d4fba8b9Smrg{
4715d4fba8b9Smrg    int stop = 0;
4716d4fba8b9Smrg    Bool fail = False;
4717d4fba8b9Smrg
4718d4fba8b9Smrg    TabZonk(xw->tabs);
4719d4fba8b9Smrg    while (*cp != '\0' && !fail) {
4720d4fba8b9Smrg	if ((*cp) >= '0' && (*cp) <= '9') {
4721d4fba8b9Smrg	    stop = (stop * 10) + ((*cp) - '0');
4722d4fba8b9Smrg	} else if (*cp == '/') {
4723d4fba8b9Smrg	    --stop;
4724d4fba8b9Smrg	    if (OkTAB(stop)) {
4725d4fba8b9Smrg		TabSet(xw->tabs, stop);
4726d4fba8b9Smrg	    }
4727980988aeSmrg	    stop = 0;
4728d4fba8b9Smrg	} else {
4729d4fba8b9Smrg	    fail = True;
4730d4fba8b9Smrg	}
4731d4fba8b9Smrg	++cp;
4732d4fba8b9Smrg    }
4733d4fba8b9Smrg    --stop;
4734d4fba8b9Smrg    if (OkTAB(stop))
4735d4fba8b9Smrg	TabSet(xw->tabs, stop);
4736d4fba8b9Smrg
4737d4fba8b9Smrg    TRACE(("...done DECTABSR\n"));
4738d4fba8b9Smrg}
4739980988aeSmrg#endif /* OPT_DEC_RECTOPS */
4740980988aeSmrg
4741980988aeSmrg/*
4742980988aeSmrg * VT510 and VT520 reference manual have the same explanation for Pn (params),
4743980988aeSmrg * but it does not agree with the possible values for Dscs because it refers
4744980988aeSmrg * to "ISO Latin-7" (ISO 8859-13 aka "Baltic Rim"), and omits ISO Greek
4745980988aeSmrg * (ISO 8859-7):
4746980988aeSmrg *
4747980988aeSmrg * ------------------------------------------------------------------------
4748980988aeSmrg * Pn  Meaning
4749980988aeSmrg * ------------------------------------------------------------------------
4750980988aeSmrg * 0   DEC, ISO Latin-1, ISO Latin-2
4751980988aeSmrg * 1   ISO Latin-5, ISO Latin-7, ISO Cyrillic, ISO Hebrew
4752980988aeSmrg * ------------------------------------------------------------------------
4753980988aeSmrg *
4754980988aeSmrg * versus
4755980988aeSmrg *
4756980988aeSmrg * ------------------------------------------------------------------------
4757980988aeSmrg * Dscs   Character Set
4758980988aeSmrg * ------------------------------------------------------------------------
4759980988aeSmrg * %5     DEC Supplemental
4760980988aeSmrg * "?     DEC Greek
4761980988aeSmrg * "4     DEC Hebrew
4762980988aeSmrg * %0     DEC Turkish
4763980988aeSmrg * &4     DEC Cyrillic
4764980988aeSmrg * <      User-preferred Supplemental
4765980988aeSmrg * A      ISO Latin-1 Supplemental
4766980988aeSmrg * B      ISO Latin-2 Supplemental
4767980988aeSmrg * F      ISO Greek Supplemental
4768980988aeSmrg * H      ISO Hebrew Supplemental
4769980988aeSmrg * M      ISO Latin-5 Supplemental
4770980988aeSmrg * L      ISO Latin-Cyrillic
4771980988aeSmrg * ------------------------------------------------------------------------
4772980988aeSmrg *
4773980988aeSmrg * DEC 070, page 5-123 explains that Pn ("Ps" in the text) selects 94 or 96
4774980988aeSmrg * character sets (0 or 1, respectively), and on the next page states that
4775980988aeSmrg * the valid combinations are 0 (DEC Supplemental) and 1 (ISO Latin-1
4776980988aeSmrg * supplemental).  The document comments in regard to LS0 that (applications)
4777980988aeSmrg * should not assume that they can use 96-character sets for G0, but that it
4778980988aeSmrg * is possible to do this using UPSS.
4779980988aeSmrg *
4780980988aeSmrg * The VT510/VT520 reference manuals under SCS Select Character Set show
4781980988aeSmrg * a list of 94- and 96-character sets with "DEC" and "NRCS" as 94-characters,
4782980988aeSmrg * and the "ISO" as 96-characters.  A few 94-character sets are added, based
4783980988aeSmrg * on testing VT520/VT525 that shows that DEC Special Graphics also is allowed.
4784980988aeSmrg */
4785980988aeSmrgstatic Bool
4786980988aeSmrgdecode_upss(XtermWidget xw, const char *cp, char psarg, DECNRCM_codes * upss)
4787980988aeSmrg{
4788980988aeSmrg    /* *INDENT-OFF* */
4789980988aeSmrg    static const struct {
4790980988aeSmrg	DECNRCM_codes code;
4791980988aeSmrg	int params;	/* 0 for 94-characters, 1 for 96-characters */
4792980988aeSmrg	int prefix;
4793980988aeSmrg	int suffix;
4794980988aeSmrg	int min_level;
4795980988aeSmrg	int max_level;
4796980988aeSmrg    } upss_table[] = {
4797980988aeSmrg	{ DFT_UPSS,               0,  '%', '5', 3, 9 },
4798980988aeSmrg    	{ nrc_ASCII,              0,  0,   'A', 1, 9 },	/* undocumented */
4799980988aeSmrg    	{ nrc_DEC_Spec_Graphic,   0,  0,   '0', 1, 9 },	/* undocumented */
4800980988aeSmrg    	{ nrc_DEC_Technical,      0,  0,   '>', 3, 9 },	/* undocumented */
4801980988aeSmrg	{ nrc_DEC_Greek_Supp,     0,  '"', '?', 5, 9 },
4802980988aeSmrg	{ nrc_DEC_Hebrew_Supp,    0,  '"', '4', 5, 9 },
4803980988aeSmrg	{ nrc_DEC_Turkish_Supp,   0,  '%', '0', 5, 9 },
4804980988aeSmrg	{ nrc_DEC_Cyrillic,       0,  '&', '4', 5, 9 },
4805980988aeSmrg	{ ALT_UPSS,               1,  0,   'A', 3, 9 },
4806980988aeSmrg	{ nrc_ISO_Latin_2_Supp,   1,  0,   'B', 5, 9 },
4807980988aeSmrg	{ nrc_ISO_Greek_Supp,     1,  0,   'F', 5, 9 },
4808980988aeSmrg	{ nrc_ISO_Hebrew_Supp,    1,  0,   'H', 5, 9 },
4809980988aeSmrg	{ nrc_ISO_Latin_5_Supp,   1,  0,   'M', 5, 9 },
4810980988aeSmrg	{ nrc_ISO_Latin_Cyrillic, 1,  0,   'L', 5, 9 },
4811980988aeSmrg    };
4812980988aeSmrg    /* *INDENT-ON* */
4813980988aeSmrg    TScreen *screen = TScreenOf(xw);
4814980988aeSmrg    Bool result = False;
4815980988aeSmrg
4816980988aeSmrg    *upss = nrc_ASCII;
4817980988aeSmrg    if (screen->vtXX_level >= 3) {
4818980988aeSmrg	Cardinal n;
4819980988aeSmrg	for (n = 0; n < XtNumber(upss_table); ++n) {
4820980988aeSmrg	    if (((int) psarg - '0') != upss_table[n].params)
4821980988aeSmrg		continue;
4822980988aeSmrg
4823980988aeSmrg	    if (cp[1] == '\0') {
4824980988aeSmrg		if (upss_table[n].suffix != cp[0])
4825980988aeSmrg		    continue;
4826980988aeSmrg	    } else if (cp[2] == '\0') {
4827980988aeSmrg		if (upss_table[n].prefix != cp[0])
4828980988aeSmrg		    continue;
4829980988aeSmrg		if (upss_table[n].suffix != cp[1])
4830980988aeSmrg		    continue;
4831980988aeSmrg	    } else {
4832980988aeSmrg		continue;
4833980988aeSmrg	    }
4834980988aeSmrg
4835980988aeSmrg	    result = True;
4836980988aeSmrg	    *upss = upss_table[n].code;
4837980988aeSmrg	    if (*upss == DFT_UPSS) {
4838980988aeSmrg		TRACE(("DECAUPSS (default)\n"));
4839980988aeSmrg	    } else if (*upss == ALT_UPSS) {
4840980988aeSmrg		TRACE(("DECAUPSS (alternate)\n"));
4841980988aeSmrg	    }
4842980988aeSmrg	    break;
4843980988aeSmrg	}
4844980988aeSmrg	TRACE(("DECAUPSS %ssuccessful %s\n",
4845980988aeSmrg	       result ? "" : "not ", visibleScsCode(*upss)));
4846980988aeSmrg    }
4847980988aeSmrg    return result;
4848980988aeSmrg}
4849d4fba8b9Smrg
4850d522f475Smrgvoid
4851fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4852d522f475Smrg{
4853cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4854d522f475Smrg    char reply[BUFSIZ];
4855cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4856d522f475Smrg    Bool okay;
4857d522f475Smrg    ANSI params;
4858d4fba8b9Smrg    char psarg = '0';
4859980988aeSmrg#if OPT_VT525_COLORS && OPT_ISO_COLORS
4860980988aeSmrg    const char *cp2;
4861980988aeSmrg#endif
4862980988aeSmrg#if (OPT_VT525_COLORS && OPT_ISO_COLORS) || OPT_MOD_FKEYS
4863980988aeSmrg    int ival;
4864d4fba8b9Smrg#endif
4865d522f475Smrg
4866cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4867d522f475Smrg
4868d522f475Smrg    if (dcslen != strlen(cp))
4869d522f475Smrg	/* shouldn't have nulls in the string */
4870d522f475Smrg	return;
4871d522f475Smrg
4872d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4873d522f475Smrg    case '$':			/* DECRQSS */
4874d522f475Smrg	okay = True;
4875d522f475Smrg
4876d522f475Smrg	cp++;
4877d4fba8b9Smrg	if (*cp == 'q') {
4878d4fba8b9Smrg	    *reply = '\0';
4879d4fba8b9Smrg	    cp++;
4880d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4881d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCA\n"));
4882d522f475Smrg		sprintf(reply, "%d%s",
4883d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4884d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4885d522f475Smrg			cp);
4886d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
48873367019cSmrg		if (screen->vtXX_level < 2) {
48883367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
48893367019cSmrg		    break;
48903367019cSmrg		}
4891d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCL\n"));
4892d522f475Smrg		sprintf(reply, "%d%s%s",
4893d522f475Smrg			(screen->vtXX_level ?
4894d522f475Smrg			 screen->vtXX_level : 1) + 60,
4895190d7dceSmrg			(screen->control_eight_bits
4896190d7dceSmrg			 ? ";0" : ";1"),
4897d522f475Smrg			cp);
4898d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4899d4fba8b9Smrg		TRACE(("DECRQSS -> DECSTBM\n"));
4900d522f475Smrg		sprintf(reply, "%d;%dr",
4901d522f475Smrg			screen->top_marg + 1,
4902d522f475Smrg			screen->bot_marg + 1);
49033367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
49043367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
4905d4fba8b9Smrg		    TRACE(("DECRQSS -> DECSLRM\n"));
49063367019cSmrg		    sprintf(reply, "%d;%ds",
49073367019cSmrg			    screen->lft_marg + 1,
49083367019cSmrg			    screen->rgt_marg + 1);
4909037a25ddSmrg		} else {
4910037a25ddSmrg		    okay = False;
49113367019cSmrg		}
4912d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4913d4fba8b9Smrg		TRACE(("DECRQSS -> SGR\n"));
4914d4fba8b9Smrg		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4915d522f475Smrg		strcat(reply, "m");
4916712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
49173367019cSmrg		int code = STEADY_BLOCK;
49183367019cSmrg		if (isCursorUnderline(screen))
49193367019cSmrg		    code = STEADY_UNDERLINE;
49203367019cSmrg		else if (isCursorBar(screen))
49213367019cSmrg		    code = STEADY_BAR;
49223367019cSmrg#if OPT_BLINK_CURS
492394644356Smrg		if (screen->cursor_blink_esc != 0)
49243367019cSmrg		    code -= 1;
49253367019cSmrg#endif
4926d4fba8b9Smrg		TRACE(("reply DECSCUSR\n"));
49273367019cSmrg		sprintf(reply, "%d%s", code, cp);
4928d4fba8b9Smrg	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4929d4fba8b9Smrg		sprintf(reply, "%d%s",
4930d4fba8b9Smrg			((screen->max_row > 24) ? screen->max_row : 24),
4931d4fba8b9Smrg			cp);
4932d4fba8b9Smrg		TRACE(("reply DECSLPP\n"));
4933d4fba8b9Smrg	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4934d4fba8b9Smrg		TRACE(("reply DECSCPP\n"));
4935d4fba8b9Smrg		sprintf(reply, "%d%s",
4936d4fba8b9Smrg			((xw->flags & IN132COLUMNS) ? 132 : 80),
4937d4fba8b9Smrg			cp);
493850027b5bSmrg	    } else
493950027b5bSmrg#if OPT_STATUS_LINE
494050027b5bSmrg	    if (!strcmp(cp, "$}")) {	/* DECSASD */
494150027b5bSmrg		TRACE(("reply DECSASD\n"));
494250027b5bSmrg		sprintf(reply, "%d%s",
494350027b5bSmrg			screen->status_active,
494450027b5bSmrg			cp);
494550027b5bSmrg	    } else if (!strcmp(cp, "$~")) {	/* DECSSDT */
494650027b5bSmrg		TRACE(("reply DECSASD\n"));
494750027b5bSmrg		sprintf(reply, "%d%s",
494850027b5bSmrg			screen->status_type,
494950027b5bSmrg			cp);
495050027b5bSmrg	    } else
4951980988aeSmrg#endif
4952980988aeSmrg#if OPT_DEC_RECTOPS
4953980988aeSmrg	    if (!strcmp(cp, "*x")) {	/* DECSACE */
4954980988aeSmrg		TRACE(("reply DECSACE\n"));
4955980988aeSmrg		sprintf(reply, "%d%s",
4956980988aeSmrg			screen->cur_decsace,
4957980988aeSmrg			cp);
4958980988aeSmrg	    } else
495950027b5bSmrg#endif
496050027b5bSmrg	    if (!strcmp(cp, "*|")) {	/* DECSNLS */
4961d4fba8b9Smrg		TRACE(("reply DECSNLS\n"));
4962d4fba8b9Smrg		sprintf(reply, "%d%s",
4963d4fba8b9Smrg			screen->max_row + 1,
4964d4fba8b9Smrg			cp);
4965980988aeSmrg	    } else
4966980988aeSmrg#if OPT_VT525_COLORS && OPT_ISO_COLORS
4967980988aeSmrg		if (screen->terminal_id == 525
4968980988aeSmrg		    && !strcmp((cp2 = skip_params(cp)), ",}")) {	/* DECATC */
4969980988aeSmrg		ival = parse_int_param(&cp);
4970980988aeSmrg		TRACE(("reply DECATC:%s\n", cp));
4971980988aeSmrg		if (ival >= 0 && ival < 16 && *cp2 == ',') {
4972980988aeSmrg		    sprintf(reply, "%d;%d;%d%s", ival,
4973980988aeSmrg			    screen->alt_colors[ival].fg,
4974980988aeSmrg			    screen->alt_colors[ival].bg,
4975980988aeSmrg			    cp2);
4976980988aeSmrg		} else {
4977980988aeSmrg		    okay = False;
4978980988aeSmrg		}
4979190d7dceSmrg	    } else if (screen->terminal_id == 525
4980190d7dceSmrg		       && !strcmp((cp2 = skip_params(cp)), "){")) {	/* DECSTGLT */
4981190d7dceSmrg		TRACE(("reply DECSTGLT:%s\n", cp));
4982190d7dceSmrg		sprintf(reply, "%d%s",
4983190d7dceSmrg			3,	/* ANSI SGR color */
4984190d7dceSmrg			cp);
4985980988aeSmrg	    } else if (screen->terminal_id == 525
4986980988aeSmrg		       && !strcmp((cp2 = skip_params(cp)), ",|")) {	/* DECAC */
4987980988aeSmrg		ival = parse_int_param(&cp);
4988980988aeSmrg		TRACE(("reply DECAC\n"));
4989980988aeSmrg		switch (ival) {
4990980988aeSmrg		case 1:	/* normal text */
4991980988aeSmrg		    sprintf(reply, "%d,%d%s",
4992980988aeSmrg			    screen->assigned_fg,
4993980988aeSmrg			    screen->assigned_bg,
4994980988aeSmrg			    cp2);
4995980988aeSmrg		    break;
4996980988aeSmrg		case 2:	/* window frame (not implemented) */
4997980988aeSmrg		    /* FALLTHRU */
4998980988aeSmrg		default:
4999980988aeSmrg		    okay = False;
5000980988aeSmrg		    break;
5001980988aeSmrg		}
5002980988aeSmrg	    } else
5003980988aeSmrg#endif
5004980988aeSmrg#if OPT_MOD_FKEYS
5005980988aeSmrg	    if (*cp == '>' && !strcmp(skip_params(1 + cp), "m")) {	/* XTQMODKEYS */
5006980988aeSmrg		++cp;
5007980988aeSmrg		okay = True;
5008980988aeSmrg		ival = parse_int_param(&cp);
5009980988aeSmrg#define GET_MOD_FKEYS(field) xw->keyboard.modify_now.field
5010980988aeSmrg#define FMT_MOD_FKEYS(field) sprintf(reply, ">%d;%dm", ival, GET_MOD_FKEYS(field))
5011980988aeSmrg		switch (ival) {
5012980988aeSmrg		case 0:
5013980988aeSmrg		    FMT_MOD_FKEYS(allow_keys);
5014980988aeSmrg		    break;
5015980988aeSmrg		case 1:
5016980988aeSmrg		    FMT_MOD_FKEYS(cursor_keys);
5017980988aeSmrg		    break;
5018980988aeSmrg		case 2:
5019980988aeSmrg		    FMT_MOD_FKEYS(function_keys);
5020980988aeSmrg		    break;
5021980988aeSmrg		case 4:
5022980988aeSmrg		    FMT_MOD_FKEYS(other_keys);
5023980988aeSmrg		    break;
5024980988aeSmrg		default:
5025980988aeSmrg		    okay = False;
5026980988aeSmrg		    break;
5027980988aeSmrg		}
5028980988aeSmrg	    } else
5029980988aeSmrg#endif
5030190d7dceSmrg		/*
5031190d7dceSmrg		 * This query returns the settings assuming the default value
5032190d7dceSmrg		 * of DEF_TITLE_MODES, which is zero.  Someone could in
5033190d7dceSmrg		 * principle alter that (so that some states could only be
5034190d7dceSmrg		 * reached by removing rather than consistently by setting),
5035190d7dceSmrg		 * but the default value could be discovered by resetting the
5036190d7dceSmrg		 * title modes, querying the resulting reset state.
5037190d7dceSmrg		 */
5038190d7dceSmrg	    if (*cp == '>' && !strcmp(skip_params(1 + cp), "t")) {	/* XTSMTITLE */
5039190d7dceSmrg		char buffer[80];
5040190d7dceSmrg		int n;
5041190d7dceSmrg
5042190d7dceSmrg		++cp;
5043190d7dceSmrg		okay = True;
5044190d7dceSmrg		ival = parse_int_param(&cp);
5045190d7dceSmrg		*buffer = '\0';
5046190d7dceSmrg		if (ival == -1) {	/* DEFAULT */
5047190d7dceSmrg		    for (n = 0; n <= MAX_TITLEMODE; ++n) {
5048190d7dceSmrg			int check = xBIT(n);
5049190d7dceSmrg			char *s = buffer + strlen(buffer);
5050190d7dceSmrg			if (s != buffer)
5051190d7dceSmrg			    *s++ = ';';
5052190d7dceSmrg			sprintf(s, "%d",
5053190d7dceSmrg				((check & screen->title_modes) != 0
5054190d7dceSmrg				 ? 1
5055190d7dceSmrg				 : 0));
5056190d7dceSmrg		    }
5057190d7dceSmrg		} else if (ival >= 0 && ival <= MAX_TITLEMODE) {
5058190d7dceSmrg		    sprintf(buffer, "%d",
5059190d7dceSmrg			    ((xBIT(ival) & screen->title_modes) != 0
5060190d7dceSmrg			     ? 1
5061190d7dceSmrg			     : 0));
5062190d7dceSmrg		} else {
5063190d7dceSmrg		    okay = False;
5064190d7dceSmrg		}
5065190d7dceSmrg		if (okay)
5066190d7dceSmrg		    sprintf(reply, ">%st", buffer);
5067190d7dceSmrg	    } else {
5068d4fba8b9Smrg		okay = False;
506922d8e007Schristos	    }
5070d4fba8b9Smrg
5071d4fba8b9Smrg	    unparseputc1(xw, ANSI_DCS);
5072d4fba8b9Smrg	    unparseputc(xw, okay ? '1' : '0');
5073d4fba8b9Smrg	    unparseputc(xw, '$');
5074d4fba8b9Smrg	    unparseputc(xw, 'r');
5075d4fba8b9Smrg	    cp = reply;
5076d4fba8b9Smrg	    unparseputs(xw, cp);
5077d4fba8b9Smrg	    unparseputc1(xw, ANSI_ST);
5078d522f475Smrg	} else {
5079d522f475Smrg	    unparseputc(xw, ANSI_CAN);
5080d522f475Smrg	}
5081d522f475Smrg	break;
5082d522f475Smrg    case '+':
5083d522f475Smrg	cp++;
5084cd3331d0Smrg	switch (*cp) {
5085d4fba8b9Smrg#if OPT_TCAP_QUERY
5086d1603babSmrg	case 'p':		/* XTSETTCAP */
5087cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
5088cd3331d0Smrg		set_termcap(xw, cp + 1);
5089cd3331d0Smrg	    }
5090cd3331d0Smrg	    break;
5091d1603babSmrg	case 'q':		/* XTGETTCAP */
5092cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
5093cd3331d0Smrg		Bool fkey;
5094cd3331d0Smrg		unsigned state;
5095cd3331d0Smrg		int code;
5096cd3331d0Smrg		const char *tmp;
5097cd3331d0Smrg		const char *parsed = ++cp;
5098d522f475Smrg
5099cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
5100d522f475Smrg
5101cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
5102b7c89284Ssnj
5103cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
5104d522f475Smrg
5105cd3331d0Smrg		unparseputc(xw, '+');
5106cd3331d0Smrg		unparseputc(xw, 'r');
5107d522f475Smrg
5108cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
5109cd3331d0Smrg		    if (cp == parsed)
5110cd3331d0Smrg			break;	/* no data found, error */
5111d522f475Smrg
5112cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
5113cd3331d0Smrg			unparseputc(xw, *tmp);
5114d522f475Smrg
5115cd3331d0Smrg		    if (code >= 0) {
5116cd3331d0Smrg			unparseputc(xw, '=');
5117cd3331d0Smrg			screen->tc_query_code = code;
5118cd3331d0Smrg			screen->tc_query_fkey = fkey;
5119d522f475Smrg#if OPT_ISO_COLORS
5120cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
5121cd3331d0Smrg			 * number of colors) */
5122cd3331d0Smrg			if (code == XK_COLORS) {
5123d4fba8b9Smrg			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
5124d4fba8b9Smrg			} else
5125d4fba8b9Smrg#if OPT_DIRECT_COLOR
5126d4fba8b9Smrg			if (code == XK_RGB) {
5127d4fba8b9Smrg			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
5128d4fba8b9Smrg				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
5129d4fba8b9Smrg				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
5130d4fba8b9Smrg				    unparseputn(xw, xw->rgb_widths[0]);
5131d4fba8b9Smrg				} else {
5132d4fba8b9Smrg				    char temp[1024];
5133d1603babSmrg				    sprintf(temp, "%u/%u/%u",
5134d4fba8b9Smrg					    xw->rgb_widths[0],
5135d4fba8b9Smrg					    xw->rgb_widths[1],
5136d4fba8b9Smrg					    xw->rgb_widths[2]);
5137d4fba8b9Smrg				    unparseputs(xw, temp);
5138d4fba8b9Smrg				}
5139d4fba8b9Smrg			    } else {
5140d4fba8b9Smrg				unparseputs(xw, "-1");
5141d4fba8b9Smrg			    }
5142cd3331d0Smrg			} else
5143d4fba8b9Smrg#endif
5144cd3331d0Smrg#endif
5145cd3331d0Smrg			if (code == XK_TCAPNAME) {
5146c219fbebSmrg			    unparseputs(xw, resource.term_name);
5147cd3331d0Smrg			} else {
5148cd3331d0Smrg			    XKeyEvent event;
5149d4fba8b9Smrg			    memset(&event, 0, sizeof(event));
5150cd3331d0Smrg			    event.state = state;
5151cd3331d0Smrg			    Input(xw, &event, False);
5152cd3331d0Smrg			}
5153cd3331d0Smrg			screen->tc_query_code = -1;
5154cd3331d0Smrg		    } else {
5155cd3331d0Smrg			break;	/* no match found, error */
5156d522f475Smrg		    }
5157d522f475Smrg
5158d522f475Smrg		    cp = parsed;
5159cd3331d0Smrg		    if (*parsed == ';') {
5160cd3331d0Smrg			unparseputc(xw, *parsed++);
5161cd3331d0Smrg			cp = parsed;
5162cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
5163cd3331d0Smrg		    }
5164d522f475Smrg		}
5165cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
5166d522f475Smrg	    }
5167cd3331d0Smrg	    break;
5168d4fba8b9Smrg#endif
5169d4fba8b9Smrg#if OPT_XRES_QUERY
5170d1603babSmrg	case 'Q':		/* XTGETXRES */
5171d4fba8b9Smrg	    ++cp;
5172d4fba8b9Smrg	    if (AllowXResOps(xw)) {
5173d4fba8b9Smrg		Boolean first = True;
5174d1603babSmrg		okay = True;
5175d1603babSmrg		while (*cp != '\0' && okay) {
5176190d7dceSmrg		    const char *parsed = NULL;
5177d4fba8b9Smrg		    const char *tmp;
5178d4fba8b9Smrg		    char *name = x_decode_hex(cp, &parsed);
5179d4fba8b9Smrg		    char *value;
5180d4fba8b9Smrg		    char *result;
5181d4fba8b9Smrg		    if (cp == parsed || name == NULL) {
5182d4fba8b9Smrg			free(name);
5183d4fba8b9Smrg			break;	/* no data found, error */
5184d4fba8b9Smrg		    }
5185d1603babSmrg		    if ((cp - parsed) > 1024) {
5186d1603babSmrg			free(name);
5187d1603babSmrg			break;	/* ignore improbable resource */
5188d1603babSmrg		    }
5189d4fba8b9Smrg		    TRACE(("query-feature '%s'\n", name));
5190190d7dceSmrg		    if ((value = vt100ResourceToString(xw, name)) != NULL) {
5191d4fba8b9Smrg			okay = True;	/* valid */
5192d4fba8b9Smrg		    } else {
5193d4fba8b9Smrg			okay = False;	/* invalid */
5194d4fba8b9Smrg		    }
5195d4fba8b9Smrg		    if (first) {
5196d4fba8b9Smrg			unparseputc1(xw, ANSI_DCS);
5197d4fba8b9Smrg			unparseputc(xw, okay ? '1' : '0');
5198d4fba8b9Smrg			unparseputc(xw, '+');
5199d4fba8b9Smrg			unparseputc(xw, 'R');
5200d4fba8b9Smrg			first = False;
5201d4fba8b9Smrg		    }
5202d4fba8b9Smrg
5203d4fba8b9Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
5204d4fba8b9Smrg			unparseputc(xw, *tmp);
5205d4fba8b9Smrg
5206190d7dceSmrg		    if (value != NULL) {
5207d4fba8b9Smrg			unparseputc1(xw, '=');
5208d4fba8b9Smrg			result = x_encode_hex(value);
5209d4fba8b9Smrg			unparseputs(xw, result);
5210d4fba8b9Smrg		    } else {
5211d4fba8b9Smrg			result = NULL;
5212d4fba8b9Smrg		    }
5213d4fba8b9Smrg
5214d4fba8b9Smrg		    free(name);
5215d4fba8b9Smrg		    free(value);
5216d4fba8b9Smrg		    free(result);
5217d4fba8b9Smrg
5218d4fba8b9Smrg		    cp = parsed;
5219d4fba8b9Smrg		    if (*parsed == ';') {
5220d4fba8b9Smrg			unparseputc(xw, *parsed++);
5221d4fba8b9Smrg			cp = parsed;
5222d4fba8b9Smrg		    }
5223d4fba8b9Smrg		}
5224d4fba8b9Smrg		if (!first)
5225d4fba8b9Smrg		    unparseputc1(xw, ANSI_ST);
5226d4fba8b9Smrg	    }
5227d4fba8b9Smrg	    break;
5228d4fba8b9Smrg#endif
5229d522f475Smrg	}
5230d522f475Smrg	break;
5231980988aeSmrg    case '0':
5232980988aeSmrg	/* FALLTHRU */
5233d4fba8b9Smrg    case '1':
5234980988aeSmrg	if (screen->vtXX_level >= 3 && *skip_params(cp) == '!') {
5235980988aeSmrg	    DECNRCM_codes upss;
5236980988aeSmrg	    psarg = *cp++;
5237980988aeSmrg	    if (*cp++ == '!' && *cp++ == 'u') {
5238980988aeSmrg#if OPT_WIDE_CHARS
5239980988aeSmrg		if (screen->wide_chars && screen->utf8_mode) {
5240980988aeSmrg		    ;		/* EMPTY */
5241980988aeSmrg		} else
5242980988aeSmrg#endif
5243980988aeSmrg		if (decode_upss(xw, cp, psarg, &upss)) {
5244980988aeSmrg		    screen->gsets_upss = upss;
5245980988aeSmrg		}
5246980988aeSmrg	    }
5247980988aeSmrg	    break;
5248980988aeSmrg	}
5249980988aeSmrg#if OPT_DEC_RECTOPS
5250d4fba8b9Smrg	/* FALLTHRU */
5251d4fba8b9Smrg    case '2':
5252d4fba8b9Smrg	if (*skip_params(cp) == '$') {
5253d4fba8b9Smrg	    psarg = *cp++;
5254d4fba8b9Smrg	    if ((*cp++ == '$')
5255d4fba8b9Smrg		&& (*cp++ == 't')
5256d4fba8b9Smrg		&& (screen->vtXX_level >= 3)) {
5257d4fba8b9Smrg		switch (psarg) {
5258d4fba8b9Smrg		case '1':
5259d4fba8b9Smrg		    TRACE(("DECRSPS (DECCIR)\n"));
5260d4fba8b9Smrg		    restore_DECCIR(xw, cp);
5261d4fba8b9Smrg		    break;
5262d4fba8b9Smrg		case '2':
5263d4fba8b9Smrg		    TRACE(("DECRSPS (DECTABSR)\n"));
5264d4fba8b9Smrg		    restore_DECTABSR(xw, cp);
5265d4fba8b9Smrg		    break;
5266d4fba8b9Smrg		}
5267d4fba8b9Smrg	    }
5268d4fba8b9Smrg	    break;
5269d4fba8b9Smrg	}
5270d522f475Smrg#endif
5271d4fba8b9Smrg	/* FALLTHRU */
5272d522f475Smrg    default:
5273d4fba8b9Smrg	if (optRegisGraphics(screen) ||
5274fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
52750d92cbfdSchristos	    parse_ansi_params(&params, &cp);
52760d92cbfdSchristos	    switch (params.a_final) {
5277d4fba8b9Smrg	    case 'p':		/* ReGIS */
52789a64e1c5Smrg#if OPT_REGIS_GRAPHICS
5279d4fba8b9Smrg		if (optRegisGraphics(screen)) {
5280fa3f02f3Smrg		    parse_regis(xw, &params, cp);
5281fa3f02f3Smrg		}
52829a64e1c5Smrg#else
52839a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
52849a64e1c5Smrg#endif
5285fa3f02f3Smrg		break;
5286190d7dceSmrg	    case 'q':		/* sixel is done in charproc.c */
52879a64e1c5Smrg		break;
52880d92cbfdSchristos	    case '|':		/* DECUDK */
52899a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
52909a64e1c5Smrg		    if (params.a_param[0] == 0)
52919a64e1c5Smrg			reset_decudk(xw);
52929a64e1c5Smrg		    parse_decudk(xw, cp);
52939a64e1c5Smrg		}
52940d92cbfdSchristos		break;
529594644356Smrg	    case L_CURL:	/* DECDLD */
52969a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
52979a64e1c5Smrg		    parse_decdld(&params, cp);
52989a64e1c5Smrg		}
52990d92cbfdSchristos		break;
53000d92cbfdSchristos	    }
5301d522f475Smrg	}
5302d522f475Smrg	break;
5303d522f475Smrg    }
5304d522f475Smrg    unparse_end(xw);
5305d522f475Smrg}
5306d522f475Smrg
5307cb4a1343Smrg#if OPT_DEC_RECTOPS
5308cb4a1343Smrgenum {
5309cb4a1343Smrg    mdUnknown = 0,
5310cb4a1343Smrg    mdMaybeSet = 1,
5311cb4a1343Smrg    mdMaybeReset = 2,
5312cb4a1343Smrg    mdAlwaysSet = 3,
5313cb4a1343Smrg    mdAlwaysReset = 4
5314cb4a1343Smrg};
5315cb4a1343Smrg
5316cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
53173367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
5318cb4a1343Smrg
5319cb4a1343Smrg/*
5320cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
5321cb4a1343Smrg * 0 - not recognized
5322cb4a1343Smrg * 1 - set
5323cb4a1343Smrg * 2 - reset
5324cb4a1343Smrg * 3 - permanently set
5325cb4a1343Smrg * 4 - permanently reset
5326cb4a1343Smrg * Only one mode can be reported at a time.
5327cb4a1343Smrg */
5328cb4a1343Smrgvoid
5329d4fba8b9Smrgdo_ansi_rqm(XtermWidget xw, int nparams, int *params)
5330cb4a1343Smrg{
5331cb4a1343Smrg    ANSI reply;
5332cb4a1343Smrg    int count = 0;
5333cb4a1343Smrg
5334d4fba8b9Smrg    TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5335cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5336037a25ddSmrg
5337cb4a1343Smrg    if (nparams >= 1) {
5338d4fba8b9Smrg	int result = mdUnknown;
5339037a25ddSmrg
5340d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5341cb4a1343Smrg	switch (params[0]) {
5342cb4a1343Smrg	case 1:		/* GATM */
5343cb4a1343Smrg	    result = mdAlwaysReset;
5344cb4a1343Smrg	    break;
5345cb4a1343Smrg	case 2:
5346cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
5347cb4a1343Smrg	    break;
5348cb4a1343Smrg	case 3:		/* CRM */
5349cb4a1343Smrg	    result = mdMaybeReset;
5350cb4a1343Smrg	    break;
5351cb4a1343Smrg	case 4:
5352cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
5353cb4a1343Smrg	    break;
5354cb4a1343Smrg	case 5:		/* SRTM */
5355cb4a1343Smrg	case 7:		/* VEM */
5356cb4a1343Smrg	case 10:		/* HEM */
5357cb4a1343Smrg	case 11:		/* PUM */
5358cb4a1343Smrg	    result = mdAlwaysReset;
5359cb4a1343Smrg	    break;
5360cb4a1343Smrg	case 12:
5361cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
5362cb4a1343Smrg	    break;
5363cb4a1343Smrg	case 13:		/* FEAM */
5364cb4a1343Smrg	case 14:		/* FETM */
5365cb4a1343Smrg	case 15:		/* MATM */
5366cb4a1343Smrg	case 16:		/* TTM */
5367cb4a1343Smrg	case 17:		/* SATM */
5368cb4a1343Smrg	case 18:		/* TSM */
5369cb4a1343Smrg	case 19:		/* EBM */
5370cb4a1343Smrg	    result = mdAlwaysReset;
5371cb4a1343Smrg	    break;
5372cb4a1343Smrg	case 20:
5373cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
5374cb4a1343Smrg	    break;
5375cb4a1343Smrg	}
5376cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5377cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5378cb4a1343Smrg    }
5379cb4a1343Smrg    reply.a_type = ANSI_CSI;
5380cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5381cb4a1343Smrg    reply.a_inters = '$';
5382cb4a1343Smrg    reply.a_final = 'y';
5383cb4a1343Smrg    unparseseq(xw, &reply);
5384cb4a1343Smrg}
5385cb4a1343Smrg
5386cb4a1343Smrgvoid
5387d4fba8b9Smrgdo_dec_rqm(XtermWidget xw, int nparams, int *params)
5388cb4a1343Smrg{
5389cb4a1343Smrg    ANSI reply;
5390cb4a1343Smrg    int count = 0;
5391cb4a1343Smrg
5392d4fba8b9Smrg    TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5393cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5394037a25ddSmrg
5395cb4a1343Smrg    if (nparams >= 1) {
5396cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
5397d4fba8b9Smrg	int result = mdUnknown;
5398cb4a1343Smrg
5399d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5400d4fba8b9Smrg	switch ((DECSET_codes) params[0]) {
5401fa3f02f3Smrg	case srm_DECCKM:
5402cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5403cb4a1343Smrg	    break;
5404fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
5405cb4a1343Smrg#if OPT_VT52_MODE
54063367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
5407cb4a1343Smrg#else
5408cb4a1343Smrg	    result = mdMaybeSet;
5409cb4a1343Smrg#endif
5410cb4a1343Smrg	    break;
5411fa3f02f3Smrg	case srm_DECCOLM:
5412cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
5413cb4a1343Smrg	    break;
5414fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
5415cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5416cb4a1343Smrg	    break;
5417fa3f02f3Smrg	case srm_DECSCNM:
5418cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5419cb4a1343Smrg	    break;
5420fa3f02f3Smrg	case srm_DECOM:
5421cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
5422cb4a1343Smrg	    break;
5423fa3f02f3Smrg	case srm_DECAWM:
5424cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
5425cb4a1343Smrg	    break;
5426fa3f02f3Smrg	case srm_DECARM:
5427cb4a1343Smrg	    result = mdAlwaysReset;
5428cb4a1343Smrg	    break;
5429fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
5430cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5431cb4a1343Smrg	    break;
5432cb4a1343Smrg#if OPT_TOOLBAR
5433fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
5434cb4a1343Smrg	    result = MdBool(resource.toolBar);
5435cb4a1343Smrg	    break;
5436cb4a1343Smrg#endif
5437cb4a1343Smrg#if OPT_BLINK_CURS
5438d4fba8b9Smrg	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5439d4fba8b9Smrg	    result = MdBool(screen->cursor_blink_esc);
5440d4fba8b9Smrg	    break;
5441d4fba8b9Smrg	case srm_CURSOR_BLINK_OPS:
5442d4fba8b9Smrg	    switch (screen->cursor_blink) {
5443d4fba8b9Smrg	    case cbTrue:
5444d4fba8b9Smrg		result = mdMaybeSet;
5445d4fba8b9Smrg		break;
5446d4fba8b9Smrg	    case cbFalse:
5447d4fba8b9Smrg		result = mdMaybeReset;
5448d4fba8b9Smrg		break;
5449d4fba8b9Smrg	    case cbAlways:
5450d4fba8b9Smrg		result = mdAlwaysSet;
5451d4fba8b9Smrg		break;
5452d4fba8b9Smrg	    case cbLAST:
5453d4fba8b9Smrg		/* FALLTHRU */
5454d4fba8b9Smrg	    case cbNever:
5455d4fba8b9Smrg		result = mdAlwaysReset;
5456d4fba8b9Smrg		break;
5457d4fba8b9Smrg	    }
5458d4fba8b9Smrg	    break;
5459d4fba8b9Smrg	case srm_XOR_CURSOR_BLINKS:
5460d4fba8b9Smrg	    result = (screen->cursor_blink_xor
5461d4fba8b9Smrg		      ? mdAlwaysSet
5462d4fba8b9Smrg		      : mdAlwaysReset);
5463cb4a1343Smrg	    break;
5464cb4a1343Smrg#endif
5465fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
5466712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
5467cb4a1343Smrg	    break;
5468fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
5469712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
5470cb4a1343Smrg	    break;
5471fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5472cb4a1343Smrg	    result = MdBool(screen->cursor_set);
5473cb4a1343Smrg	    break;
5474fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
5475cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5476cb4a1343Smrg	    break;
5477cb4a1343Smrg#if OPT_SHIFT_FONTS
5478fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
5479cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
5480cb4a1343Smrg	    break;
5481cb4a1343Smrg#endif
5482cb4a1343Smrg#if OPT_TEK4014
5483fa3f02f3Smrg	case srm_DECTEK:
5484cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
5485cb4a1343Smrg	    break;
5486cb4a1343Smrg#endif
5487fa3f02f3Smrg	case srm_132COLS:
5488cb4a1343Smrg	    result = MdBool(screen->c132);
5489cb4a1343Smrg	    break;
5490fa3f02f3Smrg	case srm_CURSES_HACK:
5491cb4a1343Smrg	    result = MdBool(screen->curses);
5492cb4a1343Smrg	    break;
5493fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
5494d4fba8b9Smrg	    if (screen->vtXX_level >= 2) {
5495d4fba8b9Smrg		result = MdFlag(xw->flags, NATIONAL);
5496d4fba8b9Smrg	    } else {
5497d4fba8b9Smrg		result = 0;
5498d4fba8b9Smrg	    }
5499cb4a1343Smrg	    break;
5500fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
5501cb4a1343Smrg	    result = MdBool(screen->marginbell);
5502cb4a1343Smrg	    break;
5503d4fba8b9Smrg#if OPT_PRINT_GRAPHICS
5504d4fba8b9Smrg	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5505d4fba8b9Smrg	    result = MdBool(screen->graphics_expanded_print_mode);
5506d4fba8b9Smrg	    break;
5507d4fba8b9Smrg#endif
5508fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
5509d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5510d4fba8b9Smrg		result = MdFlag(xw->flags, REVERSEWRAP);
5511cb4a1343Smrg	    break;
5512980988aeSmrg	case srm_REVERSEWRAP2:	/* extended reverse wraparound   */
5513980988aeSmrg	    result = MdFlag(xw->flags, REVERSEWRAP2);
5514980988aeSmrg	    break;
5515d4fba8b9Smrg#if defined(ALLOWLOGGING)
5516fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
5517d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5518d4fba8b9Smrg#if defined(ALLOWLOGFILEONOFF)
5519d4fba8b9Smrg		result = MdBool(screen->logging);
5520d4fba8b9Smrg#else
5521d4fba8b9Smrg		result = ((MdBool(screen->logging) == mdMaybeSet)
5522d4fba8b9Smrg			  ? mdAlwaysSet
5523d4fba8b9Smrg			  : mdAlwaysReset);
5524d4fba8b9Smrg#endif
5525d4fba8b9Smrg	    break;
5526d4fba8b9Smrg#elif OPT_PRINT_GRAPHICS
5527d4fba8b9Smrg	case srm_DECGPBM:	/* Graphics Print Background Mode */
5528d4fba8b9Smrg	    result = MdBool(screen->graphics_print_background_mode);
5529cb4a1343Smrg	    break;
5530cb4a1343Smrg#endif
5531fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5532cb4a1343Smrg	    /* FALLTHRU */
5533fa3f02f3Smrg	case srm_OPT_ALTBUF:
5534cb4a1343Smrg	    result = MdBool(screen->whichBuf);
5535cb4a1343Smrg	    break;
5536d4fba8b9Smrg	case srm_ALTBUF:
5537d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5538d4fba8b9Smrg		result = MdBool(screen->whichBuf);
5539d4fba8b9Smrg	    break;
5540fa3f02f3Smrg	case srm_DECNKM:
5541cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5542cb4a1343Smrg	    break;
5543fa3f02f3Smrg	case srm_DECBKM:
5544cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5545cb4a1343Smrg	    break;
5546fa3f02f3Smrg	case srm_DECLRMM:
5547d4fba8b9Smrg	    if (screen->vtXX_level >= 4) {	/* VT420 */
5548d4fba8b9Smrg		result = MdFlag(xw->flags, LEFT_RIGHT);
5549d4fba8b9Smrg	    } else {
5550d4fba8b9Smrg		result = 0;
5551d4fba8b9Smrg	    }
55523367019cSmrg	    break;
5553fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
5554fa3f02f3Smrg	case srm_DECSDM:
5555fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5556fa3f02f3Smrg	    break;
5557fa3f02f3Smrg#endif
5558190d7dceSmrg	case srm_DECNCSM:	/* no clearing screen on column change */
5559d4fba8b9Smrg	    if (screen->vtXX_level >= 5) {	/* VT510 */
5560d4fba8b9Smrg		result = MdFlag(xw->flags, NOCLEAR_COLM);
5561d4fba8b9Smrg	    } else {
5562d4fba8b9Smrg		result = 0;
5563d4fba8b9Smrg	    }
55643367019cSmrg	    break;
5565d4fba8b9Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5566cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5567cb4a1343Smrg	    break;
5568fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5569cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5570cb4a1343Smrg	    break;
5571fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
5572cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5573cb4a1343Smrg	    break;
5574fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
5575cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5576cb4a1343Smrg	    break;
5577cb4a1343Smrg#if OPT_FOCUS_EVENT
5578fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
5579cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
5580cb4a1343Smrg	    break;
5581cb4a1343Smrg#endif
5582fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
55833367019cSmrg	    /* FALLTHRU */
5584fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
55853367019cSmrg	    /* FALLTHRU */
5586fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
5587d4fba8b9Smrg	    /* FALLTHRU */
5588d4fba8b9Smrg	case srm_PIXEL_POSITION_MOUSE:
55893367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
55903367019cSmrg	    break;
5591fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
55923367019cSmrg	    result = MdBool(screen->alternateScroll);
5593cb4a1343Smrg	    break;
5594fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
5595cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
5596cb4a1343Smrg	    break;
5597fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5598cb4a1343Smrg	    result = MdBool(screen->scrollkey);
5599cb4a1343Smrg	    break;
5600fa3f02f3Smrg	case srm_EIGHT_BIT_META:
56013367019cSmrg	    result = MdBool(screen->eight_bit_meta);
5602cb4a1343Smrg	    break;
5603cb4a1343Smrg#if OPT_NUM_LOCK
5604fa3f02f3Smrg	case srm_REAL_NUMLOCK:
5605cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
5606cb4a1343Smrg	    break;
5607fa3f02f3Smrg	case srm_META_SENDS_ESC:
5608cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
5609cb4a1343Smrg	    break;
5610cb4a1343Smrg#endif
5611fa3f02f3Smrg	case srm_DELETE_IS_DEL:
5612d4fba8b9Smrg	    result = MdBool(xtermDeleteIsDEL(xw));
5613cb4a1343Smrg	    break;
5614cb4a1343Smrg#if OPT_NUM_LOCK
5615fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
5616cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
5617cb4a1343Smrg	    break;
5618cb4a1343Smrg#endif
5619fa3f02f3Smrg	case srm_KEEP_SELECTION:
5620cb4a1343Smrg	    result = MdBool(screen->keepSelection);
5621cb4a1343Smrg	    break;
5622fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
5623cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
5624cb4a1343Smrg	    break;
5625fa3f02f3Smrg	case srm_BELL_IS_URGENT:
5626cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
5627cb4a1343Smrg	    break;
5628fa3f02f3Smrg	case srm_POP_ON_BELL:
5629cb4a1343Smrg	    result = MdBool(screen->poponbell);
5630cb4a1343Smrg	    break;
5631d4fba8b9Smrg	case srm_KEEP_CLIPBOARD:
5632d4fba8b9Smrg	    result = MdBool(screen->keepClipboard);
5633d4fba8b9Smrg	    break;
5634d4fba8b9Smrg	case srm_ALLOW_ALTBUF:
5635d4fba8b9Smrg	    result = MdBool(xw->misc.titeInhibit);
5636d4fba8b9Smrg	    break;
5637d4fba8b9Smrg	case srm_SAVE_CURSOR:
5638cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
5639cb4a1343Smrg	    break;
5640980988aeSmrg	case srm_FAST_SCROLL:
5641980988aeSmrg	    result = MdBool(screen->fastscroll);
5642980988aeSmrg	    break;
5643cb4a1343Smrg#if OPT_TCAP_FKEYS
5644fa3f02f3Smrg	case srm_TCAP_FKEYS:
5645cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5646cb4a1343Smrg	    break;
5647cb4a1343Smrg#endif
5648cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
5649fa3f02f3Smrg	case srm_SUN_FKEYS:
5650cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5651cb4a1343Smrg	    break;
5652cb4a1343Smrg#endif
5653cb4a1343Smrg#if OPT_HP_FUNC_KEYS
5654fa3f02f3Smrg	case srm_HP_FKEYS:
5655cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5656cb4a1343Smrg	    break;
5657cb4a1343Smrg#endif
5658cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
5659fa3f02f3Smrg	case srm_SCO_FKEYS:
5660cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5661cb4a1343Smrg	    break;
5662cb4a1343Smrg#endif
5663fa3f02f3Smrg	case srm_LEGACY_FKEYS:
5664cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5665cb4a1343Smrg	    break;
5666cb4a1343Smrg#if OPT_SUNPC_KBD
5667fa3f02f3Smrg	case srm_VT220_FKEYS:
5668cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5669cb4a1343Smrg	    break;
5670cb4a1343Smrg#endif
5671d4fba8b9Smrg#if OPT_PASTE64 || OPT_READLINE
5672d4fba8b9Smrg	case srm_PASTE_IN_BRACKET:
5673d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5674d4fba8b9Smrg	    break;
5675d4fba8b9Smrg#endif
5676cb4a1343Smrg#if OPT_READLINE
5677fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
5678d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5679cb4a1343Smrg	    break;
5680fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
5681d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5682cb4a1343Smrg	    break;
5683fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
5684d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5685cb4a1343Smrg	    break;
5686fa3f02f3Smrg	case srm_PASTE_QUOTE:
5687d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5688cb4a1343Smrg	    break;
5689fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
5690d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5691cb4a1343Smrg	    break;
5692cb4a1343Smrg#endif /* OPT_READLINE */
5693d4fba8b9Smrg#if OPT_GRAPHICS
56949a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
56959a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
56969a64e1c5Smrg	    break;
56979a64e1c5Smrg#endif
56989a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
56999a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
57009a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
57019a64e1c5Smrg	    break;
57029a64e1c5Smrg#endif
5703190d7dceSmrg	    /* the remainder are recognized but unimplemented */
5704190d7dceSmrg	    /* VT3xx */
5705190d7dceSmrg	case srm_DEC131TM:	/* vt330:VT131 transmit */
5706190d7dceSmrg	case srm_DECEKEM:	/* vt330:edit key execution */
5707190d7dceSmrg	case srm_DECHCCM:	/* vt320:Horizontal Cursor-Coupling Mode */
5708190d7dceSmrg	case srm_DECKBUM:	/* vt330:Keyboard Usage mode */
5709190d7dceSmrg	case srm_DECKKDM:	/* vt382:Kanji/Katakana */
5710190d7dceSmrg	case srm_DECLTM:	/* vt330:line transmit */
5711190d7dceSmrg	case srm_DECPCCM:	/* vt330:Page Cursor-Coupling Mode */
5712190d7dceSmrg	case srm_DECVCCM:	/* vt330:Vertical Cursor-Coupling Mode */
5713190d7dceSmrg	case srm_DECXRLM:	/* vt330:Transmit Rate Limiting */
5714190d7dceSmrg#if !OPT_BLINK_CURS
5715190d7dceSmrg	case srm_DECKANAM:	/* vt382:Katakana shift */
5716190d7dceSmrg	case srm_DECSCFDM:	/* vt330:space compression field delimiter */
5717190d7dceSmrg	case srm_DECTEM:	/* vt330:transmission execution */
5718190d7dceSmrg#endif
5719190d7dceSmrg#if !OPT_TOOLBAR
5720190d7dceSmrg	case srm_DECEDM:	/* vt330:edit */
5721190d7dceSmrg#endif
5722190d7dceSmrg	    if (screen->vtXX_level >= 3)
5723190d7dceSmrg		result = mdAlwaysReset;
5724190d7dceSmrg	    break;
5725190d7dceSmrg	    /* VT4xx */
5726190d7dceSmrg	case srm_DECKPM:	/* vt420:Key Position Mode */
5727190d7dceSmrg	    if (screen->vtXX_level >= 4)
5728190d7dceSmrg		result = mdAlwaysReset;
5729190d7dceSmrg	    break;
5730190d7dceSmrg	    /* VT5xx */
5731190d7dceSmrg	case srm_DECAAM:	/* vt510:auto answerback */
5732190d7dceSmrg	case srm_DECARSM:	/* vt510:auto resize */
5733190d7dceSmrg	case srm_DECATCBM:	/* vt520:alternate text color blink */
5734190d7dceSmrg	case srm_DECATCUM:	/* vt520:alternate text color underline */
5735190d7dceSmrg	case srm_DECBBSM:	/* vt520:bold and blink style */
5736190d7dceSmrg	case srm_DECCANSM:	/* vt510:conceal answerback */
5737190d7dceSmrg	case srm_DECCAPSLK:	/* vt510:Caps Lock Mode */
5738190d7dceSmrg	case srm_DECCRTSM:	/* vt510:CRT save */
5739190d7dceSmrg	case srm_DECECM:	/* vt520:erase color */
5740190d7dceSmrg	case srm_DECESKM:	/* vt510:enable secondary keyboard language */
5741190d7dceSmrg	case srm_DECFWM:	/* vt520:framed windows */
5742190d7dceSmrg	case srm_DECHDPXM:	/* vt510:half duplex */
5743190d7dceSmrg	case srm_DECHEM:	/* vt510:Hebrew encoding */
5744190d7dceSmrg	case srm_DECHWUM:	/* vt520:host wake-up mode (CRT and energy saver) */
5745190d7dceSmrg	case srm_DECIPEM:	/* vt510:IBM ProPrinter Emulation Mode */
5746190d7dceSmrg	case srm_DECKLHIM:	/* vt510:ignore */
5747190d7dceSmrg	case srm_DECMCM:	/* vt510:modem control */
5748190d7dceSmrg	case srm_DECNAKB:	/* vt510:Greek/N-A Keyboard Mapping */
5749190d7dceSmrg	case srm_DECNULM:	/* vt510:Ignoring Null Mode */
5750190d7dceSmrg	case srm_DECNUMLK:	/* vt510:Num Lock Mode */
5751190d7dceSmrg	case srm_DECOSCNM:	/* vt510:Overscan Mode */
5752190d7dceSmrg	case srm_DECRLCM:	/* vt510:Right-to-Left Copy */
5753190d7dceSmrg	case srm_DECRLM:	/* vt510:left-to-right */
5754190d7dceSmrg	case srm_DECRPL:	/* vt520:Review Previous Lines */
5755190d7dceSmrg#if !OPT_SHIFT_FONTS
5756190d7dceSmrg	case srm_DECHEBM:	/* vt520:Hebrew keyboard mapping */
5757190d7dceSmrg#endif
5758190d7dceSmrg	    if (screen->vtXX_level >= 5)
5759190d7dceSmrg		result = mdAlwaysReset;
5760190d7dceSmrg	    break;
57619a64e1c5Smrg	default:
57629a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
57639a64e1c5Smrg		   params[0]));
5764cb4a1343Smrg	}
5765cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5766cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5767d4fba8b9Smrg	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5768cb4a1343Smrg    }
5769cb4a1343Smrg    reply.a_type = ANSI_CSI;
5770cb4a1343Smrg    reply.a_pintro = '?';
5771cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5772cb4a1343Smrg    reply.a_inters = '$';
5773cb4a1343Smrg    reply.a_final = 'y';
5774cb4a1343Smrg    unparseseq(xw, &reply);
5775cb4a1343Smrg}
5776cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
5777cb4a1343Smrg
5778d522f475Smrgchar *
57799a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
5780d522f475Smrg{
5781d4fba8b9Smrg    char *result = NULL;
5782d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
57839a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
5784d4fba8b9Smrg	result = xw->work.user_keys[keycode].str;
5785d4fba8b9Smrg	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5786d4fba8b9Smrg    } else {
5787d4fba8b9Smrg	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5788d4fba8b9Smrg    }
5789d4fba8b9Smrg    return result;
5790d4fba8b9Smrg}
5791d4fba8b9Smrg
5792d4fba8b9Smrg#if OPT_REPORT_ICONS
5793d4fba8b9Smrgvoid
5794d4fba8b9Smrgreport_icons(const char *fmt, ...)
5795d4fba8b9Smrg{
5796d4fba8b9Smrg    if (resource.reportIcons) {
5797d4fba8b9Smrg	va_list ap;
5798d4fba8b9Smrg	va_start(ap, fmt);
5799d4fba8b9Smrg	vfprintf(stdout, fmt, ap);
5800d4fba8b9Smrg	va_end(ap);
5801d4fba8b9Smrg#if OPT_TRACE
5802d4fba8b9Smrg	va_start(ap, fmt);
5803d4fba8b9Smrg	TraceVA(fmt, ap);
5804d4fba8b9Smrg	va_end(ap);
5805d4fba8b9Smrg#endif
5806d522f475Smrg    }
5807d522f475Smrg}
5808d4fba8b9Smrg#endif
5809d522f475Smrg
58103367019cSmrg#ifdef HAVE_LIBXPM
58113367019cSmrg
58123367019cSmrg#ifndef PIXMAP_ROOTDIR
58133367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
58143367019cSmrg#endif
58153367019cSmrg
58163367019cSmrgtypedef struct {
58173367019cSmrg    const char *name;
58183367019cSmrg    const char *const *data;
58193367019cSmrg} XPM_DATA;
58203367019cSmrg
58213367019cSmrgstatic char *
5822d4fba8b9Smrgx_find_icon(char **work, int *state, const char *filename, const char *suffix)
58233367019cSmrg{
58243367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
58253367019cSmrg    const char *larger = "_48x48";
5826190d7dceSmrg    char *result = NULL;
58273367019cSmrg
58283367019cSmrg    if (*state >= 0) {
58293367019cSmrg	if ((*state & 1) == 0)
58303367019cSmrg	    suffix = "";
58313367019cSmrg	if ((*state & 2) == 0)
58323367019cSmrg	    larger = "";
58333367019cSmrg	if ((*state & 4) == 0) {
58343367019cSmrg	    prefix = "";
58353367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
58363367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
58373367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
58383367019cSmrg	    *state = -1;
58393367019cSmrg	} else if (*state >= 8) {
58403367019cSmrg	    *state = -1;
58413367019cSmrg	}
58423367019cSmrg    }
58433367019cSmrg
58443367019cSmrg    if (*state >= 0) {
5845037a25ddSmrg	size_t length;
5846037a25ddSmrg
5847d4fba8b9Smrg	FreeAndNull(*work);
58483367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
58493367019cSmrg	    strlen(suffix);
5850190d7dceSmrg	if ((result = malloc(length)) != NULL) {
58513367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
58523367019cSmrg	    *work = result;
58533367019cSmrg	}
58543367019cSmrg	*state += 1;
58553367019cSmrg    }
5856d4fba8b9Smrg    TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
58573367019cSmrg    return result;
58583367019cSmrg}
58593367019cSmrg
58603367019cSmrg#if OPT_BUILTIN_XPMS
5861d4fba8b9Smrg
58623367019cSmrgstatic const XPM_DATA *
5863d4fba8b9Smrgbuilt_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
58643367019cSmrg{
5865190d7dceSmrg    const XPM_DATA *result = NULL;
58663367019cSmrg    if (!IsEmpty(find)) {
58673367019cSmrg	Cardinal n;
58683367019cSmrg	for (n = 0; n < length; ++n) {
58693367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
58703367019cSmrg		result = table + n;
5871d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[n].name));
58723367019cSmrg		break;
58733367019cSmrg	    }
58743367019cSmrg	}
58753367019cSmrg
58763367019cSmrg	/*
58773367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
58783367019cSmrg	 * which are all _HHxWW format.
58793367019cSmrg	 */
5880190d7dceSmrg	if (result == NULL) {
58813367019cSmrg	    const char *base = table[0].name;
58823367019cSmrg	    const char *last = strchr(base, '_');
5883190d7dceSmrg	    if (last != NULL
58843367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
58853367019cSmrg		result = table + length - 1;
5886d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[0].name));
58873367019cSmrg	    }
58883367019cSmrg	}
58893367019cSmrg    }
58903367019cSmrg    return result;
58913367019cSmrg}
5892d4fba8b9Smrg#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
58933367019cSmrg#endif /* OPT_BUILTIN_XPMS */
58943367019cSmrg
58953367019cSmrgtypedef enum {
58963367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
58973367019cSmrg    ,eHintNone
58983367019cSmrg    ,eHintSearch
58993367019cSmrg} ICON_HINT;
59003367019cSmrg#endif /* HAVE_LIBXPM */
59013367019cSmrg
59023367019cSmrgint
59033367019cSmrggetVisualDepth(XtermWidget xw)
59043367019cSmrg{
59053367019cSmrg    int result = 0;
59063367019cSmrg
5907fa3f02f3Smrg    if (getVisualInfo(xw)) {
5908fa3f02f3Smrg	result = xw->visInfo->depth;
59093367019cSmrg    }
59103367019cSmrg    return result;
59113367019cSmrg}
59123367019cSmrg
59133367019cSmrg/*
59143367019cSmrg * WM_ICON_SIZE should be honored if possible.
59153367019cSmrg */
59163367019cSmrgvoid
5917d4fba8b9SmrgxtermLoadIcon(XtermWidget xw, const char *icon_hint)
59183367019cSmrg{
59193367019cSmrg#ifdef HAVE_LIBXPM
59203367019cSmrg    Display *dpy = XtDisplay(xw);
59213367019cSmrg    Pixmap myIcon = 0;
59223367019cSmrg    Pixmap myMask = 0;
5923190d7dceSmrg    char *workname = NULL;
5924d4fba8b9Smrg    ICON_HINT hint = eHintDefault;
5925fa3f02f3Smrg#include <builtin_icons.h>
59263367019cSmrg
5927d4fba8b9Smrg    ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5928d4fba8b9Smrg    if (!IsEmpty(icon_hint)) {
5929d4fba8b9Smrg	if (!x_strcasecmp(icon_hint, "none")) {
5930d4fba8b9Smrg	    hint = eHintNone;
5931d4fba8b9Smrg	} else {
5932d4fba8b9Smrg	    hint = eHintSearch;
5933d4fba8b9Smrg	}
5934d4fba8b9Smrg    }
59353367019cSmrg
59363367019cSmrg    if (hint == eHintSearch) {
59373367019cSmrg	int state = 0;
5938190d7dceSmrg	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != NULL) {
59393367019cSmrg	    Pixmap resIcon = 0;
59403367019cSmrg	    Pixmap shapemask = 0;
59413367019cSmrg	    XpmAttributes attributes;
5942d4fba8b9Smrg	    struct stat sb;
59433367019cSmrg
59443367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
59453367019cSmrg	    attributes.valuemask = XpmDepth;
59463367019cSmrg
5947d4fba8b9Smrg	    if (IsEmpty(workname)
5948d4fba8b9Smrg		|| lstat(workname, &sb) != 0
5949d4fba8b9Smrg		|| !S_ISREG(sb.st_mode)) {
5950d4fba8b9Smrg		TRACE(("...failure (no such file)\n"));
5951d4fba8b9Smrg	    } else {
5952d4fba8b9Smrg		int rc = XpmReadFileToPixmap(dpy,
5953d4fba8b9Smrg					     DefaultRootWindow(dpy),
5954d4fba8b9Smrg					     workname,
5955d4fba8b9Smrg					     &resIcon,
5956d4fba8b9Smrg					     &shapemask,
5957d4fba8b9Smrg					     &attributes);
5958d4fba8b9Smrg		if (rc == XpmSuccess) {
5959d4fba8b9Smrg		    myIcon = resIcon;
5960d4fba8b9Smrg		    myMask = shapemask;
5961d4fba8b9Smrg		    TRACE(("...success\n"));
5962d4fba8b9Smrg		    ReportIcons(("found/loaded icon-file %s\n", workname));
5963d4fba8b9Smrg		    break;
5964d4fba8b9Smrg		} else {
5965d4fba8b9Smrg		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5966d4fba8b9Smrg		}
59673367019cSmrg	    }
59683367019cSmrg	}
59693367019cSmrg    }
59703367019cSmrg
59713367019cSmrg    /*
59723367019cSmrg     * If no external file was found, look for the name in the built-in table.
59733367019cSmrg     * If that fails, just use the biggest mini-icon.
59743367019cSmrg     */
59753367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
59763367019cSmrg	char **data;
59773367019cSmrg#if OPT_BUILTIN_XPMS
5978190d7dceSmrg	const XPM_DATA *myData = NULL;
5979d4fba8b9Smrg	myData = BuiltInXPM(mini_xterm_xpms);
5980190d7dceSmrg	if (myData == NULL)
5981d4fba8b9Smrg	    myData = BuiltInXPM(filled_xterm_xpms);
5982190d7dceSmrg	if (myData == NULL)
5983d4fba8b9Smrg	    myData = BuiltInXPM(xterm_color_xpms);
5984190d7dceSmrg	if (myData == NULL)
5985d4fba8b9Smrg	    myData = BuiltInXPM(xterm_xpms);
5986190d7dceSmrg	if (myData == NULL)
59873367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
598894644356Smrg	data = (char **) myData->data;
59893367019cSmrg#else
59903367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
59913367019cSmrg#endif
59923367019cSmrg	if (XpmCreatePixmapFromData(dpy,
59933367019cSmrg				    DefaultRootWindow(dpy),
59943367019cSmrg				    data,
5995190d7dceSmrg				    &myIcon, &myMask, NULL) == 0) {
5996d4fba8b9Smrg	    ReportIcons(("loaded built-in pixmap icon\n"));
5997d4fba8b9Smrg	} else {
59983367019cSmrg	    myIcon = 0;
59993367019cSmrg	    myMask = 0;
60003367019cSmrg	}
60013367019cSmrg    }
60023367019cSmrg
60033367019cSmrg    if (myIcon != 0) {
60043367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
60053367019cSmrg	if (!hints)
60063367019cSmrg	    hints = XAllocWMHints();
60073367019cSmrg
60083367019cSmrg	if (hints) {
60093367019cSmrg	    hints->flags |= IconPixmapHint;
60103367019cSmrg	    hints->icon_pixmap = myIcon;
60113367019cSmrg	    if (myMask) {
60123367019cSmrg		hints->flags |= IconMaskHint;
60133367019cSmrg		hints->icon_mask = myMask;
60143367019cSmrg	    }
60153367019cSmrg
60163367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
60173367019cSmrg	    XFree(hints);
6018d4fba8b9Smrg	    ReportIcons(("updated window-manager hints\n"));
60193367019cSmrg	}
60203367019cSmrg    }
60213367019cSmrg
6022d4fba8b9Smrg    free(workname);
60233367019cSmrg
60243367019cSmrg#else
60253367019cSmrg    (void) xw;
6026d4fba8b9Smrg    (void) icon_hint;
60273367019cSmrg#endif
60283367019cSmrg}
60293367019cSmrg
60303367019cSmrgvoid
6031cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
6032d522f475Smrg{
6033d522f475Smrg    Arg args[1];
6034cd3331d0Smrg    Boolean changed = True;
6035d522f475Smrg    Widget w = CURRENT_EMU();
6036d522f475Smrg    Widget top = SHELL_OF(w);
6037d522f475Smrg
6038d4fba8b9Smrg    char *my_attr = NULL;
6039d4fba8b9Smrg    char *old_value = value;
6040d4fba8b9Smrg#if OPT_WIDE_CHARS
6041d4fba8b9Smrg    Boolean titleIsUTF8;
6042d4fba8b9Smrg#endif
6043d522f475Smrg
6044b7c89284Ssnj    if (!AllowTitleOps(xw))
6045d522f475Smrg	return;
6046d522f475Smrg
6047d4fba8b9Smrg    /*
6048d4fba8b9Smrg     * Ignore empty or too-long requests.
6049d4fba8b9Smrg     */
6050190d7dceSmrg    if (value == NULL || strlen(value) > 1000)
6051d4fba8b9Smrg	return;
6052d4fba8b9Smrg
6053cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
6054cd3331d0Smrg	const char *temp;
6055cd3331d0Smrg	char *test;
6056cd3331d0Smrg
6057d4fba8b9Smrg	/* this allocates a new string, if no error is detected */
6058cd3331d0Smrg	value = x_decode_hex(value, &temp);
6059190d7dceSmrg	if (value == NULL || *temp != '\0') {
60603367019cSmrg	    free(value);
6061cd3331d0Smrg	    return;
60623367019cSmrg	}
6063cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
6064cd3331d0Smrg	    if (CharOf(*test) < 32) {
6065cd3331d0Smrg		*test = '\0';
6066cd3331d0Smrg		break;
6067cd3331d0Smrg	    }
6068cd3331d0Smrg	}
6069cd3331d0Smrg    }
6070d4fba8b9Smrg#if OPT_WIDE_CHARS
6071d522f475Smrg    /*
6072d4fba8b9Smrg     * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
6073d4fba8b9Smrg     * the WM_NAME property, rather than doing this directly.  That relies on
6074d4fba8b9Smrg     * the application to tell it if the format should be something other than
6075d4fba8b9Smrg     * STRING, i.e., by setting the XtNtitleEncoding resource.
6076d4fba8b9Smrg     *
6077d4fba8b9Smrg     * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
6078d4fba8b9Smrg     * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
6079d4fba8b9Smrg     * added UTF8_STRING (the documentation for that was discarded by an Xorg
6080d4fba8b9Smrg     * developer, although the source-code provides this feature).
6081d4fba8b9Smrg     *
6082d4fba8b9Smrg     * Since X11R5, if the X11 library fails to store a text property as
6083d4fba8b9Smrg     * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
6084d4fba8b9Smrg     * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
6085d4fba8b9Smrg     * case, limit the resulting characters to the printable ISO-8859-1 set.
6086d522f475Smrg     */
6087d4fba8b9Smrg    titleIsUTF8 = isValidUTF8((Char *) value);
6088d4fba8b9Smrg    if (IsSetUtf8Title(xw) && titleIsUTF8) {
6089d4fba8b9Smrg	char *testc = malloc(strlen(value) + 1);
6090d4fba8b9Smrg	Char *nextc = (Char *) value;
6091d4fba8b9Smrg	Boolean ok8bit = True;
6092d522f475Smrg
6093d4fba8b9Smrg	if (testc != NULL) {
6094d4fba8b9Smrg	    /*
6095d4fba8b9Smrg	     * Check if the data fits in STRING.  Along the way, replace
6096d4fba8b9Smrg	     * control characters.
6097d4fba8b9Smrg	     */
6098d4fba8b9Smrg	    Char *lastc = (Char *) testc;
6099d4fba8b9Smrg	    while (*nextc != '\0') {
6100d4fba8b9Smrg		unsigned ch;
6101d4fba8b9Smrg		nextc = convertFromUTF8(nextc, &ch);
6102d4fba8b9Smrg		if (ch > 255) {
6103d4fba8b9Smrg		    ok8bit = False;
6104d4fba8b9Smrg		} else if (!IsLatin1(ch)) {
6105d4fba8b9Smrg		    ch = OnlyLatin1(ch);
6106d4fba8b9Smrg		}
6107d4fba8b9Smrg		*lastc++ = (Char) ch;
6108d4fba8b9Smrg	    }
6109d4fba8b9Smrg	    *lastc = '\0';
6110d4fba8b9Smrg	    if (ok8bit) {
6111d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
6112d4fba8b9Smrg		if (value != old_value)
6113d4fba8b9Smrg		    free(value);
6114d4fba8b9Smrg		value = testc;
6115d4fba8b9Smrg		titleIsUTF8 = False;
6116d4fba8b9Smrg	    } else {
6117d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
6118d4fba8b9Smrg		       "\t%s\n", value));
6119d4fba8b9Smrg		free(testc);
6120d4fba8b9Smrg		nextc = (Char *) value;
6121d4fba8b9Smrg		while (*nextc != '\0') {
6122d4fba8b9Smrg		    unsigned ch;
6123d4fba8b9Smrg		    Char *skip = convertFromUTF8(nextc, &ch);
6124d4fba8b9Smrg		    if (iswcntrl((wint_t) ch)) {
6125d4fba8b9Smrg			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
6126d4fba8b9Smrg		    }
6127d4fba8b9Smrg		    nextc = skip;
6128d4fba8b9Smrg		}
6129cd3331d0Smrg	    }
6130d522f475Smrg	}
6131d4fba8b9Smrg    } else
6132d4fba8b9Smrg#endif
6133d4fba8b9Smrg    {
6134d4fba8b9Smrg	Char *c1 = (Char *) value;
6135d4fba8b9Smrg
6136d4fba8b9Smrg	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
6137d4fba8b9Smrg	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
6138d4fba8b9Smrg	    *c1 = (Char) OnlyLatin1(*c1);
6139d4fba8b9Smrg	}
6140d4fba8b9Smrg    }
6141d4fba8b9Smrg
6142d4fba8b9Smrg    my_attr = x_strdup(attribute);
6143d4fba8b9Smrg
6144d4fba8b9Smrg    ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
6145d522f475Smrg
6146d522f475Smrg#if OPT_WIDE_CHARS
6147d4fba8b9Smrg    /*
6148d4fba8b9Smrg     * If we're running in UTF-8 mode, and have not been told that the
6149d4fba8b9Smrg     * title string is in UTF-8, it is likely that non-ASCII text in the
6150d4fba8b9Smrg     * string will be rejected because it is not printable in the current
6151d4fba8b9Smrg     * locale.  So we convert it to UTF-8, allowing the X library to
6152d4fba8b9Smrg     * convert it back.
6153d4fba8b9Smrg     */
6154d4fba8b9Smrg    TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
6155d4fba8b9Smrg    if (xtermEnvUTF8() && !titleIsUTF8) {
6156d4fba8b9Smrg	size_t limit = strlen(value);
6157d4fba8b9Smrg	Char *c1 = (Char *) value;
6158d4fba8b9Smrg	int n;
6159cd3331d0Smrg
6160d4fba8b9Smrg	for (n = 0; c1[n] != '\0'; ++n) {
6161d4fba8b9Smrg	    if (c1[n] > 127) {
6162d4fba8b9Smrg		Char *converted;
6163190d7dceSmrg		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != NULL) {
6164d4fba8b9Smrg		    Char *temp = converted;
6165d4fba8b9Smrg		    while (*c1 != 0) {
6166d4fba8b9Smrg			temp = convertToUTF8(temp, *c1++);
6167d522f475Smrg		    }
6168d4fba8b9Smrg		    *temp = 0;
6169d4fba8b9Smrg		    if (value != old_value)
6170d4fba8b9Smrg			free(value);
6171d4fba8b9Smrg		    value = (char *) converted;
6172d4fba8b9Smrg		    ReportIcons(("...converted{%s}\n", value));
6173d522f475Smrg		}
6174d4fba8b9Smrg		break;
6175d522f475Smrg	    }
6176d522f475Smrg	}
6177d4fba8b9Smrg    }
6178d522f475Smrg#endif
6179d522f475Smrg
6180d522f475Smrg#if OPT_SAME_NAME
6181d4fba8b9Smrg    /* If the attribute isn't going to change, then don't bother... */
6182d4fba8b9Smrg    if (resource.sameName) {
6183190d7dceSmrg	char *buf = NULL;
6184d4fba8b9Smrg	XtSetArg(args[0], my_attr, &buf);
6185d4fba8b9Smrg	XtGetValues(top, args, 1);
6186980988aeSmrg	TRACE(("...comparing resource{%s} to new value{%s}\n",
6187980988aeSmrg	       NonNull(buf),
6188980988aeSmrg	       NonNull(value)));
6189190d7dceSmrg	if (buf != NULL && strcmp(value, buf) == 0)
6190d4fba8b9Smrg	    changed = False;
6191d4fba8b9Smrg    }
6192d522f475Smrg#endif /* OPT_SAME_NAME */
6193d522f475Smrg
6194d4fba8b9Smrg    if (changed) {
6195d4fba8b9Smrg	ReportIcons(("...updating %s\n", my_attr));
6196d4fba8b9Smrg	ReportIcons(("...value is %s\n", value));
6197d4fba8b9Smrg	XtSetArg(args[0], my_attr, value);
6198d4fba8b9Smrg	XtSetValues(top, args, 1);
6199d4fba8b9Smrg    }
6200d522f475Smrg#if OPT_WIDE_CHARS
6201d4fba8b9Smrg    if (xtermEnvUTF8()) {
6202d4fba8b9Smrg	Display *dpy = XtDisplay(xw);
6203d4fba8b9Smrg	const char *propname = (!strcmp(my_attr, XtNtitle)
6204d4fba8b9Smrg				? "_NET_WM_NAME"
6205d4fba8b9Smrg				: "_NET_WM_ICON_NAME");
6206980988aeSmrg	Atom my_atom = CachedInternAtom(dpy, propname);
6207d4fba8b9Smrg
6208d4fba8b9Smrg	if (my_atom != None) {
6209d4fba8b9Smrg	    changed = True;
6210d4fba8b9Smrg
6211d4fba8b9Smrg	    if (IsSetUtf8Title(xw)) {
6212d4fba8b9Smrg#if OPT_SAME_NAME
6213d4fba8b9Smrg		if (resource.sameName) {
6214d4fba8b9Smrg		    Atom actual_type;
6215d4fba8b9Smrg		    Atom requested_type = XA_UTF8_STRING(dpy);
6216d4fba8b9Smrg		    int actual_format = 0;
6217d4fba8b9Smrg		    long long_length = 1024;
6218d4fba8b9Smrg		    unsigned long nitems = 0;
6219d4fba8b9Smrg		    unsigned long bytes_after = 0;
6220190d7dceSmrg		    unsigned char *prop = NULL;
6221d4fba8b9Smrg
6222d4fba8b9Smrg		    if (xtermGetWinProp(dpy,
6223d4fba8b9Smrg					VShellWindow(xw),
6224d4fba8b9Smrg					my_atom,
6225d4fba8b9Smrg					0L,
6226d4fba8b9Smrg					long_length,
6227d4fba8b9Smrg					requested_type,
6228d4fba8b9Smrg					&actual_type,
6229d4fba8b9Smrg					&actual_format,
6230d4fba8b9Smrg					&nitems,
6231d4fba8b9Smrg					&bytes_after,
623250027b5bSmrg					&prop)) {
623350027b5bSmrg			if (actual_type == requested_type
623450027b5bSmrg			    && actual_format == 8
6235190d7dceSmrg			    && prop != NULL
623650027b5bSmrg			    && nitems == strlen(value)
623750027b5bSmrg			    && memcmp(value, prop, nitems) == 0) {
623850027b5bSmrg			    changed = False;
623950027b5bSmrg			}
624050027b5bSmrg			XFree(prop);
6241cd3331d0Smrg		    }
6242cd3331d0Smrg		}
6243d4fba8b9Smrg#endif /* OPT_SAME_NAME */
6244d4fba8b9Smrg		if (changed) {
6245d4fba8b9Smrg		    ReportIcons(("...updating %s\n", propname));
6246d4fba8b9Smrg		    ReportIcons(("...value is %s\n", value));
6247d4fba8b9Smrg		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
6248d4fba8b9Smrg				    XA_UTF8_STRING(dpy), 8,
6249d4fba8b9Smrg				    PropModeReplace,
6250d4fba8b9Smrg				    (Char *) value,
6251d4fba8b9Smrg				    (int) strlen(value));
6252d4fba8b9Smrg		}
6253d4fba8b9Smrg	    } else {
6254d4fba8b9Smrg		ReportIcons(("...deleting %s\n", propname));
6255d4fba8b9Smrg		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
6256d522f475Smrg	    }
6257d522f475Smrg	}
6258d522f475Smrg    }
6259d4fba8b9Smrg#endif
6260d4fba8b9Smrg    if (value != old_value) {
62613367019cSmrg	free(value);
62623367019cSmrg    }
62633367019cSmrg    free(my_attr);
62643367019cSmrg
6265cd3331d0Smrg    return;
6266d522f475Smrg}
6267d522f475Smrg
6268d522f475Smrgvoid
6269b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
6270d522f475Smrg{
62713367019cSmrg    if (!showZIconBeep(xw, name))
6272b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
6273d522f475Smrg}
6274d522f475Smrg
6275d522f475Smrgvoid
6276b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
6277d522f475Smrg{
6278b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
6279d522f475Smrg}
6280d522f475Smrg
6281712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
6282d522f475Smrg
6283d522f475Smrgvoid
6284d522f475SmrgChangeXprop(char *buf)
6285d522f475Smrg{
6286d522f475Smrg    Display *dpy = XtDisplay(toplevel);
6287d522f475Smrg    Window w = XtWindow(toplevel);
6288d522f475Smrg    XTextProperty text_prop;
6289d522f475Smrg    Atom aprop;
6290d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
6291d522f475Smrg
6292d522f475Smrg    if (pchEndPropName)
6293d522f475Smrg	*pchEndPropName = '\0';
6294980988aeSmrg    aprop = CachedInternAtom(dpy, buf);
6295d522f475Smrg    if (pchEndPropName == NULL) {
6296d522f475Smrg	/* no "=value" given, so delete the property */
6297d522f475Smrg	XDeleteProperty(dpy, w, aprop);
6298d522f475Smrg    } else {
6299d522f475Smrg	text_prop.value = pchEndPropName + 1;
6300d522f475Smrg	text_prop.encoding = XA_STRING;
6301d522f475Smrg	text_prop.format = 8;
6302d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
6303d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
6304d522f475Smrg    }
6305d522f475Smrg}
6306d522f475Smrg
6307d522f475Smrg/***====================================================================***/
6308d522f475Smrg
6309d522f475Smrg/*
6310d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
6311d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
6312d522f475Smrg */
6313d522f475Smrgvoid
63149a64e1c5SmrgReverseOldColors(XtermWidget xw)
6315d522f475Smrg{
63169a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
6317d522f475Smrg    Pixel tmpPix;
6318d522f475Smrg    char *tmpName;
6319d522f475Smrg
6320d522f475Smrg    if (pOld) {
6321d4fba8b9Smrg	/* change text cursor, if necessary */
6322d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
6323d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
6324d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
63259a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
6326d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
6327d522f475Smrg	    }
6328d522f475Smrg	    if (pOld->names[TEXT_BG]) {
6329190d7dceSmrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != NULL) {
6330d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
6331d522f475Smrg		}
6332d522f475Smrg	    }
6333d522f475Smrg	}
6334d522f475Smrg
6335d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
6336d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
6337d522f475Smrg
6338d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
6339d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
6340d522f475Smrg
6341d522f475Smrg#if OPT_TEK4014
6342d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
6343d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
6344d522f475Smrg#endif
6345d4fba8b9Smrg	FreeMarkGCs(xw);
6346d522f475Smrg    }
6347d522f475Smrg    return;
6348d522f475Smrg}
6349d522f475Smrg
6350d522f475SmrgBool
6351d522f475SmrgAllocateTermColor(XtermWidget xw,
6352d522f475Smrg		  ScrnColors * pNew,
6353d522f475Smrg		  int ndx,
6354cd3331d0Smrg		  const char *name,
6355cd3331d0Smrg		  Bool always)
6356d522f475Smrg{
6357cd3331d0Smrg    Bool result = False;
6358d522f475Smrg
6359cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
6360cd3331d0Smrg	XColor def;
6361cd3331d0Smrg	char *newName;
6362cd3331d0Smrg
6363712a7ff4Smrg	result = True;
6364712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
6365712a7ff4Smrg	    def.pixel = xw->old_foreground;
6366712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
6367712a7ff4Smrg	    def.pixel = xw->old_background;
63683367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
6369712a7ff4Smrg	    result = False;
6370712a7ff4Smrg	}
6371712a7ff4Smrg
6372712a7ff4Smrg	if (result
6373190d7dceSmrg	    && (newName = x_strdup(name)) != NULL) {
6374712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
6375cd3331d0Smrg		free(pNew->names[ndx]);
6376712a7ff4Smrg	    }
6377cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
6378cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
6379712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
6380712a7ff4Smrg		   ndx, newName, def.pixel));
6381cd3331d0Smrg	} else {
6382cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
6383712a7ff4Smrg	    result = False;
6384cd3331d0Smrg	}
6385cd3331d0Smrg    }
6386cd3331d0Smrg    return result;
6387d522f475Smrg}
6388d522f475Smrg/***====================================================================***/
6389d522f475Smrg
6390d522f475Smrg/* ARGSUSED */
6391d522f475Smrgvoid
6392cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
6393d522f475Smrg{
63943367019cSmrg    if_DEBUG({
63953367019cSmrg	xtermWarning(s, a);
63963367019cSmrg    });
6397d522f475Smrg}
6398d522f475Smrg
6399d522f475Smrgconst char *
6400d522f475SmrgSysErrorMsg(int code)
6401d522f475Smrg{
640294644356Smrg    static const char unknown[] = "unknown error";
6403d4fba8b9Smrg    const char *s = strerror(code);
6404d522f475Smrg    return s ? s : unknown;
6405d522f475Smrg}
6406d522f475Smrg
6407d522f475Smrgconst char *
6408d522f475SmrgSysReasonMsg(int code)
6409d522f475Smrg{
6410d522f475Smrg    /* *INDENT-OFF* */
6411d522f475Smrg    static const struct {
6412d522f475Smrg	int code;
6413d522f475Smrg	const char *name;
6414d522f475Smrg    } table[] = {
6415d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
6416d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
6417d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
6418d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
6419d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
6420d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
6421d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
6422d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
6423d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
6424d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
6425d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
6426d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
6427d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
6428d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
6429d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
6430d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
6431d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
6432d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
6433d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
6434d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
6435d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6436d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
6437d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
6438d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6439d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6440d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6441d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
6442d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6443d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6444d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
6445d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6446d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6447d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6448d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
6449d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6450d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6451d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6452d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6453d522f475Smrg    };
6454d522f475Smrg    /* *INDENT-ON* */
6455d522f475Smrg
6456d522f475Smrg    Cardinal n;
6457d522f475Smrg    const char *result = "?";
6458d522f475Smrg
6459d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
6460d522f475Smrg	if (code == table[n].code) {
6461d522f475Smrg	    result = table[n].name;
6462d522f475Smrg	    break;
6463d522f475Smrg	}
6464d522f475Smrg    }
6465d522f475Smrg    return result;
6466d522f475Smrg}
6467d522f475Smrg
6468d522f475Smrgvoid
6469d522f475SmrgSysError(int code)
6470d522f475Smrg{
6471d522f475Smrg    int oerrno = errno;
6472d522f475Smrg
6473c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6474d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6475d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6476d522f475Smrg
6477d522f475Smrg    Cleanup(code);
6478d522f475Smrg}
6479d522f475Smrg
6480d522f475Smrgvoid
64813367019cSmrgNormalExit(void)
6482d522f475Smrg{
6483d522f475Smrg    static Bool cleaning;
6484d522f475Smrg
6485d522f475Smrg    /*
6486d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
6487d522f475Smrg     */
64883367019cSmrg    if (cleaning) {
64893367019cSmrg	hold_screen = 0;
64903367019cSmrg	return;
64913367019cSmrg    }
6492d522f475Smrg
64933367019cSmrg    cleaning = True;
64943367019cSmrg    need_cleanup = False;
6495d522f475Smrg
64963367019cSmrg    if (hold_screen) {
64973367019cSmrg	hold_screen = 2;
64983367019cSmrg	while (hold_screen) {
6499d4fba8b9Smrg	    xtermFlushDbe(term);
6500d4fba8b9Smrg	    xevents(term);
6501d4fba8b9Smrg	    Sleep(EVENT_DELAY);
6502d522f475Smrg	}
65033367019cSmrg    }
6504d522f475Smrg#if OPT_SESSION_MGT
65053367019cSmrg    if (resource.sessionMgt) {
65063367019cSmrg	XtVaSetValues(toplevel,
65073367019cSmrg		      XtNjoinSession, False,
65083367019cSmrg		      (void *) 0);
6509d522f475Smrg    }
65103367019cSmrg#endif
65113367019cSmrg    Cleanup(0);
65123367019cSmrg}
65133367019cSmrg
6514d4fba8b9Smrg#if USE_DOUBLE_BUFFER
6515d4fba8b9Smrgvoid
6516d4fba8b9SmrgxtermFlushDbe(XtermWidget xw)
6517d4fba8b9Smrg{
6518d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
6519d4fba8b9Smrg    if (resource.buffered && screen->needSwap) {
6520d4fba8b9Smrg	XdbeSwapInfo swap;
6521d4fba8b9Smrg	swap.swap_window = VWindow(screen);
6522d4fba8b9Smrg	swap.swap_action = XdbeCopied;
6523d4fba8b9Smrg	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6524d4fba8b9Smrg	XFlush(XtDisplay(xw));
6525d4fba8b9Smrg	screen->needSwap = 0;
6526d4fba8b9Smrg	ScrollBarDrawThumb(xw, 2);
6527d4fba8b9Smrg	X_GETTIMEOFDAY(&screen->buffered_at);
6528d4fba8b9Smrg    }
6529d4fba8b9Smrg}
6530d4fba8b9Smrg
6531d4fba8b9Smrgvoid
6532d4fba8b9SmrgxtermTimedDbe(XtermWidget xw)
6533d4fba8b9Smrg{
6534d4fba8b9Smrg    if (resource.buffered) {
6535d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
6536d4fba8b9Smrg	struct timeval now;
6537d4fba8b9Smrg	long elapsed;
6538d4fba8b9Smrg	long limit = DbeMsecs(xw);
6539d4fba8b9Smrg
6540d4fba8b9Smrg	X_GETTIMEOFDAY(&now);
6541d4fba8b9Smrg	if (screen->buffered_at.tv_sec) {
6542d4fba8b9Smrg	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6543d4fba8b9Smrg		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6544d4fba8b9Smrg	} else {
6545d4fba8b9Smrg	    elapsed = limit;
6546d4fba8b9Smrg	}
6547d4fba8b9Smrg	if (elapsed >= limit) {
6548d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
6549d4fba8b9Smrg	    xtermFlushDbe(xw);
6550d4fba8b9Smrg	}
6551d4fba8b9Smrg    }
6552d4fba8b9Smrg}
6553d4fba8b9Smrg#endif
6554d4fba8b9Smrg
65553367019cSmrg/*
65563367019cSmrg * cleanup by sending SIGHUP to client processes
65573367019cSmrg */
65583367019cSmrgvoid
65593367019cSmrgCleanup(int code)
65603367019cSmrg{
65613367019cSmrg    TScreen *screen = TScreenOf(term);
65623367019cSmrg
65633367019cSmrg    TRACE(("Cleanup %d\n", code));
6564d522f475Smrg
6565d522f475Smrg    if (screen->pid > 1) {
6566d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
6567d522f475Smrg    }
6568d522f475Smrg    Exit(code);
6569d522f475Smrg}
6570d522f475Smrg
6571fa3f02f3Smrg#ifndef S_IXOTH
6572fa3f02f3Smrg#define S_IXOTH 1
6573fa3f02f3Smrg#endif
6574fa3f02f3Smrg
6575fa3f02f3SmrgBoolean
6576fa3f02f3SmrgvalidProgram(const char *pathname)
6577fa3f02f3Smrg{
6578fa3f02f3Smrg    Boolean result = False;
6579fa3f02f3Smrg    struct stat sb;
6580fa3f02f3Smrg
6581fa3f02f3Smrg    if (!IsEmpty(pathname)
6582fa3f02f3Smrg	&& *pathname == '/'
6583190d7dceSmrg	&& strstr(pathname, "/..") == NULL
6584fa3f02f3Smrg	&& stat(pathname, &sb) == 0
6585fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
6586fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
6587fa3f02f3Smrg	result = True;
6588fa3f02f3Smrg    }
6589fa3f02f3Smrg    return result;
6590fa3f02f3Smrg}
6591fa3f02f3Smrg
65923367019cSmrg#ifndef PATH_MAX
65933367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
65943367019cSmrg#endif
6595d522f475Smrgchar *
6596d522f475SmrgxtermFindShell(char *leaf, Bool warning)
6597d522f475Smrg{
65983367019cSmrg    char *s0;
6599d522f475Smrg    char *s;
6600d522f475Smrg    char *d;
6601d522f475Smrg    char *tmp;
6602d522f475Smrg    char *result = leaf;
66033367019cSmrg    Bool allocated = False;
6604d522f475Smrg
6605d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
66063367019cSmrg
66073367019cSmrg    if (!strncmp("./", result, (size_t) 2)
66083367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
66093367019cSmrg	size_t need = PATH_MAX;
66103367019cSmrg	size_t used = strlen(result) + 2;
66113367019cSmrg	char *buffer = malloc(used + need);
6612190d7dceSmrg	if (buffer != NULL) {
6613190d7dceSmrg	    if (getcwd(buffer, need) != NULL) {
66143367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
66153367019cSmrg		result = buffer;
66163367019cSmrg		allocated = True;
66173367019cSmrg	    } else {
66183367019cSmrg		free(buffer);
66193367019cSmrg	    }
66203367019cSmrg	}
6621190d7dceSmrg    } else if (*result != '\0' && strchr("+/-", *result) == NULL) {
6622d522f475Smrg	/* find it in $PATH */
6623190d7dceSmrg	if ((s = s0 = x_getenv("PATH")) != NULL) {
6624190d7dceSmrg	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != NULL) {
6625d522f475Smrg		Bool found = False;
6626d522f475Smrg		while (*s != '\0') {
6627d522f475Smrg		    strcpy(tmp, s);
6628d522f475Smrg		    for (d = tmp;; ++d) {
6629d522f475Smrg			if (*d == ':' || *d == '\0') {
6630d522f475Smrg			    int skip = (*d != '\0');
6631d522f475Smrg			    *d = '/';
6632d522f475Smrg			    strcpy(d + 1, leaf);
6633d522f475Smrg			    if (skip)
6634d522f475Smrg				++d;
6635d522f475Smrg			    s += (d - tmp);
6636fa3f02f3Smrg			    if (validProgram(tmp)) {
6637d522f475Smrg				result = x_strdup(tmp);
6638d522f475Smrg				found = True;
66393367019cSmrg				allocated = True;
6640d522f475Smrg			    }
6641d522f475Smrg			    break;
6642d522f475Smrg			}
6643d522f475Smrg		    }
6644d522f475Smrg		    if (found)
6645d522f475Smrg			break;
6646d522f475Smrg		}
6647d522f475Smrg		free(tmp);
6648d522f475Smrg	    }
66493367019cSmrg	    free(s0);
6650d522f475Smrg	}
6651d522f475Smrg    }
6652d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
6653fa3f02f3Smrg    if (!validProgram(result)) {
6654d522f475Smrg	if (warning)
66553367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
66563367019cSmrg	if (allocated)
66573367019cSmrg	    free(result);
6658190d7dceSmrg	result = NULL;
6659d522f475Smrg    }
66603367019cSmrg    /* be consistent, so that caller can always free the result */
6661190d7dceSmrg    if (result != NULL && !allocated)
66623367019cSmrg	result = x_strdup(result);
6663d522f475Smrg    return result;
6664d522f475Smrg}
6665d522f475Smrg
66660d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6667d522f475Smrg
66683367019cSmrg/*
66693367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
66703367019cSmrg * This could happen on some older machines due to the uneven standardization
66713367019cSmrg * process for the two functions.
66723367019cSmrg *
66733367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
66743367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
66753367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
66763367019cSmrg * could copy environ.
66773367019cSmrg */
66783367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
66793367019cSmrg#undef HAVE_PUTENV
66803367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
66813367019cSmrg#undef HAVE_UNSETENV
66823367019cSmrg#endif
66833367019cSmrg
6684d522f475Smrg/*
6685d522f475Smrg * copy the environment before Setenv'ing.
6686d522f475Smrg */
6687d522f475Smrgvoid
6688d522f475SmrgxtermCopyEnv(char **oldenv)
6689d522f475Smrg{
66903367019cSmrg#ifdef HAVE_PUTENV
66913367019cSmrg    (void) oldenv;
66923367019cSmrg#else
6693d522f475Smrg    unsigned size;
6694d522f475Smrg    char **newenv;
6695d522f475Smrg
6696d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
6697d522f475Smrg	;
6698d522f475Smrg    }
6699d522f475Smrg
6700d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
6701d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
6702d522f475Smrg    environ = newenv;
67033367019cSmrg#endif
67043367019cSmrg}
67053367019cSmrg
67063367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
67073367019cSmrgstatic int
67083367019cSmrgfindEnv(const char *var, int *lengthp)
67093367019cSmrg{
67103367019cSmrg    char *test;
67113367019cSmrg    int envindex = 0;
67123367019cSmrg    size_t len = strlen(var);
67133367019cSmrg    int found = -1;
67143367019cSmrg
67153367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
67163367019cSmrg
67173367019cSmrg    while ((test = environ[envindex]) != NULL) {
67183367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
67193367019cSmrg	    found = envindex;
67203367019cSmrg	    break;
67213367019cSmrg	}
67223367019cSmrg	envindex++;
67233367019cSmrg    }
67243367019cSmrg    *lengthp = envindex;
67253367019cSmrg    return found;
6726d522f475Smrg}
67273367019cSmrg#endif
6728d522f475Smrg
6729d522f475Smrg/*
6730d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6731d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
6732d522f475Smrg * This procedure assumes the memory for the first level of environ
6733d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
6734d522f475Smrg * to have to do a realloc().
6735d522f475Smrg */
6736d522f475Smrgvoid
6737cd3331d0SmrgxtermSetenv(const char *var, const char *value)
6738d522f475Smrg{
6739190d7dceSmrg    if (value != NULL) {
67403367019cSmrg#ifdef HAVE_PUTENV
67413367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
67423367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
67433367019cSmrg	if (both) {
67443367019cSmrg	    sprintf(both, "%s=%s", var, value);
67453367019cSmrg	    putenv(both);
67463367019cSmrg	}
67473367019cSmrg#else
6748d522f475Smrg	size_t len = strlen(var);
67493367019cSmrg	int envindex;
67503367019cSmrg	int found = findEnv(var, &envindex);
6751d522f475Smrg
6752d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6753d522f475Smrg
6754d522f475Smrg	if (found < 0) {
6755d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
6756d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
6757d522f475Smrg
6758d522f475Smrg	    if (need > have) {
6759d522f475Smrg		char **newenv;
6760d522f475Smrg		newenv = TypeMallocN(char *, need);
6761d522f475Smrg		if (newenv == 0) {
67623367019cSmrg		    xtermWarning("Cannot increase environment\n");
6763d522f475Smrg		    return;
6764d522f475Smrg		}
6765d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
6766d522f475Smrg		free(environ);
6767d522f475Smrg		environ = newenv;
6768d522f475Smrg	    }
6769d522f475Smrg
6770d522f475Smrg	    found = envindex;
6771d522f475Smrg	    environ[found + 1] = NULL;
6772d522f475Smrg	}
6773d522f475Smrg
6774d4fba8b9Smrg	environ[found] = malloc(2 + len + strlen(value));
6775d522f475Smrg	if (environ[found] == 0) {
67763367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
6777d522f475Smrg	    return;
6778d522f475Smrg	}
6779d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
67803367019cSmrg#endif
6781d522f475Smrg    }
6782d522f475Smrg}
6783d522f475Smrg
67843367019cSmrgvoid
67853367019cSmrgxtermUnsetenv(const char *var)
67863367019cSmrg{
67873367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
67883367019cSmrg#ifdef HAVE_UNSETENV
67893367019cSmrg    unsetenv(var);
67903367019cSmrg#else
67913367019cSmrg    {
67923367019cSmrg	int ignore;
67933367019cSmrg	int item = findEnv(var, &ignore);
67943367019cSmrg	if (item >= 0) {
67953367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
67963367019cSmrg		++item;
67973367019cSmrg	    }
67983367019cSmrg	}
67993367019cSmrg    }
68003367019cSmrg#endif
68013367019cSmrg}
68023367019cSmrg
6803d522f475Smrg/*ARGSUSED*/
6804d522f475Smrgint
68059a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
6806d522f475Smrg{
68073367019cSmrg    xtermWarning("warning, error event received:\n");
6808d4fba8b9Smrg    TRACE_X_ERR(d, ev);
6809d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6810d522f475Smrg    Exit(ERROR_XERROR);
6811d522f475Smrg    return 0;			/* appease the compiler */
6812d522f475Smrg}
6813d522f475Smrg
6814712a7ff4Smrgvoid
6815712a7ff4Smrgice_error(IceConn iceConn)
6816712a7ff4Smrg{
6817712a7ff4Smrg    (void) iceConn;
6818712a7ff4Smrg
68193367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
68203367019cSmrg		 (long) getpid(), errno);
6821712a7ff4Smrg
6822712a7ff4Smrg    Exit(ERROR_ICEERROR);
6823712a7ff4Smrg}
6824712a7ff4Smrg
6825d522f475Smrg/*ARGSUSED*/
6826d522f475Smrgint
6827fa3f02f3Smrgxioerror(Display *dpy)
6828d522f475Smrg{
6829d522f475Smrg    int the_error = errno;
6830d522f475Smrg
68313367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
68323367019cSmrg		 the_error, SysErrorMsg(the_error),
68333367019cSmrg		 DisplayString(dpy));
6834d522f475Smrg
6835d522f475Smrg    Exit(ERROR_XIOERROR);
6836d522f475Smrg    return 0;			/* appease the compiler */
6837d522f475Smrg}
6838d522f475Smrg
6839728ff447Schristosvoid
6840d522f475Smrgxt_error(String message)
6841d522f475Smrg{
68423367019cSmrg    xtermWarning("Xt error: %s\n", message);
6843d522f475Smrg
6844d522f475Smrg    /*
6845d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
6846d522f475Smrg     */
6847190d7dceSmrg    if (x_getenv("DISPLAY") == NULL) {
68483367019cSmrg	xtermWarning("DISPLAY is not set\n");
6849d522f475Smrg    }
6850980988aeSmrg    exit(ERROR_MISC);
6851d522f475Smrg}
6852d522f475Smrg
6853d522f475Smrgint
6854d522f475SmrgXStrCmp(char *s1, char *s2)
6855d522f475Smrg{
6856d522f475Smrg    if (s1 && s2)
6857d522f475Smrg	return (strcmp(s1, s2));
6858d522f475Smrg    if (s1 && *s1)
6859d522f475Smrg	return (1);
6860d522f475Smrg    if (s2 && *s2)
6861d522f475Smrg	return (-1);
6862d522f475Smrg    return (0);
6863d522f475Smrg}
6864d522f475Smrg
6865d522f475Smrg#if OPT_TEK4014
6866d522f475Smrgstatic void
6867fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
6868d522f475Smrg{
6869d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
6870d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
6871d522f475Smrg    XWithdrawWindow(dpy, w, scr);
6872d522f475Smrg    return;
6873d522f475Smrg}
6874d522f475Smrg#endif
6875d522f475Smrg
6876d522f475Smrgvoid
6877d522f475Smrgset_vt_visibility(Bool on)
6878d522f475Smrg{
6879c219fbebSmrg    XtermWidget xw = term;
6880c219fbebSmrg    TScreen *screen = TScreenOf(xw);
6881d522f475Smrg
6882d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
6883190d7dceSmrg
6884d522f475Smrg    if (on) {
6885c219fbebSmrg	if (!screen->Vshow && xw) {
6886190d7dceSmrg	    resource.notMapped = False;
6887c219fbebSmrg	    VTInit(xw);
6888c219fbebSmrg	    XtMapWidget(XtParent(xw));
6889d522f475Smrg#if OPT_TOOLBAR
6890d522f475Smrg	    /* we need both of these during initialization */
6891c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
6892d522f475Smrg	    ShowToolbar(resource.toolBar);
6893d522f475Smrg#endif
6894d522f475Smrg	    screen->Vshow = True;
6895d522f475Smrg	}
6896d522f475Smrg    }
6897d522f475Smrg#if OPT_TEK4014
6898d522f475Smrg    else {
6899c219fbebSmrg	if (screen->Vshow && xw) {
6900c219fbebSmrg	    withdraw_window(XtDisplay(xw),
6901c219fbebSmrg			    VShellWindow(xw),
6902c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
6903d522f475Smrg	    screen->Vshow = False;
6904d522f475Smrg	}
6905d522f475Smrg    }
6906d522f475Smrg    set_vthide_sensitivity();
6907d522f475Smrg    set_tekhide_sensitivity();
6908d522f475Smrg    update_vttekmode();
6909d522f475Smrg    update_tekshow();
6910d522f475Smrg    update_vtshow();
6911d522f475Smrg#endif
6912d522f475Smrg    return;
6913d522f475Smrg}
6914d522f475Smrg
6915d522f475Smrg#if OPT_TEK4014
6916d522f475Smrgvoid
6917d522f475Smrgset_tek_visibility(Bool on)
6918d522f475Smrg{
6919d4fba8b9Smrg    XtermWidget xw = term;
6920d4fba8b9Smrg
6921d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
6922d522f475Smrg
6923d522f475Smrg    if (on) {
6924d4fba8b9Smrg	if (!TEK4014_SHOWN(xw)) {
6925190d7dceSmrg	    if (tekWidget == NULL) {
6926cd3331d0Smrg		TekInit();	/* will exit on failure */
6927cd3331d0Smrg	    }
6928190d7dceSmrg	    if (tekWidget != NULL) {
6929cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
6930190d7dceSmrg		resource.notMapped = False;
6931cd3331d0Smrg		XtRealizeWidget(tekParent);
6932cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
6933d522f475Smrg#if OPT_TOOLBAR
6934cd3331d0Smrg		/* we need both of these during initialization */
6935cd3331d0Smrg		XtMapWidget(tekParent);
6936cd3331d0Smrg		XtMapWidget(tekWidget);
6937d522f475Smrg#endif
6938cd3331d0Smrg		XtOverrideTranslations(tekParent,
6939cd3331d0Smrg				       XtParseTranslationTable
6940cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6941cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
6942cd3331d0Smrg				       XtWindow(tekParent),
6943cd3331d0Smrg				       &wm_delete_window, 1);
6944d4fba8b9Smrg		TEK4014_SHOWN(xw) = True;
6945cd3331d0Smrg	    }
6946d522f475Smrg	}
6947d522f475Smrg    } else {
6948d4fba8b9Smrg	if (TEK4014_SHOWN(xw) && tekWidget) {
6949d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
6950d522f475Smrg			    TShellWindow,
6951d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6952d4fba8b9Smrg	    TEK4014_SHOWN(xw) = False;
6953d522f475Smrg	}
6954d522f475Smrg    }
6955d522f475Smrg    set_tekhide_sensitivity();
6956d522f475Smrg    set_vthide_sensitivity();
6957d522f475Smrg    update_vtshow();
6958d522f475Smrg    update_tekshow();
6959d522f475Smrg    update_vttekmode();
6960d522f475Smrg    return;
6961d522f475Smrg}
6962d522f475Smrg
6963d522f475Smrgvoid
6964d522f475Smrgend_tek_mode(void)
6965d522f475Smrg{
6966cd3331d0Smrg    XtermWidget xw = term;
6967cd3331d0Smrg
6968cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
6969cd3331d0Smrg	FlushLog(xw);
6970dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
6971dfb07bc7Smrg	xtermSetWinSize(xw);
6972d522f475Smrg	longjmp(Tekend, 1);
6973d522f475Smrg    }
6974d522f475Smrg    return;
6975d522f475Smrg}
6976d522f475Smrg
6977d522f475Smrgvoid
6978d522f475Smrgend_vt_mode(void)
6979d522f475Smrg{
6980cd3331d0Smrg    XtermWidget xw = term;
6981cd3331d0Smrg
6982cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
6983cd3331d0Smrg	FlushLog(xw);
6984d4fba8b9Smrg	set_tek_visibility(True);
6985cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
6986dfb07bc7Smrg	TekSetWinSize(tekWidget);
6987d522f475Smrg	longjmp(VTend, 1);
6988d522f475Smrg    }
6989d522f475Smrg    return;
6990d522f475Smrg}
6991d522f475Smrg
6992d522f475Smrgvoid
6993d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
6994d522f475Smrg{
6995d522f475Smrg    if (tovt) {
6996d522f475Smrg	if (tekRefreshList)
6997d522f475Smrg	    TekRefresh(tekWidget);
6998d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
6999d522f475Smrg    } else {
7000d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
7001d522f475Smrg    }
7002d522f475Smrg}
7003d522f475Smrg
7004d522f475Smrgvoid
7005d522f475Smrghide_vt_window(void)
7006d522f475Smrg{
7007d522f475Smrg    set_vt_visibility(False);
7008d522f475Smrg    if (!TEK4014_ACTIVE(term))
7009d522f475Smrg	switch_modes(False);	/* switch to tek mode */
7010d522f475Smrg}
7011d522f475Smrg
7012d522f475Smrgvoid
7013d522f475Smrghide_tek_window(void)
7014d522f475Smrg{
7015d522f475Smrg    set_tek_visibility(False);
7016d522f475Smrg    tekRefreshList = (TekLink *) 0;
7017d522f475Smrg    if (TEK4014_ACTIVE(term))
7018d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
7019d522f475Smrg}
7020d522f475Smrg#endif /* OPT_TEK4014 */
7021d522f475Smrg
7022d522f475Smrgstatic const char *
7023d522f475Smrgskip_punct(const char *s)
7024d522f475Smrg{
7025d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
7026d522f475Smrg	++s;
7027d522f475Smrg    }
7028d522f475Smrg    return s;
7029d522f475Smrg}
7030d522f475Smrg
7031d522f475Smrgstatic int
7032d522f475Smrgcmp_options(const void *a, const void *b)
7033d522f475Smrg{
7034d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
7035d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
7036d522f475Smrg    return strcmp(s1, s2);
7037d522f475Smrg}
7038d522f475Smrg
7039d522f475Smrgstatic int
7040d522f475Smrgcmp_resources(const void *a, const void *b)
7041d522f475Smrg{
7042d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
7043d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
7044d522f475Smrg}
7045d522f475Smrg
7046d522f475SmrgXrmOptionDescRec *
7047190d7dceSmrgsortedOptDescs(const XrmOptionDescRec * descs, Cardinal res_count)
7048d522f475Smrg{
7049190d7dceSmrg    static XrmOptionDescRec *res_array = NULL;
7050d522f475Smrg
7051d522f475Smrg#ifdef NO_LEAKS
7052190d7dceSmrg    if (descs == NULL) {
7053d4fba8b9Smrg	FreeAndNull(res_array);
7054d522f475Smrg    } else
7055d522f475Smrg#endif
7056190d7dceSmrg    if (res_array == NULL) {
7057d522f475Smrg	Cardinal j;
7058d522f475Smrg
7059d522f475Smrg	/* make a sorted index to 'resources' */
7060d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
7061190d7dceSmrg	if (res_array != NULL) {
7062cd3331d0Smrg	    for (j = 0; j < res_count; j++)
7063cd3331d0Smrg		res_array[j] = descs[j];
7064cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
7065cd3331d0Smrg	}
7066d522f475Smrg    }
7067d522f475Smrg    return res_array;
7068d522f475Smrg}
7069d522f475Smrg
7070d522f475Smrg/*
7071d522f475Smrg * The first time this is called, construct sorted index to the main program's
7072d522f475Smrg * list of options, taking into account the on/off options which will be
7073d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
7074d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
7075d522f475Smrg */
7076d522f475SmrgOptionHelp *
7077d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
7078d522f475Smrg{
7079190d7dceSmrg    static OptionHelp *opt_array = NULL;
7080d522f475Smrg
7081d522f475Smrg#ifdef NO_LEAKS
7082190d7dceSmrg    if (descs == NULL && opt_array != NULL) {
7083d522f475Smrg	sortedOptDescs(descs, numDescs);
7084d4fba8b9Smrg	FreeAndNull(opt_array);
7085190d7dceSmrg	return NULL;
7086190d7dceSmrg    } else if (options == NULL || descs == NULL) {
7087190d7dceSmrg	return NULL;
7088d522f475Smrg    }
7089d522f475Smrg#endif
7090d522f475Smrg
7091190d7dceSmrg    if (opt_array == NULL) {
7092cd3331d0Smrg	size_t opt_count, j;
7093d522f475Smrg#if OPT_TRACE
7094d522f475Smrg	Cardinal k;
7095d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
7096d522f475Smrg	int code;
7097cd3331d0Smrg	const char *mesg;
7098d522f475Smrg#else
7099d522f475Smrg	(void) descs;
7100d522f475Smrg	(void) numDescs;
7101d522f475Smrg#endif
7102d522f475Smrg
7103d522f475Smrg	/* count 'options' and make a sorted index to it */
7104190d7dceSmrg	for (opt_count = 0; options[opt_count].opt != NULL; ++opt_count) {
7105d522f475Smrg	    ;
7106d522f475Smrg	}
7107d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
7108d522f475Smrg	for (j = 0; j < opt_count; j++)
7109d522f475Smrg	    opt_array[j] = options[j];
7110d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
7111d522f475Smrg
7112d522f475Smrg	/* supply the "turn on/off" strings if needed */
7113d522f475Smrg#if OPT_TRACE
7114d522f475Smrg	for (j = 0; j < opt_count; j++) {
7115712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
7116c219fbebSmrg		char temp[80];
7117cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
7118d522f475Smrg		for (k = 0; k < numDescs; ++k) {
7119cd3331d0Smrg		    const char *value = res_array[k].value;
7120d522f475Smrg		    if (res_array[k].option[0] == '-') {
7121d522f475Smrg			code = -1;
7122d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
7123d522f475Smrg			code = 1;
7124d522f475Smrg		    } else {
7125d522f475Smrg			code = 0;
7126d522f475Smrg		    }
71273367019cSmrg		    sprintf(temp, "%.*s",
71283367019cSmrg			    (int) sizeof(temp) - 2,
71293367019cSmrg			    opt_array[j].desc);
7130190d7dceSmrg		    if (x_strindex(temp, "inhibit") != NULL)
7131d522f475Smrg			code = -code;
7132d522f475Smrg		    if (code != 0
7133190d7dceSmrg			&& res_array[k].value != NULL
7134d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
7135d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
7136d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
7137d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
7138d522f475Smrg			    mesg = "turn on/off";
7139d522f475Smrg			} else {
7140d522f475Smrg			    mesg = "turn off/on";
7141d522f475Smrg			}
7142d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
7143d522f475Smrg			       mesg,
7144d522f475Smrg			       res_array[k].option,
7145d522f475Smrg			       res_array[k].value,
7146d522f475Smrg			       opt_array[j].opt,
7147d522f475Smrg			       opt_array[j].desc));
7148d522f475Smrg			break;
7149d522f475Smrg		    }
7150d522f475Smrg		}
7151d522f475Smrg	    }
7152d522f475Smrg	}
7153d522f475Smrg#endif
7154d522f475Smrg    }
7155d522f475Smrg    return opt_array;
7156d522f475Smrg}
7157d522f475Smrg
7158d522f475Smrg/*
7159d522f475Smrg * Report the character-type locale that xterm was started in.
7160d522f475Smrg */
71613367019cSmrgString
7162d522f475SmrgxtermEnvLocale(void)
7163d522f475Smrg{
71643367019cSmrg    static String result;
7165d522f475Smrg
7166190d7dceSmrg    if (result == NULL) {
7167190d7dceSmrg	if ((result = x_nonempty(setlocale(LC_CTYPE, NULL))) == NULL) {
7168cd3331d0Smrg	    result = x_strdup("C");
7169cd3331d0Smrg	} else {
7170cd3331d0Smrg	    result = x_strdup(result);
7171d522f475Smrg	}
7172d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
7173d522f475Smrg    }
7174d522f475Smrg    return result;
7175d522f475Smrg}
7176d522f475Smrg
7177d522f475Smrgchar *
7178d522f475SmrgxtermEnvEncoding(void)
7179d522f475Smrg{
7180d522f475Smrg    static char *result;
7181d522f475Smrg
7182190d7dceSmrg    if (result == NULL) {
7183d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
7184d522f475Smrg	result = nl_langinfo(CODESET);
7185d522f475Smrg#else
7186d4fba8b9Smrg	const char *locale = xtermEnvLocale();
7187d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
7188d4fba8b9Smrg	    result = x_strdup("ASCII");
7189d522f475Smrg	} else {
7190d4fba8b9Smrg	    result = x_strdup("ISO-8859-1");
7191d522f475Smrg	}
7192d522f475Smrg#endif
7193d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
7194d522f475Smrg    }
7195d522f475Smrg    return result;
7196d522f475Smrg}
7197d522f475Smrg
7198d522f475Smrg#if OPT_WIDE_CHARS
7199d522f475Smrg/*
7200d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
7201d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
7202d522f475Smrg * various library calls.
7203d522f475Smrg */
7204d522f475SmrgBool
7205d522f475SmrgxtermEnvUTF8(void)
7206d522f475Smrg{
7207d522f475Smrg    static Bool init = False;
7208d522f475Smrg    static Bool result = False;
7209d522f475Smrg
7210d522f475Smrg    if (!init) {
7211d522f475Smrg	init = True;
7212d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
7213d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
7214d522f475Smrg#else
7215fa3f02f3Smrg	{
7216fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
7217fa3f02f3Smrg	    int n;
7218fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
7219fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
7220fa3f02f3Smrg	    }
7221fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
7222fa3f02f3Smrg		result = True;
7223fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
7224fa3f02f3Smrg		result = True;
7225fa3f02f3Smrg	    free(locale);
7226fa3f02f3Smrg	}
7227d522f475Smrg#endif
7228d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
7229d522f475Smrg    }
7230d522f475Smrg    return result;
7231d522f475Smrg}
7232d522f475Smrg#endif /* OPT_WIDE_CHARS */
7233d522f475Smrg
7234b7c89284Ssnj/*
7235b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
7236b7c89284Ssnj */
7237b7c89284SsnjXtermWidget
7238b7c89284SsnjgetXtermWidget(Widget w)
7239b7c89284Ssnj{
7240b7c89284Ssnj    XtermWidget xw;
7241b7c89284Ssnj
7242190d7dceSmrg    if (w == NULL) {
7243b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
7244b7c89284Ssnj	if (!IsXtermWidget(xw)) {
7245190d7dceSmrg	    xw = NULL;
7246b7c89284Ssnj	}
7247b7c89284Ssnj    } else if (IsXtermWidget(w)) {
7248b7c89284Ssnj	xw = (XtermWidget) w;
7249b7c89284Ssnj    } else {
7250b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
7251b7c89284Ssnj    }
7252b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
7253b7c89284Ssnj    return xw;
7254b7c89284Ssnj}
72553367019cSmrg
72563367019cSmrg#if OPT_SESSION_MGT
7257636d5e9fSmrg
7258636d5e9fSmrg#if OPT_TRACE
7259636d5e9fSmrgstatic void
7260636d5e9fSmrgtrace_1_SM(const char *tag, String name)
7261636d5e9fSmrg{
7262636d5e9fSmrg    Arg args[1];
7263190d7dceSmrg    char *buf = NULL;
7264636d5e9fSmrg
7265636d5e9fSmrg    XtSetArg(args[0], name, &buf);
7266636d5e9fSmrg    XtGetValues(toplevel, args, 1);
7267636d5e9fSmrg
7268636d5e9fSmrg    if (strstr(name, "Path") || strstr(name, "Directory")) {
7269636d5e9fSmrg	TRACE(("%s %s: %s\n", tag, name, NonNull(buf)));
7270636d5e9fSmrg    } else if (strstr(name, "Command")) {
7271636d5e9fSmrg	if (buf != NULL) {
7272636d5e9fSmrg	    char **vec = (char **) (void *) buf;
7273636d5e9fSmrg	    int n;
7274636d5e9fSmrg	    TRACE(("%s %s:\n", tag, name));
7275636d5e9fSmrg	    for (n = 0; vec[n] != NULL; ++n) {
7276636d5e9fSmrg		TRACE((" arg[%d] = %s\n", n, vec[n]));
7277636d5e9fSmrg	    }
7278636d5e9fSmrg	} else {
7279636d5e9fSmrg	    TRACE(("%s %s: %p\n", tag, name, buf));
7280636d5e9fSmrg	}
7281636d5e9fSmrg    } else {
7282636d5e9fSmrg	TRACE(("%s %s: %p\n", tag, name, buf));
7283636d5e9fSmrg    }
7284636d5e9fSmrg}
7285636d5e9fSmrg
7286636d5e9fSmrgstatic void
7287636d5e9fSmrgtrace_SM_props(void)
7288636d5e9fSmrg{
7289636d5e9fSmrg    /* *INDENT-OFF* */
7290636d5e9fSmrg    static struct { String app, cls; } table[] = {
7291636d5e9fSmrg	{ XtNcurrentDirectory,	XtCCurrentDirectory },
7292636d5e9fSmrg	{ XtNdieCallback,	XtNdiscardCommand },
7293636d5e9fSmrg	{ XtCDiscardCommand,	XtNenvironment },
7294636d5e9fSmrg	{ XtCEnvironment,	XtNinteractCallback },
7295636d5e9fSmrg	{ XtNjoinSession,	XtCJoinSession },
7296636d5e9fSmrg	{ XtNprogramPath,	XtCProgramPath },
7297636d5e9fSmrg	{ XtNresignCommand,	XtCResignCommand },
7298636d5e9fSmrg	{ XtNrestartCommand,	XtCRestartCommand },
7299636d5e9fSmrg	{ XtNrestartStyle,	XtCRestartStyle },
7300636d5e9fSmrg	{ XtNsaveCallback,	XtNsaveCompleteCallback },
7301636d5e9fSmrg	{ XtNsessionID,		XtCSessionID },
7302636d5e9fSmrg	{ XtNshutdownCommand,	XtCShutdownCommand },
7303636d5e9fSmrg    };
7304636d5e9fSmrg    /* *INDENT-ON* */
7305636d5e9fSmrg    Cardinal n;
7306636d5e9fSmrg    TRACE(("Session properties:\n"));
7307636d5e9fSmrg    for (n = 0; n < XtNumber(table); ++n) {
7308636d5e9fSmrg	trace_1_SM("app", table[n].app);
7309636d5e9fSmrg	trace_1_SM("cls", table[n].cls);
7310636d5e9fSmrg    }
7311636d5e9fSmrg}
7312636d5e9fSmrg#define TRACE_SM_PROPS()	trace_SM_props()
7313636d5e9fSmrg#else
7314636d5e9fSmrg#define TRACE_SM_PROPS()	/* nothing */
7315636d5e9fSmrg#endif
7316636d5e9fSmrg
73173367019cSmrgstatic void
73183367019cSmrgdie_callback(Widget w GCC_UNUSED,
73193367019cSmrg	     XtPointer client_data GCC_UNUSED,
73203367019cSmrg	     XtPointer call_data GCC_UNUSED)
73213367019cSmrg{
7322c48a5815Smrg    TRACE(("die_callback client=%p, call=%p\n",
7323c48a5815Smrg	   (void *) client_data,
7324c48a5815Smrg	   (void *) call_data));
7325636d5e9fSmrg    TRACE_SM_PROPS();
73263367019cSmrg    NormalExit();
73273367019cSmrg}
73283367019cSmrg
73293367019cSmrgstatic void
73303367019cSmrgsave_callback(Widget w GCC_UNUSED,
73313367019cSmrg	      XtPointer client_data GCC_UNUSED,
73323367019cSmrg	      XtPointer call_data)
73333367019cSmrg{
73343367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
7335636d5e9fSmrg    TRACE(("save_callback:\n"));
7336636d5e9fSmrg    TRACE(("... save_type            <-%d\n", token->save_type));
7337636d5e9fSmrg    TRACE(("... interact_style       <-%d\n", token->interact_style));
7338636d5e9fSmrg    TRACE(("... shutdown             <-%s\n", BtoS(token->shutdown)));
7339636d5e9fSmrg    TRACE(("... fast                 <-%s\n", BtoS(token->fast)));
7340636d5e9fSmrg    TRACE(("... cancel_shutdown      <-%s\n", BtoS(token->cancel_shutdown)));
7341636d5e9fSmrg    TRACE(("... phase                <-%d\n", token->phase));
7342636d5e9fSmrg    TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type));
7343636d5e9fSmrg    TRACE(("... request_cancel       ->%s\n", BtoS(token->request_cancel)));
7344636d5e9fSmrg    TRACE(("... request_next_phase   ->%s\n", BtoS(token->request_next_phase)));
7345636d5e9fSmrg    TRACE(("... save_success         ->%s\n", BtoS(token->save_success)));
7346636d5e9fSmrg    xtermUpdateRestartCommand(term);
7347636d5e9fSmrg    /* we have nothing more to save */
73483367019cSmrg    token->save_success = True;
73493367019cSmrg}
73503367019cSmrg
73513367019cSmrgstatic void
73523367019cSmrgicewatch(IceConn iceConn,
73533367019cSmrg	 IcePointer clientData GCC_UNUSED,
73543367019cSmrg	 Bool opening,
73553367019cSmrg	 IcePointer * watchData GCC_UNUSED)
73563367019cSmrg{
73573367019cSmrg    if (opening) {
73583367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
73593367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
73603367019cSmrg    } else {
73613367019cSmrg	ice_fd = -1;
73623367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
73633367019cSmrg    }
73643367019cSmrg}
73653367019cSmrg
73663367019cSmrgvoid
73673367019cSmrgxtermOpenSession(void)
73683367019cSmrg{
73693367019cSmrg    if (resource.sessionMgt) {
73703367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
73713367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
73723367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
7373636d5e9fSmrg
7374636d5e9fSmrg	TRACE_SM_PROPS();
73753367019cSmrg    }
73763367019cSmrg}
73773367019cSmrg
73783367019cSmrgvoid
73793367019cSmrgxtermCloseSession(void)
73803367019cSmrg{
73813367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
73823367019cSmrg}
7383636d5e9fSmrg
7384636d5e9fSmrgtypedef enum {
7385636d5e9fSmrg    B_ARG = 0,
7386636d5e9fSmrg    I_ARG,
7387636d5e9fSmrg    D_ARG,
7388636d5e9fSmrg    S_ARG
7389636d5e9fSmrg} ParamType;
7390636d5e9fSmrg
7391636d5e9fSmrg#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) }
7392636d5e9fSmrg#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) }
7393636d5e9fSmrg#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) }
7394636d5e9fSmrg#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) }
7395636d5e9fSmrg
7396636d5e9fSmrgtypedef struct {
7397636d5e9fSmrg    const char name[30];
7398636d5e9fSmrg    ParamType type;
7399636d5e9fSmrg    Cardinal offset;
7400636d5e9fSmrg} FontParams;
7401636d5e9fSmrg
7402636d5e9fSmrg/* *INDENT-OFF* */
7403636d5e9fSmrgstatic const FontParams fontParams[] = {
7404636d5e9fSmrg    Iarg(XtNinitialFont,     screen.menu_font_number),	/* "-fc" */
7405636d5e9fSmrg    Barg(XtNallowBoldFonts,  screen.allowBoldFonts),	/* menu */
7406636d5e9fSmrg#if OPT_BOX_CHARS
7407636d5e9fSmrg    Barg(XtNforceBoxChars,   screen.force_box_chars),	/* "-fbx" */
7408636d5e9fSmrg    Barg(XtNforcePackedFont, screen.force_packed),	/* menu */
7409636d5e9fSmrg#endif
7410636d5e9fSmrg#if OPT_DEC_CHRSET
7411636d5e9fSmrg    Barg(XtNfontDoublesize,  screen.font_doublesize),	/* menu */
7412636d5e9fSmrg#endif
7413636d5e9fSmrg#if OPT_WIDE_CHARS
7414636d5e9fSmrg    Barg(XtNutf8Fonts,       screen.utf8_fonts),	/* menu */
7415636d5e9fSmrg#endif
7416636d5e9fSmrg#if OPT_RENDERFONT
7417636d5e9fSmrg    Darg(XtNfaceSize,        misc.face_size[0]),	/* "-fs" */
7418636d5e9fSmrg    Sarg(XtNfaceName,        misc.default_xft.f_n),	/* "-fa" */
7419636d5e9fSmrg    Sarg(XtNrenderFont,      misc.render_font_s),	/* (resource) */
7420636d5e9fSmrg#endif
7421636d5e9fSmrg};
7422636d5e9fSmrg/* *INDENT-ON* */
7423636d5e9fSmrg
7424636d5e9fSmrg#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2)
7425636d5e9fSmrg#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset)
7426636d5e9fSmrg
7427636d5e9fSmrg/*
7428636d5e9fSmrg * If no widget is given, no value is used.
7429636d5e9fSmrg */
7430636d5e9fSmrgstatic char *
7431636d5e9fSmrgformatFontParam(char *result, XtermWidget xw, const FontParams * parameter)
7432636d5e9fSmrg{
7433636d5e9fSmrg    sprintf(result, "%s*%s:", ProgramName, parameter->name);
7434190d7dceSmrg    if (xw != NULL) {
7435636d5e9fSmrg	char *next = result + strlen(result);
7436636d5e9fSmrg	switch (parameter->type) {
7437636d5e9fSmrg	case B_ARG:
7438636d5e9fSmrg	    sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset)
7439636d5e9fSmrg		    ? "true"
7440636d5e9fSmrg		    : "false");
7441636d5e9fSmrg	    break;
7442636d5e9fSmrg	case I_ARG:
7443636d5e9fSmrg	    sprintf(next, "%d", TypedPtr(int));
7444636d5e9fSmrg	    break;
7445636d5e9fSmrg	case D_ARG:
7446636d5e9fSmrg	    sprintf(next, "%.1f", TypedPtr(float));
7447636d5e9fSmrg	    break;
7448636d5e9fSmrg	case S_ARG:
7449636d5e9fSmrg	    strcpy(next, TypedPtr(char *));
7450636d5e9fSmrg#if OPT_RENDERFONT
7451636d5e9fSmrg	    if (!strcmp(parameter->name, XtNfaceName)) {
7452636d5e9fSmrg		if (IsEmpty(next)
7453636d5e9fSmrg		    && xw->work.render_font) {
7454636d5e9fSmrg		    strcpy(next, DEFFACENAME_AUTO);
7455636d5e9fSmrg		}
7456636d5e9fSmrg	    } else if (!strcmp(parameter->name, XtNrenderFont)) {
7457636d5e9fSmrg		if (xw->work.render_font == erDefault
7458636d5e9fSmrg		    && IsEmpty(xw->misc.default_xft.f_n)) {
7459636d5e9fSmrg		    strcpy(next, "DefaultOff");
7460636d5e9fSmrg		}
7461636d5e9fSmrg	    }
7462636d5e9fSmrg#endif
7463636d5e9fSmrg	    break;
7464636d5e9fSmrg	}
7465636d5e9fSmrg    }
7466636d5e9fSmrg    return result;
7467636d5e9fSmrg}
7468636d5e9fSmrg
7469636d5e9fSmrg#if OPT_TRACE
7470636d5e9fSmrgstatic void
7471636d5e9fSmrgdumpFontParams(XtermWidget xw)
7472636d5e9fSmrg{
7473636d5e9fSmrg    char buffer[1024];
7474636d5e9fSmrg    Cardinal n;
7475636d5e9fSmrg
7476636d5e9fSmrg    TRACE(("FontParams:\n"));
7477636d5e9fSmrg    for (n = 0; n < XtNumber(fontParams); ++n) {
7478636d5e9fSmrg	TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n)));
7479636d5e9fSmrg    }
7480636d5e9fSmrg}
7481636d5e9fSmrg#else
7482636d5e9fSmrg#define dumpFontParams(xw)	/* nothing */
7483636d5e9fSmrg#endif
7484636d5e9fSmrg
7485636d5e9fSmrgstatic Boolean
7486636d5e9fSmrgfindFontParams(int argc, char **argv)
7487636d5e9fSmrg{
7488636d5e9fSmrg    Boolean result = False;
7489636d5e9fSmrg
7490636d5e9fSmrg    if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) {
7491636d5e9fSmrg	int n;
7492636d5e9fSmrg
7493636d5e9fSmrg	for (n = 0; n < RESTART_PARAMS; ++n) {
7494636d5e9fSmrg	    int my_index = argc - restart_params - n - 1;
7495636d5e9fSmrg	    int my_param = (RESTART_PARAMS - n - 1) / 2;
7496636d5e9fSmrg	    char *actual = argv[my_index];
7497636d5e9fSmrg	    char expect[1024];
7498636d5e9fSmrg	    Boolean value = (Boolean) ((n % 2) == 0);
7499636d5e9fSmrg
7500636d5e9fSmrg	    result = False;
7501636d5e9fSmrg	    TRACE(("...index: %d\n", my_index));
7502636d5e9fSmrg	    TRACE(("...param: %d\n", my_param));
7503636d5e9fSmrg	    TRACE(("...actual %s\n", actual));
7504636d5e9fSmrg	    if (IsEmpty(actual))
7505636d5e9fSmrg		break;
7506636d5e9fSmrg
7507636d5e9fSmrg	    if (value) {
7508190d7dceSmrg		formatFontParam(expect, NULL, fontParams + my_param);
7509636d5e9fSmrg	    } else {
7510636d5e9fSmrg		strcpy(expect, "-xrm");
7511636d5e9fSmrg	    }
7512636d5e9fSmrg
7513636d5e9fSmrg	    TRACE(("...expect %s\n", expect));
7514636d5e9fSmrg
7515636d5e9fSmrg	    if (value) {
7516636d5e9fSmrg		if (strlen(expect) >= strlen(actual))
7517636d5e9fSmrg		    break;
7518636d5e9fSmrg		if (strncmp(expect, actual, strlen(expect)))
7519636d5e9fSmrg		    break;
7520636d5e9fSmrg	    } else {
7521636d5e9fSmrg		if (strcmp(actual, expect))
7522636d5e9fSmrg		    break;
7523636d5e9fSmrg	    }
7524636d5e9fSmrg	    TRACE(("fixme/ok:%d\n", n));
7525636d5e9fSmrg	    result = True;
7526636d5e9fSmrg	}
7527636d5e9fSmrg	TRACE(("findFontParams: %s (tested %d of %d parameters)\n",
7528636d5e9fSmrg	       BtoS(result), n + 1, RESTART_PARAMS));
7529636d5e9fSmrg    }
7530636d5e9fSmrg    return result;
7531636d5e9fSmrg}
7532636d5e9fSmrg
7533636d5e9fSmrgstatic int
7534c48a5815SmrginsertFontParams(XtermWidget xw, int *targetp, Bool first)
7535636d5e9fSmrg{
7536636d5e9fSmrg    int changed = 0;
7537636d5e9fSmrg    int n;
7538636d5e9fSmrg    int target = *targetp;
7539636d5e9fSmrg    char buffer[1024];
7540636d5e9fSmrg    const char *option = "-xrm";
7541636d5e9fSmrg
7542636d5e9fSmrg    for (n = 0; n < (int) XtNumber(fontParams); ++n) {
7543636d5e9fSmrg	formatFontParam(buffer, xw, fontParams + n);
7544636d5e9fSmrg	TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer));
7545636d5e9fSmrg	if (restart_command[target] == NULL)
7546636d5e9fSmrg	    restart_command[target] = x_strdup(option);
7547636d5e9fSmrg	++target;
7548636d5e9fSmrg	if (first) {
7549636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7550636d5e9fSmrg	    ++changed;
7551636d5e9fSmrg	} else if (restart_command[target] == NULL
7552636d5e9fSmrg		   || strcmp(restart_command[target], buffer)) {
7553636d5e9fSmrg	    free(restart_command[target]);
7554636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7555636d5e9fSmrg	    ++changed;
7556636d5e9fSmrg	}
7557636d5e9fSmrg	++target;
7558636d5e9fSmrg    }
7559636d5e9fSmrg    *targetp = target;
7560636d5e9fSmrg    return changed;
7561636d5e9fSmrg}
7562636d5e9fSmrg
7563636d5e9fSmrgvoid
7564636d5e9fSmrgxtermUpdateRestartCommand(XtermWidget xw)
7565636d5e9fSmrg{
7566636d5e9fSmrg    if (resource.sessionMgt) {
7567636d5e9fSmrg	Arg args[1];
7568190d7dceSmrg	char **argv = NULL;
7569636d5e9fSmrg
7570636d5e9fSmrg	XtSetArg(args[0], XtNrestartCommand, &argv);
7571636d5e9fSmrg	XtGetValues(toplevel, args, 1);
7572636d5e9fSmrg	if (argv != NULL) {
7573636d5e9fSmrg	    static int my_params = 0;
7574636d5e9fSmrg
7575636d5e9fSmrg	    int changes = 0;
7576636d5e9fSmrg	    Boolean first = False;
7577636d5e9fSmrg	    int argc;
7578636d5e9fSmrg	    int want;
7579636d5e9fSmrg	    int source, target;
7580636d5e9fSmrg
7581636d5e9fSmrg	    TRACE(("xtermUpdateRestartCommand\n"));
7582636d5e9fSmrg	    dumpFontParams(xw);
7583636d5e9fSmrg	    for (argc = 0; argv[argc] != NULL; ++argc) {
7584636d5e9fSmrg		TRACE((" arg[%d] = %s\n", argc, argv[argc]));
7585636d5e9fSmrg		;
7586636d5e9fSmrg	    }
7587636d5e9fSmrg	    want = argc - (restart_params + RESTART_PARAMS);
7588636d5e9fSmrg
7589636d5e9fSmrg	    TRACE((" argc:           %d\n", argc));
7590636d5e9fSmrg	    TRACE((" restart_params: %d\n", restart_params));
7591636d5e9fSmrg	    TRACE((" want to insert: %d\n", want));
7592636d5e9fSmrg
7593636d5e9fSmrg	    /*
7594636d5e9fSmrg	     * If we already have the font-choice option, do not add it again.
7595636d5e9fSmrg	     */
7596636d5e9fSmrg	    if (findFontParams(argc, argv)) {
7597636d5e9fSmrg		my_params = (want);
7598636d5e9fSmrg	    } else {
7599636d5e9fSmrg		first = True;
7600636d5e9fSmrg		my_params = (argc - restart_params);
7601636d5e9fSmrg	    }
7602636d5e9fSmrg	    TRACE((" my_params:      %d\n", my_params));
7603636d5e9fSmrg
7604636d5e9fSmrg	    if (my_params > argc) {
7605636d5e9fSmrg		TRACE((" re-allocate restartCommand\n"));
7606636d5e9fSmrg		FreeAndNull(restart_command);
7607636d5e9fSmrg	    }
7608636d5e9fSmrg
7609636d5e9fSmrg	    if (restart_command == NULL) {
7610636d5e9fSmrg		int need = argc + RESTART_PARAMS + 1;
7611636d5e9fSmrg
7612636d5e9fSmrg		restart_command = TypeCallocN(char *, need);
7613636d5e9fSmrg
7614636d5e9fSmrg		TRACE(("..inserting font-parameters\n"));
7615636d5e9fSmrg		for (source = target = 0; source < argc; ++source) {
7616636d5e9fSmrg		    if (source == my_params) {
7617636d5e9fSmrg			changes += insertFontParams(xw, &target, first);
7618636d5e9fSmrg			if (!first) {
7619636d5e9fSmrg			    source += (RESTART_PARAMS - 1);
7620636d5e9fSmrg			    continue;
7621636d5e9fSmrg			}
7622636d5e9fSmrg		    }
7623636d5e9fSmrg		    if (argv[source] == NULL)
7624636d5e9fSmrg			break;
7625636d5e9fSmrg		    restart_command[target++] = x_strdup(argv[source]);
7626636d5e9fSmrg		}
7627636d5e9fSmrg		restart_command[target] = NULL;
7628636d5e9fSmrg	    } else {
7629636d5e9fSmrg		TRACE(("..replacing font-parameters\n"));
7630636d5e9fSmrg		target = my_params;
7631636d5e9fSmrg		changes += insertFontParams(xw, &target, first);
7632636d5e9fSmrg	    }
7633636d5e9fSmrg	    if (changes) {
7634636d5e9fSmrg		TRACE(("..%d parameters changed\n", changes));
7635636d5e9fSmrg		XtSetArg(args[0], XtNrestartCommand, restart_command);
7636636d5e9fSmrg		XtSetValues(toplevel, args, 1);
7637636d5e9fSmrg	    } else {
7638636d5e9fSmrg		TRACE(("..NO parameters changed\n"));
7639636d5e9fSmrg	    }
7640636d5e9fSmrg	}
7641636d5e9fSmrg	TRACE_SM_PROPS();
7642636d5e9fSmrg    }
7643636d5e9fSmrg}
76443367019cSmrg#endif /* OPT_SESSION_MGT */
76453367019cSmrg
76463367019cSmrgWidget
76473367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
76483367019cSmrg		     String my_class,
76493367019cSmrg		     XrmOptionDescRec * options,
76503367019cSmrg		     Cardinal num_options,
76513367019cSmrg		     int *argc_in_out,
7652d4fba8b9Smrg		     char **argv_in_out,
7653fa3f02f3Smrg		     String *fallback_resources,
76543367019cSmrg		     WidgetClass widget_class,
76553367019cSmrg		     ArgList args,
76563367019cSmrg		     Cardinal num_args)
76573367019cSmrg{
76583367019cSmrg    Widget result;
76593367019cSmrg
76603367019cSmrg    XtSetErrorHandler(xt_error);
76613367019cSmrg#if OPT_SESSION_MGT
76623367019cSmrg    result = XtOpenApplication(app_context_return,
76633367019cSmrg			       my_class,
76643367019cSmrg			       options,
76653367019cSmrg			       num_options,
76663367019cSmrg			       argc_in_out,
76673367019cSmrg			       argv_in_out,
76683367019cSmrg			       fallback_resources,
76693367019cSmrg			       widget_class,
76703367019cSmrg			       args,
76713367019cSmrg			       num_args);
76723367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
76733367019cSmrg#else
76749a64e1c5Smrg    (void) widget_class;
76759a64e1c5Smrg    (void) args;
76769a64e1c5Smrg    (void) num_args;
76773367019cSmrg    result = XtAppInitialize(app_context_return,
76783367019cSmrg			     my_class,
76793367019cSmrg			     options,
76803367019cSmrg			     num_options,
76813367019cSmrg			     argc_in_out,
76823367019cSmrg			     argv_in_out,
76833367019cSmrg			     fallback_resources,
76843367019cSmrg			     NULL, 0);
76853367019cSmrg#endif /* OPT_SESSION_MGT */
7686e8264990Smrg    XtSetErrorHandler(NULL);
76873367019cSmrg
76883367019cSmrg    return result;
76893367019cSmrg}
76903367019cSmrg
7691d4fba8b9Smrg/*
7692d4fba8b9Smrg * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
7693d4fba8b9Smrg * our own error-handler.
7694d4fba8b9Smrg */
7695d4fba8b9Smrg/* ARGSUSED */
7696d4fba8b9Smrgint
7697d4fba8b9Smrgignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
7698d4fba8b9Smrg{
7699d4fba8b9Smrg    return 1;
7700d4fba8b9Smrg}
7701d4fba8b9Smrg
77023367019cSmrgstatic int x11_errors;
77033367019cSmrg
77043367019cSmrgstatic int
77059a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
77063367019cSmrg{
77073367019cSmrg    (void) display;
77083367019cSmrg    (void) error_event;
77093367019cSmrg    ++x11_errors;
77103367019cSmrg    return 0;
77113367019cSmrg}
77123367019cSmrg
77133367019cSmrgBoolean
7714fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
77153367019cSmrg{
77163367019cSmrg    Boolean result = False;
77173367019cSmrg    Status code;
77183367019cSmrg
77193367019cSmrg    memset(attrs, 0, sizeof(*attrs));
77203367019cSmrg    if (win != None) {
77213367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
77223367019cSmrg	x11_errors = 0;
77233367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
77243367019cSmrg	XSetErrorHandler(save);
77253367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
77263367019cSmrg	if (result) {
77273367019cSmrg	    TRACE_WIN_ATTRS(attrs);
77283367019cSmrg	} else {
77293367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
77303367019cSmrg	}
77313367019cSmrg    }
77323367019cSmrg    return result;
77333367019cSmrg}
77343367019cSmrg
77353367019cSmrgBoolean
7736fa3f02f3SmrgxtermGetWinProp(Display *display,
77373367019cSmrg		Window win,
77383367019cSmrg		Atom property,
77393367019cSmrg		long long_offset,
77403367019cSmrg		long long_length,
77413367019cSmrg		Atom req_type,
77429a64e1c5Smrg		Atom *actual_type_return,
77433367019cSmrg		int *actual_format_return,
77443367019cSmrg		unsigned long *nitems_return,
77453367019cSmrg		unsigned long *bytes_after_return,
77463367019cSmrg		unsigned char **prop_return)
77473367019cSmrg{
7748d4fba8b9Smrg    Boolean result = False;
77493367019cSmrg
77503367019cSmrg    if (win != None) {
77513367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
77523367019cSmrg	x11_errors = 0;
77533367019cSmrg	if (XGetWindowProperty(display,
77543367019cSmrg			       win,
77553367019cSmrg			       property,
77563367019cSmrg			       long_offset,
77573367019cSmrg			       long_length,
77583367019cSmrg			       False,
77593367019cSmrg			       req_type,
77603367019cSmrg			       actual_type_return,
77613367019cSmrg			       actual_format_return,
77623367019cSmrg			       nitems_return,
77633367019cSmrg			       bytes_after_return,
77643367019cSmrg			       prop_return) == Success
77653367019cSmrg	    && x11_errors == 0) {
77663367019cSmrg	    result = True;
77673367019cSmrg	}
77683367019cSmrg	XSetErrorHandler(save);
77693367019cSmrg    }
77703367019cSmrg    return result;
77713367019cSmrg}
77723367019cSmrg
77733367019cSmrgvoid
77743367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
77753367019cSmrg{
77763367019cSmrg    Display *dpy = XtDisplay(toplevel);
77773367019cSmrg    XWindowAttributes attrs;
77783367019cSmrg
77793367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
77803367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
77813367019cSmrg	XtermWidget xw = term;
77823367019cSmrg	TScreen *screen = TScreenOf(xw);
77833367019cSmrg
77843367019cSmrg	XtRealizeWidget(toplevel);
77853367019cSmrg
77863367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
77873367019cSmrg	       XtWindow(toplevel),
77883367019cSmrg	       winToEmbedInto));
77893367019cSmrg	XReparentWindow(dpy,
77903367019cSmrg			XtWindow(toplevel),
77913367019cSmrg			winToEmbedInto, 0, 0);
77923367019cSmrg
77933367019cSmrg	screen->embed_high = (Dimension) attrs.height;
77943367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
77953367019cSmrg    }
77963367019cSmrg}
779794644356Smrg
779894644356Smrgvoid
779994644356Smrgfree_string(String value)
780094644356Smrg{
780194644356Smrg    free((void *) value);
780294644356Smrg}
7803dfb07bc7Smrg
7804dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
7805d4fba8b9Smrgint
780650027b5bSmrgupdate_winsize(TScreen *screen, int rows, int cols, int height, int width)
7807dfb07bc7Smrg{
7808d4fba8b9Smrg    int code = -1;
7809dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
7810d4fba8b9Smrg    static int last_rows = -1;
7811d4fba8b9Smrg    static int last_cols = -1;
7812636d5e9fSmrg    static int last_high = -1;
7813636d5e9fSmrg    static int last_wide = -1;
7814636d5e9fSmrg
7815636d5e9fSmrg    TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n",
7816636d5e9fSmrg	   last_rows, last_cols, last_high, last_wide,
7817636d5e9fSmrg	   rows, cols, height, width));
7818dfb07bc7Smrg
7819636d5e9fSmrg    if (rows != last_rows
7820636d5e9fSmrg	|| cols != last_cols
7821636d5e9fSmrg	|| last_high != height
7822636d5e9fSmrg	|| last_wide != width) {
7823d4fba8b9Smrg	TTYSIZE_STRUCT ts;
7824d4fba8b9Smrg
7825d4fba8b9Smrg	last_rows = rows;
7826d4fba8b9Smrg	last_cols = cols;
7827636d5e9fSmrg	last_high = height;
7828636d5e9fSmrg	last_wide = width;
7829d4fba8b9Smrg	setup_winsize(ts, rows, cols, height, width);
783050027b5bSmrg	TRACE_RC(code, SET_TTYSIZE(screen->respond, ts));
7831d4fba8b9Smrg	trace_winsize(ts, "from SET_TTYSIZE");
7832d4fba8b9Smrg    }
7833dfb07bc7Smrg#endif
7834dfb07bc7Smrg
7835dfb07bc7Smrg    (void) rows;
7836dfb07bc7Smrg    (void) cols;
7837dfb07bc7Smrg    (void) height;
7838dfb07bc7Smrg    (void) width;
7839d4fba8b9Smrg
7840d4fba8b9Smrg    return code;
7841dfb07bc7Smrg}
7842dfb07bc7Smrg
7843dfb07bc7Smrg/*
7844dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
7845dfb07bc7Smrg * manipulation 18 and 19.
7846dfb07bc7Smrg */
7847dfb07bc7Smrgvoid
7848dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
7849dfb07bc7Smrg{
7850dfb07bc7Smrg#if OPT_TEK4014
7851dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
7852dfb07bc7Smrg#endif
7853dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
7854dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
7855dfb07bc7Smrg
7856dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
785750027b5bSmrg	    update_winsize(screen,
7858dfb07bc7Smrg			   MaxRows(screen),
7859dfb07bc7Smrg			   MaxCols(screen),
7860dfb07bc7Smrg			   Height(screen),
7861dfb07bc7Smrg			   Width(screen));
7862dfb07bc7Smrg	}
7863dfb07bc7Smrg}
7864d4fba8b9Smrg
7865980988aeSmrgstatic void
7866980988aeSmrgxtermInitTitle(TScreen *screen, int which)
7867980988aeSmrg{
7868980988aeSmrg    TRACE(("xtermInitTitle #%d\n", which));
7869980988aeSmrg    screen->saved_titles.data[which].iconName = NULL;
7870980988aeSmrg    screen->saved_titles.data[which].windowName = NULL;
7871980988aeSmrg}
7872980988aeSmrg
7873980988aeSmrg/*
7874980988aeSmrg * Store/update an item on the title stack.
7875980988aeSmrg */
7876980988aeSmrgvoid
7877980988aeSmrgxtermPushTitle(TScreen *screen, int which, SaveTitle * item)
7878980988aeSmrg{
7879980988aeSmrg    if (which-- <= 0) {
7880980988aeSmrg	which = screen->saved_titles.used++;
7881980988aeSmrg	screen->saved_titles.used %= MAX_SAVED_TITLES;
7882980988aeSmrg    }
7883980988aeSmrg    which %= MAX_SAVED_TITLES;
7884980988aeSmrg    xtermFreeTitle(&screen->saved_titles.data[which]);
7885980988aeSmrg    screen->saved_titles.data[which] = *item;
7886980988aeSmrg    TRACE(("xtermPushTitle #%d: icon='%s', window='%s'\n", which,
7887980988aeSmrg	   NonNull(item->iconName),
7888980988aeSmrg	   NonNull(item->windowName)));
7889980988aeSmrg}
7890980988aeSmrg
7891980988aeSmrg/*
7892980988aeSmrg * Pop/retrieve an item from the title stack.
7893980988aeSmrg */
7894980988aeSmrgBoolean
7895980988aeSmrgxtermPopTitle(TScreen *screen, int which, SaveTitle * item)
7896980988aeSmrg{
7897980988aeSmrg    Boolean result = True;
7898980988aeSmrg    Boolean popped = False;
7899980988aeSmrg
7900980988aeSmrg    if (which-- > 0) {
7901980988aeSmrg	which %= MAX_SAVED_TITLES;
7902980988aeSmrg    } else if (screen->saved_titles.used > 0) {
7903980988aeSmrg	which = ((--(screen->saved_titles.used) + MAX_SAVED_TITLES) % MAX_SAVED_TITLES);
7904980988aeSmrg	popped = True;
7905980988aeSmrg    } else {
7906980988aeSmrg	result = False;
7907980988aeSmrg    }
7908980988aeSmrg    if (result) {
7909980988aeSmrg	*item = screen->saved_titles.data[which];
7910980988aeSmrg	TRACE(("xtermPopTitle #%d: icon='%s', window='%s'\n", which,
7911980988aeSmrg	       NonNull(item->iconName),
7912980988aeSmrg	       NonNull(item->windowName)));
7913980988aeSmrg
7914980988aeSmrg	/* if the data is incomplete, try to get it from the next levels */
7915980988aeSmrg#define TryHigher(name) \
7916980988aeSmrg	if (item->name == NULL) { \
7917980988aeSmrg	    int n; \
7918980988aeSmrg	    for (n = 1; n < MAX_SAVED_TITLES; ++n) { \
7919980988aeSmrg		int nw = ((which - n) + MAX_SAVED_TITLES) % MAX_SAVED_TITLES; \
7920980988aeSmrg		if ((item->name = screen->saved_titles.data[nw].name) != NULL) { \
7921980988aeSmrg		    item->name = x_strdup(item->name); \
7922980988aeSmrg		    break; \
7923980988aeSmrg		} \
7924980988aeSmrg	    } \
7925980988aeSmrg	}
7926980988aeSmrg	TryHigher(iconName);
7927980988aeSmrg	TryHigher(windowName);
7928980988aeSmrg
7929980988aeSmrg	if (popped) {
7930980988aeSmrg	    xtermInitTitle(screen, which);
7931980988aeSmrg	}
7932980988aeSmrg    }
7933980988aeSmrg    return result;
7934980988aeSmrg}
7935980988aeSmrg
7936980988aeSmrg/*
7937980988aeSmrg * Discard data used for pushing or popping title.
7938980988aeSmrg */
7939980988aeSmrgvoid
7940980988aeSmrgxtermFreeTitle(SaveTitle * item)
7941980988aeSmrg{
7942980988aeSmrg    TRACE(("xtermFreeTitle icon='%s', window='%s'\n",
7943980988aeSmrg	   NonNull(item->iconName),
7944980988aeSmrg	   NonNull(item->windowName)));
7945980988aeSmrg    FreeAndNull(item->iconName);
7946980988aeSmrg    FreeAndNull(item->windowName);
7947980988aeSmrg}
7948980988aeSmrg
7949d4fba8b9Smrg#if OPT_XTERM_SGR
7950190d7dceSmrgvoid
7951190d7dceSmrgxtermReportTitleStack(XtermWidget xw)
7952190d7dceSmrg{
7953190d7dceSmrg    TScreen *screen = TScreenOf(xw);
7954190d7dceSmrg    char reply[100];
7955190d7dceSmrg
7956190d7dceSmrg    sprintf(reply, "%d;%d", screen->saved_titles.used, MAX_SAVED_TITLES);
7957190d7dceSmrg    unparseputc1(xw, ANSI_CSI);
7958190d7dceSmrg    unparseputs(xw, reply);
7959190d7dceSmrg    unparseputc(xw, '#');
7960190d7dceSmrg    unparseputc(xw, 'S');
7961190d7dceSmrg    unparse_end(xw);
7962190d7dceSmrg}
7963d4fba8b9Smrg
7964d4fba8b9Smrg#if OPT_TRACE
7965d4fba8b9Smrgstatic char *
7966d4fba8b9SmrgtraceIFlags(IFlags flags)
7967d4fba8b9Smrg{
7968d4fba8b9Smrg    static char result[1000];
7969d4fba8b9Smrg    result[0] = '\0';
7970d4fba8b9Smrg#define DATA(name) if (flags & name) { strcat(result, " " #name); }
7971d4fba8b9Smrg    DATA(INVERSE);
7972d4fba8b9Smrg    DATA(UNDERLINE);
7973d4fba8b9Smrg    DATA(BOLD);
7974d4fba8b9Smrg    DATA(BLINK);
7975d4fba8b9Smrg    DATA(INVISIBLE);
7976d4fba8b9Smrg    DATA(BG_COLOR);
7977d4fba8b9Smrg    DATA(FG_COLOR);
7978d4fba8b9Smrg
7979d4fba8b9Smrg#if OPT_WIDE_ATTRS
7980d4fba8b9Smrg    DATA(ATR_FAINT);
7981d4fba8b9Smrg    DATA(ATR_ITALIC);
7982d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7983d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7984d4fba8b9Smrg    DATA(ATR_DIRECT_FG);
7985d4fba8b9Smrg    DATA(ATR_DIRECT_BG);
7986d4fba8b9Smrg#endif
7987d4fba8b9Smrg#undef DATA
7988d4fba8b9Smrg    return result;
7989d4fba8b9Smrg}
7990d4fba8b9Smrg
7991d4fba8b9Smrgstatic char *
7992d4fba8b9SmrgtraceIStack(unsigned flags)
7993d4fba8b9Smrg{
7994d4fba8b9Smrg    static char result[1000];
7995d4fba8b9Smrg    result[0] = '\0';
7996d4fba8b9Smrg#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7997d4fba8b9Smrg    DATA(INVERSE);
7998d4fba8b9Smrg    DATA(UNDERLINE);
7999d4fba8b9Smrg    DATA(BOLD);
8000d4fba8b9Smrg    DATA(BLINK);
8001d4fba8b9Smrg    DATA(INVISIBLE);
8002d4fba8b9Smrg#if OPT_ISO_COLORS
8003d4fba8b9Smrg    DATA(BG_COLOR);
8004d4fba8b9Smrg    DATA(FG_COLOR);
8005d4fba8b9Smrg#endif
8006d4fba8b9Smrg
8007d4fba8b9Smrg#if OPT_WIDE_ATTRS
8008d4fba8b9Smrg    DATA(ATR_FAINT);
8009d4fba8b9Smrg    DATA(ATR_ITALIC);
8010d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
8011d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
8012d4fba8b9Smrg    /* direct-colors are a special case of ISO-colors (see above) */
8013d4fba8b9Smrg#endif
8014d4fba8b9Smrg#undef DATA
8015d4fba8b9Smrg    return result;
8016d4fba8b9Smrg}
8017d4fba8b9Smrg#endif
8018d4fba8b9Smrg
8019d4fba8b9Smrgvoid
8020d4fba8b9SmrgxtermPushSGR(XtermWidget xw, int value)
8021d4fba8b9Smrg{
8022d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
8023d4fba8b9Smrg
8024d4fba8b9Smrg    TRACE(("xtermPushSGR %d mask %#x %s\n",
8025d4fba8b9Smrg	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
8026d4fba8b9Smrg
8027d4fba8b9Smrg    if (s->used < MAX_SAVED_SGR) {
8028d4fba8b9Smrg	s->stack[s->used].mask = (IFlags) value;
8029d4fba8b9Smrg#define PUSH_FLAG(name) \
8030d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
8031d4fba8b9Smrg	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
8032d4fba8b9Smrg#define PUSH_DATA(name) \
8033d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
8034d4fba8b9Smrg	    TRACE(("...may pop %s %d\n", #name, xw->name))
8035d4fba8b9Smrg	PUSH_FLAG(flags);
8036d4fba8b9Smrg#if OPT_ISO_COLORS
8037d4fba8b9Smrg	PUSH_DATA(sgr_foreground);
8038d4fba8b9Smrg	PUSH_DATA(sgr_background);
8039d4fba8b9Smrg	PUSH_DATA(sgr_38_xcolors);
8040d4fba8b9Smrg#endif
8041d4fba8b9Smrg    }
8042d4fba8b9Smrg    s->used++;
8043d4fba8b9Smrg}
8044d4fba8b9Smrg
8045d4fba8b9Smrg#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
8046d4fba8b9Smrg
8047d4fba8b9Smrgvoid
8048d4fba8b9SmrgxtermReportSGR(XtermWidget xw, XTermRect *value)
8049d4fba8b9Smrg{
8050d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
8051d4fba8b9Smrg    char reply[BUFSIZ];
8052d4fba8b9Smrg    CellData working;
8053d4fba8b9Smrg    int row, col;
8054d4fba8b9Smrg    Boolean first = True;
8055d4fba8b9Smrg
8056d4fba8b9Smrg    TRACE(("xtermReportSGR %d,%d - %d,%d\n",
8057d4fba8b9Smrg	   value->top, value->left,
8058d4fba8b9Smrg	   value->bottom, value->right));
8059d4fba8b9Smrg
8060d4fba8b9Smrg    memset(&working, 0, sizeof(working));
8061d4fba8b9Smrg    for (row = value->top - 1; row < value->bottom; ++row) {
8062d4fba8b9Smrg	LineData *ld = getLineData(screen, row);
8063190d7dceSmrg	if (ld == NULL)
8064d4fba8b9Smrg	    continue;
8065d4fba8b9Smrg	for (col = value->left - 1; col < value->right; ++col) {
8066d4fba8b9Smrg	    if (first) {
8067d4fba8b9Smrg		first = False;
8068d4fba8b9Smrg		saveCellData(screen, &working, 0, ld, NULL, col);
8069d4fba8b9Smrg	    }
8070d4fba8b9Smrg	    working.attribs &= ld->attribs[col];
8071d4fba8b9Smrg#if OPT_ISO_COLORS
8072d4fba8b9Smrg	    if (working.attribs & FG_COLOR
8073d4fba8b9Smrg		&& GetCellColorFG(working.color)
8074d4fba8b9Smrg		!= GetCellColorFG(ld->color[col])) {
8075d4fba8b9Smrg		IAttrClr(working.attribs, FG_COLOR);
8076d4fba8b9Smrg	    }
8077d4fba8b9Smrg	    if (working.attribs & BG_COLOR
8078d4fba8b9Smrg		&& GetCellColorBG(working.color)
8079d4fba8b9Smrg		!= GetCellColorBG(ld->color[col])) {
8080d4fba8b9Smrg		IAttrClr(working.attribs, BG_COLOR);
8081d4fba8b9Smrg	    }
8082d4fba8b9Smrg#endif
8083d4fba8b9Smrg	}
8084d4fba8b9Smrg    }
8085d4fba8b9Smrg    xtermFormatSGR(xw, reply,
8086d4fba8b9Smrg		   working.attribs,
8087d4fba8b9Smrg		   GetCellColorFG(working.color),
8088d4fba8b9Smrg		   GetCellColorBG(working.color));
8089d4fba8b9Smrg    unparseputc1(xw, ANSI_CSI);
8090d4fba8b9Smrg    unparseputs(xw, reply);
8091d4fba8b9Smrg    unparseputc(xw, 'm');
8092d4fba8b9Smrg    unparse_end(xw);
8093d4fba8b9Smrg}
8094d4fba8b9Smrg
8095d4fba8b9Smrgvoid
8096d4fba8b9SmrgxtermPopSGR(XtermWidget xw)
8097d4fba8b9Smrg{
8098d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
8099d4fba8b9Smrg
8100d4fba8b9Smrg    TRACE(("xtermPopSGR %d\n", s->used));
8101d4fba8b9Smrg
8102d4fba8b9Smrg    if (s->used > 0) {
8103d4fba8b9Smrg	if (s->used-- <= MAX_SAVED_SGR) {
8104d4fba8b9Smrg	    IFlags mask = s->stack[s->used].mask;
8105d4fba8b9Smrg	    Boolean changed = False;
8106d4fba8b9Smrg
8107d4fba8b9Smrg	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
8108d4fba8b9Smrg	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
8109d4fba8b9Smrg	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
8110d4fba8b9Smrg#define POP_FLAG(name) \
8111d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
8112d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
8113d4fba8b9Smrg		    changed = True; \
8114d4fba8b9Smrg		    UIntClr(xw->flags, name); \
8115d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
8116d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
8117d4fba8b9Smrg		} \
8118d4fba8b9Smrg	    }
8119d4fba8b9Smrg#define POP_FLAG2(name,part) \
8120d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
8121d4fba8b9Smrg	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
8122d4fba8b9Smrg		    changed = True; \
8123d4fba8b9Smrg		    UIntClr(xw->flags, part); \
8124d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
8125d4fba8b9Smrg		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
8126d4fba8b9Smrg		} \
8127d4fba8b9Smrg	    }
8128d4fba8b9Smrg#define POP_DATA(name,value) \
8129d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
8130d4fba8b9Smrg	        Bool always = False; \
8131d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
8132d4fba8b9Smrg		    always = changed = True; \
8133d4fba8b9Smrg		    UIntClr(xw->flags, name); \
8134d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
8135d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
8136d4fba8b9Smrg		} \
8137d4fba8b9Smrg		if (always || (xw->value != s->stack[s->used].value)) { \
8138d4fba8b9Smrg		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
8139d4fba8b9Smrg		    xw->value = s->stack[s->used].value; \
8140d4fba8b9Smrg		    changed = True; \
8141d4fba8b9Smrg		} \
8142d4fba8b9Smrg	    }
8143d4fba8b9Smrg	    POP_FLAG(BOLD);
8144d4fba8b9Smrg	    POP_FLAG(UNDERLINE);
8145d4fba8b9Smrg	    POP_FLAG(BLINK);
8146d4fba8b9Smrg	    POP_FLAG(INVERSE);
8147d4fba8b9Smrg	    POP_FLAG(INVISIBLE);
8148d4fba8b9Smrg#if OPT_WIDE_ATTRS
8149d4fba8b9Smrg	    if (xBIT(psATR_ITALIC - 1) & mask) {
8150d4fba8b9Smrg		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
8151d4fba8b9Smrg	    }
8152d4fba8b9Smrg	    POP_FLAG(ATR_ITALIC);
8153d4fba8b9Smrg	    POP_FLAG(ATR_FAINT);
8154d4fba8b9Smrg	    POP_FLAG(ATR_STRIKEOUT);
8155d4fba8b9Smrg	    POP_FLAG(ATR_DBL_UNDER);
8156d4fba8b9Smrg#endif
8157d4fba8b9Smrg#if OPT_ISO_COLORS
8158d4fba8b9Smrg	    POP_DATA(FG_COLOR, sgr_foreground);
8159d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_background);
8160d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_38_xcolors);
8161d4fba8b9Smrg#if OPT_DIRECT_COLOR
8162d4fba8b9Smrg	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
8163d4fba8b9Smrg	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
8164d4fba8b9Smrg#endif
8165d4fba8b9Smrg	    if (changed) {
8166d4fba8b9Smrg		setExtendedColors(xw);
8167d4fba8b9Smrg	    }
8168d4fba8b9Smrg#else
8169d4fba8b9Smrg	    (void) changed;
8170d4fba8b9Smrg#endif
8171d4fba8b9Smrg	}
8172d4fba8b9Smrg#if OPT_ISO_COLORS
8173d4fba8b9Smrg	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
8174d4fba8b9Smrg	       traceIFlags(xw->flags),
8175d4fba8b9Smrg	       xw->sgr_foreground,
8176d4fba8b9Smrg	       xw->sgr_background,
8177d4fba8b9Smrg	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
8178d4fba8b9Smrg#else
8179d4fba8b9Smrg	TRACE(("xtermP -> flags%s\n",
8180d4fba8b9Smrg	       traceIFlags(xw->flags)));
8181d4fba8b9Smrg#endif
8182d4fba8b9Smrg    }
8183d4fba8b9Smrg}
8184d4fba8b9Smrg
8185d4fba8b9Smrg#if OPT_ISO_COLORS
8186d4fba8b9Smrgstatic ColorSlot *
8187d4fba8b9SmrgallocColorSlot(XtermWidget xw, int slot)
8188d4fba8b9Smrg{
8189d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8190d4fba8b9Smrg    ColorSlot *result = NULL;
8191d4fba8b9Smrg
8192d4fba8b9Smrg    if (slot >= 0 && slot < MAX_SAVED_SGR) {
8193d4fba8b9Smrg	if (s->palettes[slot] == NULL) {
8194d4fba8b9Smrg	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
8195d4fba8b9Smrg						     sizeof(ColorSlot)
8196d4fba8b9Smrg						     + (sizeof(ColorRes)
8197d4fba8b9Smrg							* MAXCOLORS));
8198d4fba8b9Smrg	}
8199d4fba8b9Smrg	result = s->palettes[slot];
8200d4fba8b9Smrg    }
8201d4fba8b9Smrg    return result;
8202d4fba8b9Smrg}
8203d4fba8b9Smrg
8204d4fba8b9Smrgstatic void
8205d4fba8b9SmrgpopOldColors(XtermWidget xw, ScrnColors * source)
8206d4fba8b9Smrg{
8207d4fba8b9Smrg    Boolean changed = False;
8208d4fba8b9Smrg    ScrnColors *target = xw->work.oldColors;
8209d4fba8b9Smrg
8210d4fba8b9Smrg    if (source->which != target->which) {
8211d4fba8b9Smrg	changed = True;
8212d4fba8b9Smrg    } else {
8213d4fba8b9Smrg	int n;
8214d4fba8b9Smrg	for (n = 0; n < NCOLORS; ++n) {
8215d4fba8b9Smrg	    if (COLOR_DEFINED(source, n)) {
8216d4fba8b9Smrg		if (COLOR_DEFINED(target, n)) {
8217d4fba8b9Smrg		    if (source->colors[n] != target->colors[n]) {
8218d4fba8b9Smrg			changed = True;
8219d4fba8b9Smrg			break;
8220d4fba8b9Smrg		    }
8221d4fba8b9Smrg		} else {
8222d4fba8b9Smrg		    changed = True;
8223d4fba8b9Smrg		    break;
8224d4fba8b9Smrg		}
8225d4fba8b9Smrg	    } else if (COLOR_DEFINED(target, n)) {
8226d4fba8b9Smrg		changed = True;
8227d4fba8b9Smrg		break;
8228d4fba8b9Smrg	    }
8229d4fba8b9Smrg	}
8230d4fba8b9Smrg    }
8231d4fba8b9Smrg    if (changed) {
8232d4fba8b9Smrg	ChangeColors(xw, source);
8233d4fba8b9Smrg	UpdateOldColors(xw, source);
8234d4fba8b9Smrg    }
8235d4fba8b9Smrg}
8236d4fba8b9Smrg#endif /* OPT_ISO_COLORS */
8237d4fba8b9Smrg
8238d4fba8b9Smrg#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
8239d4fba8b9Smrg#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
8240d4fba8b9Smrg
8241d4fba8b9Smrg/*
8242d4fba8b9Smrg * By default, a "push" increments the stack after copying to the current
8243d4fba8b9Smrg * slot.  But a specific target allows one to copy into a specific slot.
8244d4fba8b9Smrg */
8245d4fba8b9Smrgvoid
8246d4fba8b9SmrgxtermPushColors(XtermWidget xw, int value)
8247d4fba8b9Smrg{
8248d4fba8b9Smrg#if OPT_ISO_COLORS
8249d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8250d4fba8b9Smrg    int pushed = s->used;
8251d4fba8b9Smrg    int actual = (value <= 0) ? pushed : (value - 1);
8252d4fba8b9Smrg
8253d4fba8b9Smrg    TRACE(("xtermPushColors %d:%d\n", actual, pushed));
8254d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
8255d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
8256d4fba8b9Smrg	ColorSlot *palette;
8257d4fba8b9Smrg
8258d4fba8b9Smrg	if ((palette = allocColorSlot(xw, actual)) != NULL) {
8259d4fba8b9Smrg	    GetColors(xw, &(palette->base));
8260d4fba8b9Smrg	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
8261d4fba8b9Smrg	    if (value < 0) {
8262d4fba8b9Smrg		s->used++;
8263d4fba8b9Smrg		if (s->last < s->used)
8264d4fba8b9Smrg		    s->last = s->used;
8265d4fba8b9Smrg	    } else {
8266d4fba8b9Smrg		s->used = value;
8267d4fba8b9Smrg	    }
8268d4fba8b9Smrg	}
8269d4fba8b9Smrg    }
8270d4fba8b9Smrg#else
8271d4fba8b9Smrg    (void) xw;
8272d4fba8b9Smrg    (void) value;
8273d4fba8b9Smrg#endif
8274d4fba8b9Smrg}
8275d4fba8b9Smrg
8276d4fba8b9Smrgvoid
8277d4fba8b9SmrgxtermPopColors(XtermWidget xw, int value)
8278d4fba8b9Smrg{
8279d4fba8b9Smrg#if OPT_ISO_COLORS
8280d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8281d4fba8b9Smrg    int popped = (s->used - 1);
8282d4fba8b9Smrg    int actual = (value <= 0) ? popped : (value - 1);
8283d4fba8b9Smrg
8284d4fba8b9Smrg    TRACE(("xtermPopColors %d:%d\n", actual, popped));
8285d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
8286d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
8287d4fba8b9Smrg	ColorSlot *palette;
8288d4fba8b9Smrg
8289d4fba8b9Smrg	if ((palette = s->palettes[actual]) != NULL) {
8290d4fba8b9Smrg	    Boolean changed = DiffColorSlot(screen->Acolors,
8291d4fba8b9Smrg					    palette->ansi,
8292d4fba8b9Smrg					    MAXCOLORS);
8293d4fba8b9Smrg
8294d4fba8b9Smrg	    GetOldColors(xw);
8295d4fba8b9Smrg	    popOldColors(xw, &(palette->base));
8296d4fba8b9Smrg	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
8297d4fba8b9Smrg	    s->used = actual;
8298d4fba8b9Smrg	    if (changed)
8299d4fba8b9Smrg		xtermRepaint(xw);
8300d4fba8b9Smrg	}
8301d4fba8b9Smrg    }
8302d4fba8b9Smrg#else
8303d4fba8b9Smrg    (void) xw;
8304d4fba8b9Smrg    (void) value;
8305d4fba8b9Smrg#endif
8306d4fba8b9Smrg}
8307d4fba8b9Smrg
8308d4fba8b9Smrgvoid
8309d4fba8b9SmrgxtermReportColors(XtermWidget xw)
8310d4fba8b9Smrg{
8311d4fba8b9Smrg    ANSI reply;
8312d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8313d4fba8b9Smrg
8314d4fba8b9Smrg    memset(&reply, 0, sizeof(reply));
8315d4fba8b9Smrg    reply.a_type = ANSI_CSI;
8316d4fba8b9Smrg    reply.a_pintro = '?';
8317d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->used;
8318d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->last;
8319d4fba8b9Smrg    reply.a_inters = '#';
8320d4fba8b9Smrg    reply.a_final = 'Q';
8321d4fba8b9Smrg    unparseseq(xw, &reply);
8322d4fba8b9Smrg}
8323d4fba8b9Smrg#endif /* OPT_XTERM_SGR */
8324