misc.c revision 980988ae
1980988aeSmrg/* $XTermId: misc.c,v 1.1092 2024/05/21 00:28:28 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
108d522f475Smrg#ifdef VMS
109d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
110d522f475Smrg#ifdef ALLOWLOGFILEEXEC
111d522f475Smrg#undef ALLOWLOGFILEEXEC
112d522f475Smrg#endif
113d522f475Smrg#endif /* VMS */
114d522f475Smrg
115d4fba8b9Smrg#if USE_DOUBLE_BUFFER
116d4fba8b9Smrg#include <X11/extensions/Xdbe.h>
117d4fba8b9Smrg#endif
118d4fba8b9Smrg
119d4fba8b9Smrg#if OPT_WIDE_CHARS
120d4fba8b9Smrg#include <wctype.h>
121d4fba8b9Smrg#endif
122d4fba8b9Smrg
123d522f475Smrg#if OPT_TEK4014
124d522f475Smrg#define OUR_EVENT(event,Type) \
125d522f475Smrg		(event.type == Type && \
126d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
127d522f475Smrg		    (tekWidget && \
128d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
129d522f475Smrg#else
130d522f475Smrg#define OUR_EVENT(event,Type) \
131d522f475Smrg		(event.type == Type && \
132d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
133d522f475Smrg#endif
134d522f475Smrg
135d4fba8b9Smrg#define VB_DELAY    screen->visualBellDelay
136d4fba8b9Smrg#define EVENT_DELAY TScreenOf(term)->nextEventDelay
137d4fba8b9Smrg
1383367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
139d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
140d522f475Smrg
141d522f475Smrg#if OPT_EXEC_XTERM
142d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
143d522f475Smrg   error; adapted from libc docs */
144d522f475Smrgstatic char *
145d522f475SmrgReadlink(const char *filename)
146d522f475Smrg{
147d522f475Smrg    char *buf = NULL;
148cd3331d0Smrg    size_t size = 100;
149d522f475Smrg
150d522f475Smrg    for (;;) {
151037a25ddSmrg	int n;
152037a25ddSmrg	char *tmp = TypeRealloc(char, size, buf);
153037a25ddSmrg	if (tmp == NULL) {
154037a25ddSmrg	    free(buf);
155037a25ddSmrg	    return NULL;
156037a25ddSmrg	}
157037a25ddSmrg	buf = tmp;
158d522f475Smrg	memset(buf, 0, size);
159d522f475Smrg
160cd3331d0Smrg	n = (int) readlink(filename, buf, size);
161d522f475Smrg	if (n < 0) {
162d522f475Smrg	    free(buf);
163d522f475Smrg	    return NULL;
164d522f475Smrg	}
165d522f475Smrg
166d522f475Smrg	if ((unsigned) n < size) {
167d522f475Smrg	    return buf;
168d522f475Smrg	}
169d522f475Smrg
170d522f475Smrg	size *= 2;
171d522f475Smrg    }
172d522f475Smrg}
173d522f475Smrg#endif /* OPT_EXEC_XTERM */
174d522f475Smrg
175d522f475Smrgstatic void
176d522f475SmrgSleep(int msec)
177d522f475Smrg{
178d522f475Smrg    static struct timeval select_timeout;
179d522f475Smrg
180d522f475Smrg    select_timeout.tv_sec = 0;
181d522f475Smrg    select_timeout.tv_usec = msec * 1000;
182d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
183d522f475Smrg}
184d522f475Smrg
185d522f475Smrgstatic void
1863367019cSmrgselectwindow(XtermWidget xw, int flag)
187d522f475Smrg{
1883367019cSmrg    TScreen *screen = TScreenOf(xw);
1893367019cSmrg
190d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
191d522f475Smrg
192d522f475Smrg#if OPT_TEK4014
1933367019cSmrg    if (TEK4014_ACTIVE(xw)) {
194d522f475Smrg	if (!Ttoggled)
195d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
196d522f475Smrg	screen->select |= flag;
197d522f475Smrg	if (!Ttoggled)
198d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
199d522f475Smrg    } else
200d522f475Smrg#endif
201d522f475Smrg    {
202d4fba8b9Smrg#if OPT_INPUT_METHOD
2033367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
2043367019cSmrg	if (input && input->xic)
2053367019cSmrg	    XSetICFocus(input->xic);
2063367019cSmrg#endif
207d522f475Smrg
208d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
209d4fba8b9Smrg	    HideCursor(xw);
210d522f475Smrg	screen->select |= flag;
211d522f475Smrg	if (screen->cursor_state)
212d4fba8b9Smrg	    ShowCursor(xw);
213d522f475Smrg    }
214cd3331d0Smrg    GetScrollLock(screen);
215d522f475Smrg}
216d522f475Smrg
217d522f475Smrgstatic void
2183367019cSmrgunselectwindow(XtermWidget xw, int flag)
219d522f475Smrg{
2203367019cSmrg    TScreen *screen = TScreenOf(xw);
2213367019cSmrg
222d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
223d522f475Smrg
2243367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
225d522f475Smrg	screen->hide_pointer = False;
2268f44fb3bSmrg	xtermDisplayPointer(xw);
227d522f475Smrg    }
228d522f475Smrg
229d4fba8b9Smrg    screen->select &= ~flag;
230d4fba8b9Smrg
231d522f475Smrg    if (!screen->always_highlight) {
232d522f475Smrg#if OPT_TEK4014
2333367019cSmrg	if (TEK4014_ACTIVE(xw)) {
234d522f475Smrg	    if (!Ttoggled)
235d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
236d522f475Smrg	    if (!Ttoggled)
237d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
238d522f475Smrg	} else
239d522f475Smrg#endif
240d522f475Smrg	{
241d4fba8b9Smrg#if OPT_INPUT_METHOD
2423367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2433367019cSmrg	    if (input && input->xic)
2443367019cSmrg		XUnsetICFocus(input->xic);
2453367019cSmrg#endif
246d522f475Smrg
247d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
248d4fba8b9Smrg		HideCursor(xw);
249d522f475Smrg	    if (screen->cursor_state)
250d4fba8b9Smrg		ShowCursor(xw);
251d522f475Smrg	}
252d522f475Smrg    }
253d522f475Smrg}
254d522f475Smrg
255d522f475Smrgstatic void
2569a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
257d522f475Smrg{
258d522f475Smrg    TScreen *screen = TScreenOf(xw);
259d522f475Smrg
260d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
261cd3331d0Smrg    TRACE_FOCUS(xw, ev);
262cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
263cd3331d0Smrg	ev->focus &&
264cd3331d0Smrg	!(screen->select & FOCUS))
2653367019cSmrg	selectwindow(xw, INWINDOW);
266d522f475Smrg}
267d522f475Smrg
268d522f475Smrgstatic void
2699a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
270d522f475Smrg{
271d522f475Smrg    TScreen *screen = TScreenOf(xw);
272d522f475Smrg
273d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
274cd3331d0Smrg    TRACE_FOCUS(xw, ev);
275cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
276cd3331d0Smrg	ev->focus &&
277cd3331d0Smrg	!(screen->select & FOCUS))
2783367019cSmrg	unselectwindow(xw, INWINDOW);
279d522f475Smrg}
280d522f475Smrg
281d522f475Smrg#ifndef XUrgencyHint
282d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
283d522f475Smrg#endif
284d522f475Smrg
285d522f475Smrgstatic void
286c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
287d522f475Smrg{
288c219fbebSmrg    TScreen *screen = TScreenOf(xw);
289c219fbebSmrg
290d522f475Smrg    if (screen->bellIsUrgent) {
291c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
292d522f475Smrg	if (h != 0) {
293c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
294d522f475Smrg		h->flags |= XUrgencyHint;
295d522f475Smrg	    } else {
296d522f475Smrg		h->flags &= ~XUrgencyHint;
297d522f475Smrg	    }
298c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
299d522f475Smrg	}
300d522f475Smrg    }
301d522f475Smrg}
302d522f475Smrg
303d522f475Smrgvoid
304d4fba8b9Smrgdo_xevents(XtermWidget xw)
305d522f475Smrg{
306d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
307d522f475Smrg
3083367019cSmrg    if (xtermAppPending()
309980988aeSmrg	|| GetBytesAvailable(screen->display) > 0) {
310d4fba8b9Smrg	xevents(xw);
311980988aeSmrg    }
312d522f475Smrg}
313d522f475Smrg
314d522f475Smrgvoid
3158f44fb3bSmrgxtermDisplayPointer(XtermWidget xw)
316d522f475Smrg{
317d522f475Smrg    TScreen *screen = TScreenOf(xw);
318d522f475Smrg
319d522f475Smrg    if (screen->Vshow) {
320d522f475Smrg	if (screen->hide_pointer) {
3218f44fb3bSmrg	    TRACE(("Display text pointer (hidden)\n"));
322d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
323d522f475Smrg	} else {
3248f44fb3bSmrg	    TRACE(("Display text pointer (visible)\n"));
325d522f475Smrg	    recolor_cursor(screen,
326d522f475Smrg			   screen->pointer_cursor,
327d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
328d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
329d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
330d522f475Smrg	}
331d522f475Smrg    }
332d522f475Smrg}
333d522f475Smrg
334d522f475Smrgvoid
335d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
336d522f475Smrg{
337d522f475Smrg    static int tried = -1;
338d522f475Smrg    TScreen *screen = TScreenOf(xw);
339d522f475Smrg
340d522f475Smrg#if OPT_TEK4014
341d522f475Smrg    if (TEK4014_SHOWN(xw))
342d522f475Smrg	enable = True;
343d522f475Smrg#endif
344d522f475Smrg
345d522f475Smrg    /*
346d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
347d522f475Smrg     * the mouse-mode:
348d522f475Smrg     */
349d522f475Smrg    if (!enable) {
350d522f475Smrg	switch (screen->pointer_mode) {
351d522f475Smrg	case pNever:
352d522f475Smrg	    enable = True;
353d522f475Smrg	    break;
354d522f475Smrg	case pNoMouse:
355d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
356d522f475Smrg		enable = True;
357d522f475Smrg	    break;
358d522f475Smrg	case pAlways:
3593367019cSmrg	case pFocused:
360d522f475Smrg	    break;
361d522f475Smrg	}
362d522f475Smrg    }
363d522f475Smrg
364d522f475Smrg    if (enable) {
365d522f475Smrg	if (screen->hide_pointer) {
366d522f475Smrg	    screen->hide_pointer = False;
3678f44fb3bSmrg	    xtermDisplayPointer(xw);
368d522f475Smrg	    switch (screen->send_mouse_pos) {
369d522f475Smrg	    case ANY_EVENT_MOUSE:
370d522f475Smrg		break;
371d522f475Smrg	    default:
372d522f475Smrg		MotionOff(screen, xw);
373d522f475Smrg		break;
374d522f475Smrg	    }
375d522f475Smrg	}
376d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
377d522f475Smrg	if (screen->hidden_cursor == 0) {
378d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
379d522f475Smrg	}
380d522f475Smrg	if (screen->hidden_cursor == 0) {
381d522f475Smrg	    tried = 1;
382d522f475Smrg	} else {
383d522f475Smrg	    tried = 0;
384d522f475Smrg	    screen->hide_pointer = True;
3858f44fb3bSmrg	    xtermDisplayPointer(xw);
386d522f475Smrg	    MotionOn(screen, xw);
387d522f475Smrg	}
388d522f475Smrg    }
389d522f475Smrg}
390d522f475Smrg
3913367019cSmrg/* true if p contains q */
3923367019cSmrg#define ExposeContains(p,q) \
3933367019cSmrg	    ((p)->y <= (q)->y \
3943367019cSmrg	  && (p)->x <= (q)->x \
3953367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
3963367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
3973367019cSmrg
3983367019cSmrgstatic XtInputMask
3999a64e1c5SmrgmergeExposeEvents(XEvent *target)
4003367019cSmrg{
4013367019cSmrg    XEvent next_event;
402037a25ddSmrg    XExposeEvent *p;
4033367019cSmrg
4043367019cSmrg    XtAppNextEvent(app_con, target);
4053367019cSmrg    p = (XExposeEvent *) target;
4063367019cSmrg
4073367019cSmrg    while (XtAppPending(app_con)
4083367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4093367019cSmrg	   && next_event.type == Expose) {
4103367019cSmrg	Boolean merge_this = False;
411d4fba8b9Smrg	XExposeEvent *q = (XExposeEvent *) (&next_event);
4123367019cSmrg
4133367019cSmrg	XtAppNextEvent(app_con, &next_event);
414d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4153367019cSmrg
4163367019cSmrg	/*
4173367019cSmrg	 * If either window is contained within the other, merge the events.
4183367019cSmrg	 * The traces show that there are also cases where a full repaint of
4193367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4203367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4213367019cSmrg	 * to skim several events ahead.
4223367019cSmrg	 */
4233367019cSmrg	if (p->window == q->window) {
4243367019cSmrg	    if (ExposeContains(p, q)) {
4253367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4263367019cSmrg		merge_this = True;
4273367019cSmrg		next_event = *target;
4283367019cSmrg	    } else if (ExposeContains(q, p)) {
4293367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4303367019cSmrg		merge_this = True;
4313367019cSmrg	    }
4323367019cSmrg	}
4333367019cSmrg	if (!merge_this) {
4343367019cSmrg	    XtDispatchEvent(target);
4353367019cSmrg	}
4363367019cSmrg	*target = next_event;
4373367019cSmrg    }
4383367019cSmrg    XtDispatchEvent(target);
4393367019cSmrg    return XtAppPending(app_con);
4403367019cSmrg}
4413367019cSmrg
4423367019cSmrg/*
4433367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4443367019cSmrg * event.  Remove that from the queue so we can look further.
4453367019cSmrg *
4463367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4473367019cSmrg * that.  If the adjacent events are for different windows, process the older
4483367019cSmrg * event and update the event used for comparing windows.  If they are for the
4493367019cSmrg * same window, only the newer event is of interest.
4503367019cSmrg *
4513367019cSmrg * Finally, process the (remaining) configure-notify event.
4523367019cSmrg */
4533367019cSmrgstatic XtInputMask
4549a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4553367019cSmrg{
4563367019cSmrg    XEvent next_event;
457037a25ddSmrg    XConfigureEvent *p;
4583367019cSmrg
4593367019cSmrg    XtAppNextEvent(app_con, target);
4603367019cSmrg    p = (XConfigureEvent *) target;
4613367019cSmrg
4623367019cSmrg    if (XtAppPending(app_con)
4633367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
4643367019cSmrg	&& next_event.type == ConfigureNotify) {
4653367019cSmrg	Boolean merge_this = False;
466d4fba8b9Smrg	XConfigureEvent *q = (XConfigureEvent *) (&next_event);
4673367019cSmrg
4683367019cSmrg	XtAppNextEvent(app_con, &next_event);
469d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4703367019cSmrg
4713367019cSmrg	if (p->window == q->window) {
4723367019cSmrg	    TRACE(("pending Configure...merged\n"));
4733367019cSmrg	    merge_this = True;
4743367019cSmrg	}
4753367019cSmrg	if (!merge_this) {
4763367019cSmrg	    TRACE(("pending Configure...skipped\n"));
4773367019cSmrg	    XtDispatchEvent(target);
4783367019cSmrg	}
4793367019cSmrg	*target = next_event;
4803367019cSmrg    }
4813367019cSmrg    XtDispatchEvent(target);
4823367019cSmrg    return XtAppPending(app_con);
4833367019cSmrg}
4843367019cSmrg
485d4fba8b9Smrg#define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
486d4fba8b9Smrg#define SameButtonEvent(a,b) ( \
487d4fba8b9Smrg	SAME(a,b,type) && \
488d4fba8b9Smrg	SAME(a,b,serial) && \
489d4fba8b9Smrg	SAME(a,b,send_event) && \
490d4fba8b9Smrg	SAME(a,b,display) && \
491d4fba8b9Smrg	SAME(a,b,window) && \
492d4fba8b9Smrg	SAME(a,b,root) && \
493d4fba8b9Smrg	SAME(a,b,subwindow) && \
494d4fba8b9Smrg	SAME(a,b,time) && \
495d4fba8b9Smrg	SAME(a,b,x) && \
496d4fba8b9Smrg	SAME(a,b,y) && \
497d4fba8b9Smrg	SAME(a,b,x_root) && \
498d4fba8b9Smrg	SAME(a,b,y_root) && \
499d4fba8b9Smrg	SAME(a,b,state) && \
500d4fba8b9Smrg	SAME(a,b,button) && \
501d4fba8b9Smrg	SAME(a,b,same_screen))
502d4fba8b9Smrg
503d4fba8b9Smrg/*
504d4fba8b9Smrg * Work around a bug in the X mouse code, which delivers duplicate events.
505d4fba8b9Smrg */
506d4fba8b9Smrgstatic XtInputMask
507d4fba8b9SmrgmergeButtonEvents(XEvent *target)
508d4fba8b9Smrg{
509d4fba8b9Smrg    XEvent next_event;
510d4fba8b9Smrg    XButtonEvent *p;
511d4fba8b9Smrg
512d4fba8b9Smrg    XtAppNextEvent(app_con, target);
513d4fba8b9Smrg    p = (XButtonEvent *) target;
514d4fba8b9Smrg
515d4fba8b9Smrg    if (XtAppPending(app_con)
516d4fba8b9Smrg	&& XtAppPeekEvent(app_con, &next_event)
517d4fba8b9Smrg	&& SameButtonEvent(target, &next_event)) {
518d4fba8b9Smrg	Boolean merge_this = False;
519d4fba8b9Smrg	XButtonEvent *q = (XButtonEvent *) (&next_event);
520d4fba8b9Smrg
521d4fba8b9Smrg	XtAppNextEvent(app_con, &next_event);
522d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
523d4fba8b9Smrg
524d4fba8b9Smrg	if (p->window == q->window) {
525d4fba8b9Smrg	    TRACE(("pending ButtonEvent...merged\n"));
526d4fba8b9Smrg	    merge_this = True;
527d4fba8b9Smrg	}
528d4fba8b9Smrg	if (!merge_this) {
529d4fba8b9Smrg	    TRACE(("pending ButtonEvent...skipped\n"));
530d4fba8b9Smrg	    XtDispatchEvent(target);
531d4fba8b9Smrg	}
532d4fba8b9Smrg	*target = next_event;
533d4fba8b9Smrg    }
534d4fba8b9Smrg    XtDispatchEvent(target);
535d4fba8b9Smrg    return XtAppPending(app_con);
536d4fba8b9Smrg}
537d4fba8b9Smrg
5383367019cSmrg/*
5393367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5403367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5413367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5423367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5433367019cSmrg * point.
5443367019cSmrg *
5453367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5463367019cSmrg */
5473367019cSmrgXtInputMask
5483367019cSmrgxtermAppPending(void)
5493367019cSmrg{
5503367019cSmrg    XtInputMask result = XtAppPending(app_con);
5513367019cSmrg    XEvent this_event;
552037a25ddSmrg    Boolean found = False;
5533367019cSmrg
5543367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
555037a25ddSmrg	found = True;
556d4fba8b9Smrg	TRACE_EVENT("pending", &this_event, (String *) 0, 0);
5573367019cSmrg	if (this_event.type == Expose) {
5583367019cSmrg	    result = mergeExposeEvents(&this_event);
5593367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5603367019cSmrg	    result = mergeConfigureEvents(&this_event);
561d4fba8b9Smrg	} else if (this_event.type == ButtonPress ||
562d4fba8b9Smrg		   this_event.type == ButtonRelease) {
563d4fba8b9Smrg	    result = mergeButtonEvents(&this_event);
5643367019cSmrg	} else {
5653367019cSmrg	    break;
5663367019cSmrg	}
5673367019cSmrg    }
568037a25ddSmrg
569037a25ddSmrg    /*
570037a25ddSmrg     * With NetBSD, closing a shell results in closing the X input event
571037a25ddSmrg     * stream, which interferes with the "-hold" option.  Wait a short time in
572037a25ddSmrg     * this case, to avoid max'ing the CPU.
573037a25ddSmrg     */
574037a25ddSmrg    if (hold_screen && caught_intr && !found) {
575d4fba8b9Smrg	Sleep(EVENT_DELAY);
576037a25ddSmrg    }
5773367019cSmrg    return result;
5783367019cSmrg}
5793367019cSmrg
580d522f475Smrgvoid
581d4fba8b9Smrgxevents(XtermWidget xw)
582d522f475Smrg{
583d522f475Smrg    TScreen *screen = TScreenOf(xw);
584d522f475Smrg    XEvent event;
585d522f475Smrg    XtInputMask input_mask;
586d522f475Smrg
587d522f475Smrg    if (need_cleanup)
5883367019cSmrg	NormalExit();
589d522f475Smrg
590d522f475Smrg    if (screen->scroll_amt)
591d522f475Smrg	FlushScroll(xw);
592d522f475Smrg    /*
593d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
594d522f475Smrg     * will process the timeout and return without blockng on the
595cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
596d522f475Smrg     * with select().
597d522f475Smrg     */
5983367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
599cd3331d0Smrg	if (input_mask & XtIMTimer)
600cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
601d522f475Smrg#if OPT_SESSION_MGT
602cd3331d0Smrg	/*
603cd3331d0Smrg	 * Session management events are alternative input events. Deal with
604cd3331d0Smrg	 * them in the same way.
605cd3331d0Smrg	 */
606cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
607cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
608d522f475Smrg#endif
609cd3331d0Smrg	else
610cd3331d0Smrg	    break;
611cd3331d0Smrg    }
612d522f475Smrg
613d522f475Smrg    /*
614d4fba8b9Smrg     * If there are no XEvents, don't wait around...
615d522f475Smrg     */
616d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
617d522f475Smrg	return;
618d522f475Smrg    do {
619d522f475Smrg	/*
620d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
621d522f475Smrg	 * We simply ignore all events except for those not passed down to
622d522f475Smrg	 * this function, e.g., those handled in in_put().
623d522f475Smrg	 */
624d522f475Smrg	if (screen->waitingForTrackInfo) {
625d4fba8b9Smrg	    Sleep(EVENT_DELAY);
626d522f475Smrg	    return;
627d522f475Smrg	}
628d522f475Smrg	XtAppNextEvent(app_con, &event);
629d522f475Smrg	/*
630d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
631d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
632d522f475Smrg	 * looking at the event ourselves we make sure that we can
633d522f475Smrg	 * do the right thing.
634d522f475Smrg	 */
635d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
636d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
637d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
638d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
639d4fba8b9Smrg	} else if (event.xany.type == MotionNotify
640d4fba8b9Smrg		   && event.xcrossing.window == XtWindow(xw)) {
641d4fba8b9Smrg	    switch (screen->send_mouse_pos) {
642d4fba8b9Smrg	    case ANY_EVENT_MOUSE:
643d522f475Smrg#if OPT_DEC_LOCATOR
644d4fba8b9Smrg	    case DEC_LOCATOR:
645d522f475Smrg#endif /* OPT_DEC_LOCATOR */
646d4fba8b9Smrg		SendMousePosition(xw, &event);
647d4fba8b9Smrg		xtermShowPointer(xw, True);
648d4fba8b9Smrg		continue;
649d4fba8b9Smrg	    case BTN_EVENT_MOUSE:
650d4fba8b9Smrg		SendMousePosition(xw, &event);
651d4fba8b9Smrg		xtermShowPointer(xw, True);
652d4fba8b9Smrg	    }
653d522f475Smrg	}
654d522f475Smrg
655cb4a1343Smrg	/*
656cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
657cb4a1343Smrg	 * mouse pointer back on.
658cb4a1343Smrg	 */
659cb4a1343Smrg	if (screen->hide_pointer) {
6603367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6613367019cSmrg		switch (event.xany.type) {
6623367019cSmrg		case MotionNotify:
6633367019cSmrg		    xtermShowPointer(xw, True);
6643367019cSmrg		    break;
6653367019cSmrg		}
6663367019cSmrg	    } else {
6673367019cSmrg		switch (event.xany.type) {
6683367019cSmrg		case KeyPress:
6693367019cSmrg		case KeyRelease:
6703367019cSmrg		case ButtonPress:
6713367019cSmrg		case ButtonRelease:
6723367019cSmrg		    /* also these... */
6733367019cSmrg		case Expose:
674037a25ddSmrg		case GraphicsExpose:
6753367019cSmrg		case NoExpose:
6763367019cSmrg		case PropertyNotify:
6773367019cSmrg		case ClientMessage:
6783367019cSmrg		    break;
6793367019cSmrg		default:
6803367019cSmrg		    xtermShowPointer(xw, True);
6813367019cSmrg		    break;
6823367019cSmrg		}
683cb4a1343Smrg	    }
684cb4a1343Smrg	}
685cb4a1343Smrg
686d522f475Smrg	if (!event.xany.send_event ||
687d522f475Smrg	    screen->allowSendEvents ||
688d522f475Smrg	    ((event.xany.type != KeyPress) &&
689d522f475Smrg	     (event.xany.type != KeyRelease) &&
690d522f475Smrg	     (event.xany.type != ButtonPress) &&
691d522f475Smrg	     (event.xany.type != ButtonRelease))) {
692d522f475Smrg
693d4fba8b9Smrg	    if (event.xany.type == MappingNotify) {
694d4fba8b9Smrg		XRefreshKeyboardMapping(&(event.xmapping));
695d4fba8b9Smrg		VTInitModifiers(xw);
696d4fba8b9Smrg	    }
697d522f475Smrg	    XtDispatchEvent(&event);
698d522f475Smrg	}
6993367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
700d522f475Smrg}
701d522f475Smrg
702d522f475Smrgstatic Cursor
703d522f475Smrgmake_hidden_cursor(XtermWidget xw)
704d522f475Smrg{
705d522f475Smrg    TScreen *screen = TScreenOf(xw);
706d522f475Smrg    Cursor c;
707d522f475Smrg    Display *dpy = screen->display;
708d522f475Smrg    XFontStruct *fn;
709d522f475Smrg
710d522f475Smrg    static XColor dummy;
711d522f475Smrg
712d522f475Smrg    /*
713d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
714d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
715b7c89284Ssnj     * server insists on drawing _something_.
716d522f475Smrg     */
717d522f475Smrg    TRACE(("Ask for nil2 font\n"));
7188f44fb3bSmrg    if ((fn = xtermLoadQueryFont(xw, "nil2")) == 0) {
719d522f475Smrg	TRACE(("...Ask for fixed font\n"));
7208f44fb3bSmrg	fn = xtermLoadQueryFont(xw, DEFFONT);
721d522f475Smrg    }
722d522f475Smrg
723d4fba8b9Smrg    if (fn != None) {
724d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
725d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
726d522f475Smrg	XFreeFont(dpy, fn);
727d522f475Smrg    } else {
728d4fba8b9Smrg	c = None;
729d522f475Smrg    }
730d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
731d4fba8b9Smrg    return c;
732d522f475Smrg}
733d522f475Smrg
734fa3f02f3Smrg/*
735fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
736fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
737fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
738fa3f02f3Smrg * until the window is initialized.
739fa3f02f3Smrg */
7408f44fb3bSmrg#ifdef HAVE_LIB_XCURSOR
741fa3f02f3Smrgvoid
742037a25ddSmrginit_colored_cursor(Display *dpy)
743fa3f02f3Smrg{
74494644356Smrg    static const char theme[] = "index.theme";
745d1603babSmrg    static const char pattern[] = "xtermXXXXXXXX";
746fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
747fa3f02f3Smrg
748fa3f02f3Smrg    xterm_cursor_theme = 0;
749037a25ddSmrg    /*
750037a25ddSmrg     * The environment variable overrides a (possible) resource Xcursor.theme
751037a25ddSmrg     */
752fa3f02f3Smrg    if (IsEmpty(env)) {
753037a25ddSmrg	env = XGetDefault(dpy, "Xcursor", "theme");
7548f44fb3bSmrg	TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env)));
7558f44fb3bSmrg    } else {
7568f44fb3bSmrg	TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env)));
757037a25ddSmrg    }
7588f44fb3bSmrg
759037a25ddSmrg    /*
760037a25ddSmrg     * If neither found, provide our own default theme.
761037a25ddSmrg     */
762037a25ddSmrg    if (IsEmpty(env)) {
763037a25ddSmrg	const char *tmp_dir;
764037a25ddSmrg	char *filename;
765037a25ddSmrg	size_t needed;
766037a25ddSmrg
7678f44fb3bSmrg	TRACE(("init_colored_cursor will make an empty Xcursor theme\n"));
7688f44fb3bSmrg
769fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
770fa3f02f3Smrg	    tmp_dir = P_tmpdir;
771fa3f02f3Smrg	}
772fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
773fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
774fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
775fa3f02f3Smrg
776fa3f02f3Smrg#ifdef HAVE_MKDTEMP
777fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
778fa3f02f3Smrg#else
779d1603babSmrg	    if (MakeTemp(filename) != 0
780fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
781fa3f02f3Smrg		xterm_cursor_theme = filename;
782fa3f02f3Smrg	    }
783fa3f02f3Smrg#endif
784d4fba8b9Smrg	    if (xterm_cursor_theme != filename)
785d4fba8b9Smrg		free(filename);
786fa3f02f3Smrg	    /*
787fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
788fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
789fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
790fa3f02f3Smrg	     */
791fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
792fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
793037a25ddSmrg		FILE *fp;
794037a25ddSmrg
795fa3f02f3Smrg		strcat(leaf, "/");
796fa3f02f3Smrg		strcat(leaf, theme);
7978f44fb3bSmrg
798fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
799fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
800fa3f02f3Smrg		    fclose(fp);
801fa3f02f3Smrg		    *leaf = '\0';
802fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
803fa3f02f3Smrg		    *leaf = '/';
8048f44fb3bSmrg
8058f44fb3bSmrg		    TRACE(("...initialized xterm_cursor_theme \"%s\"\n",
8068f44fb3bSmrg			   xterm_cursor_theme));
8078f44fb3bSmrg		    atexit(cleanup_colored_cursor);
8088f44fb3bSmrg		} else {
8098f44fb3bSmrg		    FreeAndNull(xterm_cursor_theme);
810fa3f02f3Smrg		}
811fa3f02f3Smrg	    }
812fa3f02f3Smrg	}
813fa3f02f3Smrg    }
814fa3f02f3Smrg}
8158f44fb3bSmrg#endif /* HAVE_LIB_XCURSOR */
816fa3f02f3Smrg
817fa3f02f3Smrg/*
818fa3f02f3Smrg * Once done, discard the file and directory holding it.
819fa3f02f3Smrg */
820fa3f02f3Smrgvoid
821fa3f02f3Smrgcleanup_colored_cursor(void)
822fa3f02f3Smrg{
823fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
824fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
825fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
826fa3f02f3Smrg	struct stat sb;
827fa3f02f3Smrg	if (!IsEmpty(my_path)
828fa3f02f3Smrg	    && stat(my_path, &sb) == 0
829fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
830fa3f02f3Smrg	    unlink(xterm_cursor_theme);
831fa3f02f3Smrg	    rmdir(my_path);
832fa3f02f3Smrg	}
8338f44fb3bSmrg	FreeAndNull(xterm_cursor_theme);
834fa3f02f3Smrg    }
835fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
836fa3f02f3Smrg}
837fa3f02f3Smrg
838d522f475SmrgCursor
839d4fba8b9Smrgmake_colored_cursor(unsigned c_index,		/* index into font */
840d522f475Smrg		    unsigned long fg,	/* pixel value */
841d522f475Smrg		    unsigned long bg)	/* pixel value */
842d522f475Smrg{
843d522f475Smrg    TScreen *screen = TScreenOf(term);
844d4fba8b9Smrg    Cursor c = None;
845d522f475Smrg    Display *dpy = screen->display;
846d522f475Smrg
847d4fba8b9Smrg    TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
848d4fba8b9Smrg    if (!IsEmpty(screen->cursor_font_name)) {
849d4fba8b9Smrg	static XTermFonts myFont;
850d4fba8b9Smrg
851d4fba8b9Smrg	/* adapted from XCreateFontCursor(), which hardcodes the font name */
852d4fba8b9Smrg	TRACE(("loading cursor from alternate cursor font\n"));
8538f44fb3bSmrg	myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name);
8548f44fb3bSmrg	if (myFont.fs != NULL) {
855d4fba8b9Smrg	    if (!xtermMissingChar(c_index, &myFont)
856d4fba8b9Smrg		&& !xtermMissingChar(c_index + 1, &myFont)) {
857d4fba8b9Smrg#define DATA(c) { 0UL, c, c, c, 0, 0 }
858d4fba8b9Smrg		static XColor foreground = DATA(0);
859d4fba8b9Smrg		static XColor background = DATA(65535);
860d4fba8b9Smrg#undef DATA
861d4fba8b9Smrg
862d4fba8b9Smrg		/*
863d4fba8b9Smrg		 * Cursor fonts follow each shape glyph with a mask glyph; so
864d4fba8b9Smrg		 * that character position 0 contains a shape, 1 the mask for
865d4fba8b9Smrg		 * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
866d4fba8b9Smrg		 * contains defined names for each shape.
867d4fba8b9Smrg		 */
868d4fba8b9Smrg		c = XCreateGlyphCursor(dpy,
869d4fba8b9Smrg				       myFont.fs->fid,	/* source_font */
870d4fba8b9Smrg				       myFont.fs->fid,	/* mask_font */
871d4fba8b9Smrg				       c_index + 0,	/* source_char */
872d4fba8b9Smrg				       c_index + 1,	/* mask_char */
873d4fba8b9Smrg				       &foreground,
874d4fba8b9Smrg				       &background);
875d4fba8b9Smrg	    }
876d4fba8b9Smrg	    XFreeFont(dpy, myFont.fs);
877d4fba8b9Smrg	}
878d4fba8b9Smrg	if (c == None) {
879d4fba8b9Smrg	    xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
880d4fba8b9Smrg			 c_index, screen->cursor_font_name);
881d4fba8b9Smrg	}
882d4fba8b9Smrg    }
883d4fba8b9Smrg    if (c == None)
884d4fba8b9Smrg	c = XCreateFontCursor(dpy, c_index);
885d4fba8b9Smrg
886d522f475Smrg    if (c != None) {
887d522f475Smrg	recolor_cursor(screen, c, fg, bg);
888d522f475Smrg    }
889d4fba8b9Smrg    return c;
890d522f475Smrg}
891d522f475Smrg
8928f44fb3bSmrg/* adapted from <X11/cursorfont.h> */
8938f44fb3bSmrgstatic int
8948f44fb3bSmrgLookupCursorShape(const char *name)
8958f44fb3bSmrg{
8968f44fb3bSmrg#define DATA(name) { XC_##name, #name }
8978f44fb3bSmrg    static struct {
8988f44fb3bSmrg	int code;
8998f44fb3bSmrg	const char name[25];
9008f44fb3bSmrg    } table[] = {
9018f44fb3bSmrg	DATA(X_cursor),
9028f44fb3bSmrg	    DATA(arrow),
9038f44fb3bSmrg	    DATA(based_arrow_down),
9048f44fb3bSmrg	    DATA(based_arrow_up),
9058f44fb3bSmrg	    DATA(boat),
9068f44fb3bSmrg	    DATA(bogosity),
9078f44fb3bSmrg	    DATA(bottom_left_corner),
9088f44fb3bSmrg	    DATA(bottom_right_corner),
9098f44fb3bSmrg	    DATA(bottom_side),
9108f44fb3bSmrg	    DATA(bottom_tee),
9118f44fb3bSmrg	    DATA(box_spiral),
9128f44fb3bSmrg	    DATA(center_ptr),
9138f44fb3bSmrg	    DATA(circle),
9148f44fb3bSmrg	    DATA(clock),
9158f44fb3bSmrg	    DATA(coffee_mug),
9168f44fb3bSmrg	    DATA(cross),
9178f44fb3bSmrg	    DATA(cross_reverse),
9188f44fb3bSmrg	    DATA(crosshair),
9198f44fb3bSmrg	    DATA(diamond_cross),
9208f44fb3bSmrg	    DATA(dot),
9218f44fb3bSmrg	    DATA(dotbox),
9228f44fb3bSmrg	    DATA(double_arrow),
9238f44fb3bSmrg	    DATA(draft_large),
9248f44fb3bSmrg	    DATA(draft_small),
9258f44fb3bSmrg	    DATA(draped_box),
9268f44fb3bSmrg	    DATA(exchange),
9278f44fb3bSmrg	    DATA(fleur),
9288f44fb3bSmrg	    DATA(gobbler),
9298f44fb3bSmrg	    DATA(gumby),
9308f44fb3bSmrg	    DATA(hand1),
9318f44fb3bSmrg	    DATA(hand2),
9328f44fb3bSmrg	    DATA(heart),
9338f44fb3bSmrg	    DATA(icon),
9348f44fb3bSmrg	    DATA(iron_cross),
9358f44fb3bSmrg	    DATA(left_ptr),
9368f44fb3bSmrg	    DATA(left_side),
9378f44fb3bSmrg	    DATA(left_tee),
9388f44fb3bSmrg	    DATA(leftbutton),
9398f44fb3bSmrg	    DATA(ll_angle),
9408f44fb3bSmrg	    DATA(lr_angle),
9418f44fb3bSmrg	    DATA(man),
9428f44fb3bSmrg	    DATA(middlebutton),
9438f44fb3bSmrg	    DATA(mouse),
9448f44fb3bSmrg	    DATA(pencil),
9458f44fb3bSmrg	    DATA(pirate),
9468f44fb3bSmrg	    DATA(plus),
9478f44fb3bSmrg	    DATA(question_arrow),
9488f44fb3bSmrg	    DATA(right_ptr),
9498f44fb3bSmrg	    DATA(right_side),
9508f44fb3bSmrg	    DATA(right_tee),
9518f44fb3bSmrg	    DATA(rightbutton),
9528f44fb3bSmrg	    DATA(rtl_logo),
9538f44fb3bSmrg	    DATA(sailboat),
9548f44fb3bSmrg	    DATA(sb_down_arrow),
9558f44fb3bSmrg	    DATA(sb_h_double_arrow),
9568f44fb3bSmrg	    DATA(sb_left_arrow),
9578f44fb3bSmrg	    DATA(sb_right_arrow),
9588f44fb3bSmrg	    DATA(sb_up_arrow),
9598f44fb3bSmrg	    DATA(sb_v_double_arrow),
9608f44fb3bSmrg	    DATA(shuttle),
9618f44fb3bSmrg	    DATA(sizing),
9628f44fb3bSmrg	    DATA(spider),
9638f44fb3bSmrg	    DATA(spraycan),
9648f44fb3bSmrg	    DATA(star),
9658f44fb3bSmrg	    DATA(target),
9668f44fb3bSmrg	    DATA(tcross),
9678f44fb3bSmrg	    DATA(top_left_arrow),
9688f44fb3bSmrg	    DATA(top_left_corner),
9698f44fb3bSmrg	    DATA(top_right_corner),
9708f44fb3bSmrg	    DATA(top_side),
9718f44fb3bSmrg	    DATA(top_tee),
9728f44fb3bSmrg	    DATA(trek),
9738f44fb3bSmrg	    DATA(ul_angle),
9748f44fb3bSmrg	    DATA(umbrella),
9758f44fb3bSmrg	    DATA(ur_angle),
9768f44fb3bSmrg	    DATA(watch),
9778f44fb3bSmrg	    DATA(xterm),
9788f44fb3bSmrg    };
9798f44fb3bSmrg#undef DATA
9808f44fb3bSmrg    Cardinal j;
9818f44fb3bSmrg    int result = -1;
9828f44fb3bSmrg    if (!IsEmpty(name)) {
9838f44fb3bSmrg	for (j = 0; j < XtNumber(table); ++j) {
9848f44fb3bSmrg	    if (!strcmp(name, table[j].name)) {
9858f44fb3bSmrg		result = table[j].code;
9868f44fb3bSmrg		break;
9878f44fb3bSmrg	    }
9888f44fb3bSmrg	}
9898f44fb3bSmrg    }
9908f44fb3bSmrg    return result;
9918f44fb3bSmrg}
9928f44fb3bSmrg
9938f44fb3bSmrgvoid
9948f44fb3bSmrgxtermSetupPointer(XtermWidget xw, const char *theShape)
9958f44fb3bSmrg{
9968f44fb3bSmrg    TScreen *screen = TScreenOf(xw);
9978f44fb3bSmrg    unsigned shape = XC_xterm;
9988f44fb3bSmrg    int other = LookupCursorShape(theShape);
9998f44fb3bSmrg    unsigned which;
10008f44fb3bSmrg
10018f44fb3bSmrg    if (other >= 0 && other < XC_num_glyphs)
10028f44fb3bSmrg	shape = (unsigned) other;
10038f44fb3bSmrg
10048f44fb3bSmrg    TRACE(("looked up shape index %d from shape name \"%s\"\n", other,
10058f44fb3bSmrg	   NonNull(theShape)));
10068f44fb3bSmrg
10078f44fb3bSmrg    which = (unsigned) (shape / 2);
10088f44fb3bSmrg    if (xw->work.pointer_cursors[which] == None) {
10098f44fb3bSmrg	TRACE(("creating text pointer cursor from shape %d\n", shape));
10108f44fb3bSmrg	xw->work.pointer_cursors[which] =
10118f44fb3bSmrg	    make_colored_cursor(shape,
10128f44fb3bSmrg				T_COLOR(screen, MOUSE_FG),
10138f44fb3bSmrg				T_COLOR(screen, MOUSE_BG));
10148f44fb3bSmrg    } else {
10158f44fb3bSmrg	TRACE(("updating text pointer cursor for shape %d\n", shape));
10168f44fb3bSmrg	recolor_cursor(screen,
10178f44fb3bSmrg		       screen->pointer_cursor,
10188f44fb3bSmrg		       T_COLOR(screen, MOUSE_FG),
10198f44fb3bSmrg		       T_COLOR(screen, MOUSE_BG));
10208f44fb3bSmrg    }
10218f44fb3bSmrg    if (screen->pointer_cursor != xw->work.pointer_cursors[which]) {
10228f44fb3bSmrg	screen->pointer_cursor = xw->work.pointer_cursors[which];
10238f44fb3bSmrg	TRACE(("defining text pointer cursor with shape %d\n", shape));
10248f44fb3bSmrg	XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor);
10258f44fb3bSmrg	if (XtIsRealized((Widget) xw)) {
10268f44fb3bSmrg	    /* briefly override pointerMode after changing the pointer */
10278f44fb3bSmrg	    if (screen->pointer_mode != pNever)
10288f44fb3bSmrg		screen->hide_pointer = True;
10298f44fb3bSmrg	    xtermShowPointer(xw, True);
10308f44fb3bSmrg	}
10318f44fb3bSmrg    }
10328f44fb3bSmrg}
10338f44fb3bSmrg
1034d522f475Smrg/* ARGSUSED */
1035d522f475Smrgvoid
1036d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
10379a64e1c5Smrg		 XEvent *event,
1038fa3f02f3Smrg		 String *params GCC_UNUSED,
1039d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
1040d522f475Smrg{
1041cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
1042cd3331d0Smrg    Input(term, &event->xkey, False);
1043d522f475Smrg}
1044d522f475Smrg
1045d522f475Smrg/* ARGSUSED */
1046d522f475Smrgvoid
1047d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
10489a64e1c5Smrg			 XEvent *event,
1049fa3f02f3Smrg			 String *params GCC_UNUSED,
1050d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
1051d522f475Smrg{
1052cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
1053cd3331d0Smrg    Input(term, &event->xkey, True);
1054d522f475Smrg}
1055d522f475Smrg
1056d522f475Smrg/* ARGSUSED */
1057d522f475Smrgvoid
1058d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
10599a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1060fa3f02f3Smrg		  String *params,
1061d522f475Smrg		  Cardinal *nparams)
1062d522f475Smrg{
1063d522f475Smrg
1064d522f475Smrg    if (*nparams != 1)
1065d522f475Smrg	return;
1066d522f475Smrg
1067d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
10680d92cbfdSchristos	const char *abcdef = "ABCDEF";
10690d92cbfdSchristos	const char *xxxxxx;
1070cd3331d0Smrg	Char c;
1071cd3331d0Smrg	UString p;
10720d92cbfdSchristos	unsigned value = 0;
10730d92cbfdSchristos
1074cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
10750d92cbfdSchristos	     '\0'; p++) {
10760d92cbfdSchristos	    value *= 16;
1077d522f475Smrg	    if (c >= '0' && c <= '9')
10780d92cbfdSchristos		value += (unsigned) (c - '0');
1079fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
10800d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
1081d522f475Smrg	    else
1082d522f475Smrg		break;
1083d522f475Smrg	}
10840d92cbfdSchristos	if (c == '\0') {
10850d92cbfdSchristos	    Char hexval[2];
10860d92cbfdSchristos	    hexval[0] = (Char) value;
10870d92cbfdSchristos	    hexval[1] = 0;
1088b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
10890d92cbfdSchristos	}
1090d522f475Smrg    } else {
1091cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
1092d522f475Smrg    }
1093d522f475Smrg}
1094d522f475Smrg
1095d522f475Smrg#if OPT_EXEC_XTERM
1096d522f475Smrg
1097d522f475Smrg#ifndef PROCFS_ROOT
1098d522f475Smrg#define PROCFS_ROOT "/proc"
1099d522f475Smrg#endif
1100d522f475Smrg
1101037a25ddSmrg/*
1102037a25ddSmrg * Determine the current working directory of the child so that we can
1103037a25ddSmrg * spawn a new terminal in the same directory.
1104037a25ddSmrg *
1105037a25ddSmrg * If we cannot get the CWD of the child, just use our own.
1106037a25ddSmrg */
1107037a25ddSmrgchar *
1108037a25ddSmrgProcGetCWD(pid_t pid)
1109037a25ddSmrg{
1110037a25ddSmrg    char *child_cwd = NULL;
1111037a25ddSmrg
1112037a25ddSmrg    if (pid) {
1113037a25ddSmrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
1114037a25ddSmrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
1115037a25ddSmrg	child_cwd = Readlink(child_cwd_link);
1116037a25ddSmrg    }
1117037a25ddSmrg    return child_cwd;
1118037a25ddSmrg}
1119037a25ddSmrg
1120d522f475Smrg/* ARGSUSED */
1121d522f475Smrgvoid
1122d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
11239a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1124fa3f02f3Smrg		    String *params,
1125d522f475Smrg		    Cardinal *nparams)
1126d522f475Smrg{
1127cd3331d0Smrg    TScreen *screen = TScreenOf(term);
1128d522f475Smrg    char *child_cwd = NULL;
1129d522f475Smrg    char *child_exe;
1130d522f475Smrg    pid_t pid;
1131d522f475Smrg
1132d522f475Smrg    /*
1133d522f475Smrg     * Try to find the actual program which is running in the child process.
1134d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
1135d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
1136d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
1137d522f475Smrg     */
1138d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
1139d522f475Smrg    if (!child_exe) {
1140cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
1141cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
1142d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
1143d522f475Smrg	} else {
11443367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
1145d522f475Smrg	}
1146d522f475Smrg	if (child_exe == 0)
1147d522f475Smrg	    return;
1148d522f475Smrg    }
1149d522f475Smrg
1150037a25ddSmrg    child_cwd = ProcGetCWD(screen->pid);
1151d522f475Smrg
1152d522f475Smrg    /* The reaper will take care of cleaning up the child */
1153d522f475Smrg    pid = fork();
1154d522f475Smrg    if (pid == -1) {
11553367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
1156d522f475Smrg    } else if (!pid) {
1157d522f475Smrg	/* We are the child */
1158d522f475Smrg	if (child_cwd) {
1159cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
1160d522f475Smrg	}
1161d522f475Smrg
1162d522f475Smrg	if (setuid(screen->uid) == -1
1163d522f475Smrg	    || setgid(screen->gid) == -1) {
11643367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
1165d522f475Smrg	} else {
11660d92cbfdSchristos	    unsigned myargc = *nparams + 1;
1167d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
1168d522f475Smrg
116994644356Smrg	    if (myargv != 0) {
117094644356Smrg		unsigned n = 0;
1171d522f475Smrg
117294644356Smrg		myargv[n++] = child_exe;
1173d522f475Smrg
117494644356Smrg		while (n < myargc) {
117594644356Smrg		    myargv[n++] = (char *) *params++;
117694644356Smrg		}
117794644356Smrg
117894644356Smrg		myargv[n] = 0;
117994644356Smrg		execv(child_exe, myargv);
118094644356Smrg	    }
1181d522f475Smrg
1182d522f475Smrg	    /* If we get here, we've failed */
11833367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
1184d522f475Smrg	}
1185d522f475Smrg	_exit(0);
1186d522f475Smrg    }
11873367019cSmrg
11883367019cSmrg    /* We are the parent; clean up */
1189d4fba8b9Smrg    free(child_cwd);
11903367019cSmrg    free(child_exe);
1191d522f475Smrg}
1192d522f475Smrg#endif /* OPT_EXEC_XTERM */
1193d522f475Smrg
1194d522f475Smrg/*
1195d522f475Smrg * Rather than sending characters to the host, put them directly into our
1196d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
1197d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
1198d522f475Smrg *
1199d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
1200d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
1201d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
1202d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
1203d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
1204d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
1205d522f475Smrg */
1206d522f475Smrg/* ARGSUSED */
1207d522f475Smrgvoid
1208d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
12099a64e1c5Smrg		XEvent *event GCC_UNUSED,
1210fa3f02f3Smrg		String *params,
1211d522f475Smrg		Cardinal *param_count)
1212d522f475Smrg{
1213d522f475Smrg    if (*param_count == 1) {
1214cd3331d0Smrg	const char *value = params[0];
1215d1603babSmrg	size_t need = strlen(value);
1216d1603babSmrg	size_t used = (size_t) (VTbuffer->next - VTbuffer->buffer);
1217d1603babSmrg	size_t have = (size_t) (VTbuffer->last - VTbuffer->buffer);
1218d522f475Smrg
1219d1603babSmrg	if ((have - used) + need < (size_t) BUF_SIZE) {
1220d522f475Smrg
1221d1603babSmrg	    fillPtyData(term, VTbuffer, value, strlen(value));
1222d522f475Smrg
1223d522f475Smrg	    TRACE(("Interpret %s\n", value));
1224d522f475Smrg	    VTbuffer->update++;
1225d522f475Smrg	}
1226d522f475Smrg    }
1227d522f475Smrg}
1228d522f475Smrg
1229d522f475Smrg/*ARGSUSED*/
1230d522f475Smrgvoid
1231d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
1232d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12339a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1234fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1235d522f475Smrg{
1236d522f475Smrg    /* NOP since we handled it above */
1237d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
1238cd3331d0Smrg    TRACE_FOCUS(w, event);
1239d522f475Smrg}
1240d522f475Smrg
1241d522f475Smrg/*ARGSUSED*/
1242d522f475Smrgvoid
1243d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
1244d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12459a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1246fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1247d522f475Smrg{
1248d522f475Smrg    /* NOP since we handled it above */
1249d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1250cd3331d0Smrg    TRACE_FOCUS(w, event);
1251d522f475Smrg}
1252d522f475Smrg
1253d522f475Smrg/*ARGSUSED*/
1254d522f475Smrgvoid
1255d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1256d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
12579a64e1c5Smrg		  XEvent *ev,
1258fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1259d522f475Smrg{
1260d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1261d522f475Smrg    XtermWidget xw = term;
1262d522f475Smrg    TScreen *screen = TScreenOf(xw);
1263d522f475Smrg
12643367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1265d522f475Smrg	   visibleEventType(event->type),
12663367019cSmrg	   visibleNotifyMode(event->mode),
12673367019cSmrg	   visibleNotifyDetail(event->detail)));
1268cd3331d0Smrg    TRACE_FOCUS(xw, event);
1269d522f475Smrg
1270d522f475Smrg    if (screen->quiet_grab
1271d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1272c219fbebSmrg	/* EMPTY */ ;
1273d522f475Smrg    } else if (event->type == FocusIn) {
127494644356Smrg	if (event->detail != NotifyPointer) {
127594644356Smrg	    setXUrgency(xw, False);
127694644356Smrg	}
1277d522f475Smrg
1278d522f475Smrg	/*
1279d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1280d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1281d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1282d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1283d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1284d522f475Smrg	 * delivered to the obscured window.
1285d522f475Smrg	 */
1286d522f475Smrg	if (event->detail == NotifyNonlinear
1287d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
12883367019cSmrg	    unselectwindow(xw, INWINDOW);
1289d522f475Smrg	}
12903367019cSmrg	selectwindow(xw,
1291d522f475Smrg		     ((event->detail == NotifyPointer)
1292d522f475Smrg		      ? INWINDOW
1293d522f475Smrg		      : FOCUS));
1294d522f475Smrg	SendFocusButton(xw, event);
1295d522f475Smrg    } else {
1296d522f475Smrg#if OPT_FOCUS_EVENT
1297d522f475Smrg	if (event->type == FocusOut) {
1298d522f475Smrg	    SendFocusButton(xw, event);
1299d522f475Smrg	}
1300d522f475Smrg#endif
1301d522f475Smrg	/*
1302d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1303d522f475Smrg	 * ignore.
1304d522f475Smrg	 */
1305d522f475Smrg	if (event->mode != NotifyGrab) {
13063367019cSmrg	    unselectwindow(xw,
1307d522f475Smrg			   ((event->detail == NotifyPointer)
1308d522f475Smrg			    ? INWINDOW
1309d522f475Smrg			    : FOCUS));
1310d522f475Smrg	}
1311d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1312cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1313d522f475Smrg	    ReverseVideo(xw);
1314d522f475Smrg	    screen->grabbedKbd = False;
1315d522f475Smrg	    update_securekbd();
1316d522f475Smrg	}
1317d522f475Smrg    }
1318d522f475Smrg}
1319d522f475Smrg
1320d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1321d522f475Smrg
1322b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1323b7c89284Ssnjstatic Atom
1324b7c89284SsnjAtomBell(XtermWidget xw, int which)
1325b7c89284Ssnj{
1326b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1327b7c89284Ssnj    static struct {
1328b7c89284Ssnj	int value;
1329b7c89284Ssnj	const char *name;
1330b7c89284Ssnj    } table[] = {
1331b7c89284Ssnj	DATA(Info),
1332b7c89284Ssnj	    DATA(MarginBell),
1333b7c89284Ssnj	    DATA(MinorError),
1334b7c89284Ssnj	    DATA(TerminalBell)
1335b7c89284Ssnj    };
1336d4fba8b9Smrg#undef DATA
1337b7c89284Ssnj    Cardinal n;
1338b7c89284Ssnj    Atom result = None;
1339b7c89284Ssnj
1340b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1341b7c89284Ssnj	if (table[n].value == which) {
1342980988aeSmrg	    result = CachedInternAtom(XtDisplay(xw), table[n].name);
1343b7c89284Ssnj	    break;
1344b7c89284Ssnj	}
1345b7c89284Ssnj    }
1346b7c89284Ssnj    return result;
1347b7c89284Ssnj}
1348b7c89284Ssnj#endif
1349b7c89284Ssnj
1350d522f475Smrgvoid
1351b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1352d522f475Smrg{
1353b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1354b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1355b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1356cd3331d0Smrg#endif
1357cd3331d0Smrg
1358cd3331d0Smrg    switch (which) {
1359cd3331d0Smrg    case XkbBI_Info:
1360cd3331d0Smrg    case XkbBI_MinorError:
1361cd3331d0Smrg    case XkbBI_MajorError:
1362cd3331d0Smrg    case XkbBI_TerminalBell:
1363cd3331d0Smrg	switch (screen->warningVolume) {
1364cd3331d0Smrg	case bvOff:
1365cd3331d0Smrg	    percent = -100;
1366cd3331d0Smrg	    break;
1367cd3331d0Smrg	case bvLow:
1368cd3331d0Smrg	    break;
1369cd3331d0Smrg	case bvHigh:
1370cd3331d0Smrg	    percent = 100;
1371cd3331d0Smrg	    break;
1372cd3331d0Smrg	}
1373cd3331d0Smrg	break;
1374cd3331d0Smrg    case XkbBI_MarginBell:
1375cd3331d0Smrg	switch (screen->marginVolume) {
1376cd3331d0Smrg	case bvOff:
1377cd3331d0Smrg	    percent = -100;
1378cd3331d0Smrg	    break;
1379cd3331d0Smrg	case bvLow:
1380cd3331d0Smrg	    break;
1381cd3331d0Smrg	case bvHigh:
1382cd3331d0Smrg	    percent = 100;
1383cd3331d0Smrg	    break;
1384cd3331d0Smrg	}
1385cd3331d0Smrg	break;
1386cd3331d0Smrg    default:
1387cd3331d0Smrg	break;
1388cd3331d0Smrg    }
1389cd3331d0Smrg
1390cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1391b7c89284Ssnj    if (tony != None) {
1392c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1393b7c89284Ssnj    } else
1394b7c89284Ssnj#endif
1395b7c89284Ssnj	XBell(screen->display, percent);
1396b7c89284Ssnj}
1397b7c89284Ssnj
1398b7c89284Ssnjvoid
1399cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1400b7c89284Ssnj{
1401b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1402d522f475Smrg    struct timeval curtime;
1403d522f475Smrg
1404b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1405b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1406d522f475Smrg	return;
1407d522f475Smrg    }
1408d522f475Smrg
1409c219fbebSmrg    setXUrgency(xw, True);
1410d522f475Smrg
1411d522f475Smrg    /* has enough time gone by that we are allowed to ring
1412d522f475Smrg       the bell again? */
1413d522f475Smrg    if (screen->bellSuppressTime) {
1414037a25ddSmrg	long now_msecs;
1415037a25ddSmrg
1416d522f475Smrg	if (screen->bellInProgress) {
1417d4fba8b9Smrg	    do_xevents(xw);
1418d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1419d522f475Smrg		return;
1420d522f475Smrg	    }
1421d522f475Smrg	}
1422d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1423d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1424d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1425d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1426d522f475Smrg	    return;
1427d522f475Smrg	}
1428d522f475Smrg	lastBellTime = now_msecs;
1429d522f475Smrg    }
1430d522f475Smrg
1431d522f475Smrg    if (screen->visualbell) {
1432d522f475Smrg	VisualBell();
1433d522f475Smrg    } else {
1434b7c89284Ssnj	xtermBell(xw, which, percent);
1435d522f475Smrg    }
1436d522f475Smrg
1437d522f475Smrg    if (screen->poponbell)
1438c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1439d522f475Smrg
1440d522f475Smrg    if (screen->bellSuppressTime) {
1441d522f475Smrg	/* now we change a property and wait for the notify event to come
1442d522f475Smrg	   back.  If the server is suspending operations while the bell
1443d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1444d522f475Smrg	   know when the previous bell has finished */
1445d522f475Smrg	Widget w = CURRENT_EMU();
1446d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1447d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1448d522f475Smrg	screen->bellInProgress = True;
1449d522f475Smrg    }
1450d522f475Smrg}
1451d522f475Smrg
1452d522f475Smrgstatic void
1453fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1454d522f475Smrg{
14553367019cSmrg    int y = 0;
14563367019cSmrg    int x = 0;
14573367019cSmrg
14583367019cSmrg    if (screen->flash_line) {
14593367019cSmrg	y = CursorY(screen, screen->cur_row);
14603367019cSmrg	height = (unsigned) FontHeight(screen);
14613367019cSmrg    }
14623367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1463d522f475Smrg    XFlush(screen->display);
1464d522f475Smrg    Sleep(VB_DELAY);
14653367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1466d522f475Smrg}
1467d522f475Smrg
1468d522f475Smrgvoid
1469d522f475SmrgVisualBell(void)
1470d522f475Smrg{
1471d4fba8b9Smrg    XtermWidget xw = term;
1472d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1473d522f475Smrg
1474d522f475Smrg    if (VB_DELAY > 0) {
1475d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1476d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1477d522f475Smrg	XGCValues gcval;
1478d522f475Smrg	GC visualGC;
1479d522f475Smrg
1480d522f475Smrg	gcval.function = GXxor;
1481d522f475Smrg	gcval.foreground = xorPixel;
1482d4fba8b9Smrg	visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
1483d522f475Smrg#if OPT_TEK4014
1484d4fba8b9Smrg	if (TEK4014_ACTIVE(xw)) {
1485cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1486d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1487d522f475Smrg			TFullWidth(tekscr),
1488d522f475Smrg			TFullHeight(tekscr));
1489d522f475Smrg	} else
1490d522f475Smrg#endif
1491d522f475Smrg	{
1492d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1493d522f475Smrg			FullWidth(screen),
1494d522f475Smrg			FullHeight(screen));
1495d522f475Smrg	}
1496d4fba8b9Smrg	XtReleaseGC((Widget) xw, visualGC);
1497d522f475Smrg    }
1498d522f475Smrg}
1499d522f475Smrg
1500d522f475Smrg/* ARGSUSED */
1501d522f475Smrgvoid
1502d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1503d522f475Smrg			 XtPointer data GCC_UNUSED,
15049a64e1c5Smrg			 XEvent *ev,
1505fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1506d522f475Smrg{
1507d522f475Smrg    TScreen *screen = TScreenOf(term);
1508d522f475Smrg
1509d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1510d522f475Smrg	screen->bellInProgress = False;
1511d522f475Smrg    }
1512d522f475Smrg}
1513d522f475Smrg
15143367019cSmrgvoid
1515d4fba8b9SmrgxtermWarning(const char *fmt, ...)
15163367019cSmrg{
15173367019cSmrg    int save_err = errno;
15183367019cSmrg    va_list ap;
15193367019cSmrg
1520dfb07bc7Smrg    fflush(stdout);
1521d4fba8b9Smrg
1522d4fba8b9Smrg#if OPT_TRACE
1523d4fba8b9Smrg    va_start(ap, fmt);
1524d4fba8b9Smrg    Trace("xtermWarning: ");
1525d4fba8b9Smrg    TraceVA(fmt, ap);
1526d4fba8b9Smrg    va_end(ap);
1527d4fba8b9Smrg#endif
1528d4fba8b9Smrg
15293367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15303367019cSmrg    va_start(ap, fmt);
15313367019cSmrg    vfprintf(stderr, fmt, ap);
15323367019cSmrg    (void) fflush(stderr);
15333367019cSmrg
15343367019cSmrg    va_end(ap);
15353367019cSmrg    errno = save_err;
15363367019cSmrg}
15373367019cSmrg
15383367019cSmrgvoid
1539d4fba8b9SmrgxtermPerror(const char *fmt, ...)
15403367019cSmrg{
15413367019cSmrg    int save_err = errno;
1542d4fba8b9Smrg    const char *msg = strerror(errno);
15433367019cSmrg    va_list ap;
15443367019cSmrg
1545dfb07bc7Smrg    fflush(stdout);
1546d4fba8b9Smrg
1547d4fba8b9Smrg#if OPT_TRACE
1548d4fba8b9Smrg    va_start(ap, fmt);
1549d4fba8b9Smrg    Trace("xtermPerror: ");
1550d4fba8b9Smrg    TraceVA(fmt, ap);
1551d4fba8b9Smrg    va_end(ap);
1552d4fba8b9Smrg#endif
1553d4fba8b9Smrg
15543367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
15553367019cSmrg    va_start(ap, fmt);
15563367019cSmrg    vfprintf(stderr, fmt, ap);
15573367019cSmrg    fprintf(stderr, ": %s\n", msg);
15583367019cSmrg    (void) fflush(stderr);
15593367019cSmrg
15603367019cSmrg    va_end(ap);
15613367019cSmrg    errno = save_err;
15623367019cSmrg}
15633367019cSmrg
1564d522f475SmrgWindow
1565c219fbebSmrgWMFrameWindow(XtermWidget xw)
1566d522f475Smrg{
1567d522f475Smrg    Window win_root, win_current, *children;
1568d522f475Smrg    Window win_parent = 0;
1569d522f475Smrg    unsigned int nchildren;
1570d522f475Smrg
1571c219fbebSmrg    win_current = XtWindow(xw);
1572d522f475Smrg
1573d522f475Smrg    /* find the parent which is child of root */
1574d522f475Smrg    do {
1575d522f475Smrg	if (win_parent)
1576d522f475Smrg	    win_current = win_parent;
1577c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1578d522f475Smrg		   win_current,
1579d522f475Smrg		   &win_root,
1580d522f475Smrg		   &win_parent,
1581d522f475Smrg		   &children,
1582d522f475Smrg		   &nchildren);
1583d522f475Smrg	XFree(children);
1584d522f475Smrg    } while (win_root != win_parent);
1585d522f475Smrg
1586d522f475Smrg    return win_current;
1587d522f475Smrg}
1588d522f475Smrg
1589d522f475Smrg#if OPT_DABBREV
1590d522f475Smrg/*
1591d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1592d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1593d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1594d522f475Smrg * expansions and ignores one of them if they are identical.
1595d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1596d522f475Smrg */
1597d522f475Smrg
1598d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1599d522f475Smrg
1600d522f475Smrgstatic int
1601fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1602d522f475Smrg{
1603b7c89284Ssnj    int result = -1;
1604b7c89284Ssnj    int firstLine = -(screen->savedlines);
1605d522f475Smrg
1606b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1607b7c89284Ssnj    while (cell->row >= firstLine) {
1608b7c89284Ssnj	if (--(cell->col) >= 0) {
1609b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1610b7c89284Ssnj	    break;
1611b7c89284Ssnj	}
1612b7c89284Ssnj	if (--(cell->row) < firstLine)
1613b7c89284Ssnj	    break;		/* ...there is no previous line */
1614b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1615b7c89284Ssnj	cell->col = MaxCols(screen);
1616b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1617b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1618d522f475Smrg	    break;
1619b7c89284Ssnj	}
1620d522f475Smrg    }
1621b7c89284Ssnj    return result;
1622d522f475Smrg}
1623d522f475Smrg
1624d522f475Smrgstatic char *
16259a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1626d522f475Smrg{
16279a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1628d522f475Smrg    char *abword;
1629d522f475Smrg    int c;
16309a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1631b7c89284Ssnj    char *result = 0;
1632d522f475Smrg
1633b7c89284Ssnj    abword = ab_end;
1634d522f475Smrg    *abword = '\0';		/* end of string marker */
1635d522f475Smrg
1636b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1637b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
16389a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1639b7c89284Ssnj	    *(--abword) = (char) c;
1640d522f475Smrg    }
1641d522f475Smrg
1642b7c89284Ssnj    if (c >= 0) {
1643b7c89284Ssnj	result = abword;
1644b7c89284Ssnj    } else if (abword != ab_end) {
1645b7c89284Ssnj	result = abword;
1646b7c89284Ssnj    }
1647b7c89284Ssnj
1648b7c89284Ssnj    if (result != 0) {
1649b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1650b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1651b7c89284Ssnj	    ;			/* skip preceding spaces */
1652b7c89284Ssnj	}
1653b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1654b7c89284Ssnj    }
1655b7c89284Ssnj    return result;
1656d522f475Smrg}
1657d522f475Smrg
1658d522f475Smrgstatic int
16599a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1660d522f475Smrg{
16619a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1662d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1663d522f475Smrg
1664b7c89284Ssnj    static CELL cell;
1665d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1666d522f475Smrg    static unsigned int expansions;
1667d522f475Smrg
1668d522f475Smrg    char *expansion;
1669d522f475Smrg    size_t hint_len;
1670b7c89284Ssnj    int result = 0;
1671b7c89284Ssnj    LineData *ld;
1672d522f475Smrg
1673d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1674d522f475Smrg	expansions = 0;
1675b7c89284Ssnj	cell.col = screen->cur_col;
1676b7c89284Ssnj	cell.row = screen->cur_row;
1677b7c89284Ssnj
1678d4fba8b9Smrg	free(dabbrev_hint);
1679b7c89284Ssnj
16809a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1681b7c89284Ssnj
1682d4fba8b9Smrg	    free(lastexpansion);
1683b7c89284Ssnj
1684b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1685b7c89284Ssnj
1686b7c89284Ssnj		/* make own copy */
1687b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1688b7c89284Ssnj		    screen->dabbrev_working = True;
1689b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1690b7c89284Ssnj		}
1691cd3331d0Smrg	    } else {
1692cd3331d0Smrg		return result;
1693b7c89284Ssnj	    }
1694cd3331d0Smrg	} else {
1695cd3331d0Smrg	    return result;
1696d522f475Smrg	}
1697b7c89284Ssnj	if (!screen->dabbrev_working) {
1698d4fba8b9Smrg	    free(lastexpansion);
1699d4fba8b9Smrg	    lastexpansion = 0;
1700b7c89284Ssnj	    return result;
1701b7c89284Ssnj	}
1702d522f475Smrg    }
1703d522f475Smrg
1704cd3331d0Smrg    if (dabbrev_hint == 0)
1705cd3331d0Smrg	return result;
1706cd3331d0Smrg
1707d522f475Smrg    hint_len = strlen(dabbrev_hint);
1708d522f475Smrg    for (;;) {
17099a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1710d522f475Smrg	    if (expansions >= 2) {
1711d522f475Smrg		expansions = 0;
1712b7c89284Ssnj		cell.col = screen->cur_col;
1713b7c89284Ssnj		cell.row = screen->cur_row;
1714d522f475Smrg		continue;
1715d522f475Smrg	    }
1716d522f475Smrg	    break;
1717d522f475Smrg	}
1718d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1719d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1720d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1721d522f475Smrg	    break;
1722d522f475Smrg    }
1723d522f475Smrg
1724b7c89284Ssnj    if (expansion != 0) {
1725037a25ddSmrg	Char *copybuffer;
1726037a25ddSmrg	size_t del_cnt = strlen(lastexpansion) - hint_len;
1727037a25ddSmrg	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1728b7c89284Ssnj
1729b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1730b7c89284Ssnj	    /* delete previous expansion */
1731b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1732b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1733b7c89284Ssnj		    expansion + hint_len,
1734b7c89284Ssnj		    strlen(expansion) - hint_len);
1735d1603babSmrg	    v_write(pty, copybuffer, buf_cnt);
1736b7c89284Ssnj	    /* v_write() just reset our flag */
1737b7c89284Ssnj	    screen->dabbrev_working = True;
1738b7c89284Ssnj	    free(copybuffer);
1739b7c89284Ssnj
1740b7c89284Ssnj	    free(lastexpansion);
1741b7c89284Ssnj
1742b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1743b7c89284Ssnj		result = 1;
1744b7c89284Ssnj		expansions++;
1745b7c89284Ssnj	    }
1746b7c89284Ssnj	}
1747b7c89284Ssnj    }
1748b7c89284Ssnj
1749b7c89284Ssnj    return result;
1750d522f475Smrg}
1751d522f475Smrg
1752d522f475Smrg/*ARGSUSED*/
1753d522f475Smrgvoid
1754b7c89284SsnjHandleDabbrevExpand(Widget w,
17559a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1756fa3f02f3Smrg		    String *params GCC_UNUSED,
1757d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1758d522f475Smrg{
1759b7c89284Ssnj    XtermWidget xw;
1760b7c89284Ssnj
1761cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1762b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
17639a64e1c5Smrg	if (!dabbrev_expand(xw))
1764cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1765d522f475Smrg    }
1766d522f475Smrg}
1767d522f475Smrg#endif /* OPT_DABBREV */
1768d522f475Smrg
1769d4fba8b9Smrgvoid
1770d4fba8b9SmrgxtermDeiconify(XtermWidget xw)
1771d4fba8b9Smrg{
1772d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1773d4fba8b9Smrg    Display *dpy = screen->display;
1774d4fba8b9Smrg    Window target = VShellWindow(xw);
1775d4fba8b9Smrg    XEvent e;
1776980988aeSmrg    Atom atom_state = CachedInternAtom(dpy, "_NET_ACTIVE_WINDOW");
1777d4fba8b9Smrg
1778d4fba8b9Smrg    if (xtermIsIconified(xw)) {
1779d4fba8b9Smrg	TRACE(("...de-iconify window %#lx\n", target));
1780980988aeSmrg	ResetHiddenHint(xw);
1781d4fba8b9Smrg	XMapWindow(dpy, target);
1782d4fba8b9Smrg
1783d4fba8b9Smrg	memset(&e, 0, sizeof(e));
1784d4fba8b9Smrg	e.xclient.type = ClientMessage;
1785d4fba8b9Smrg	e.xclient.message_type = atom_state;
1786d4fba8b9Smrg	e.xclient.display = dpy;
1787d4fba8b9Smrg	e.xclient.window = target;
1788d4fba8b9Smrg	e.xclient.format = 32;
1789d4fba8b9Smrg	e.xclient.data.l[0] = 1;
1790d4fba8b9Smrg	e.xclient.data.l[1] = CurrentTime;
1791d4fba8b9Smrg
1792d4fba8b9Smrg	XSendEvent(dpy, DefaultRootWindow(dpy), False,
1793d4fba8b9Smrg		   SubstructureRedirectMask | SubstructureNotifyMask, &e);
1794d4fba8b9Smrg	xevents(xw);
1795d4fba8b9Smrg    }
1796d4fba8b9Smrg}
1797d4fba8b9Smrg
1798d4fba8b9Smrgvoid
1799d4fba8b9SmrgxtermIconify(XtermWidget xw)
1800d4fba8b9Smrg{
1801d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1802d4fba8b9Smrg    Window target = VShellWindow(xw);
1803d4fba8b9Smrg
1804d4fba8b9Smrg    if (!xtermIsIconified(xw)) {
1805d4fba8b9Smrg	TRACE(("...iconify window %#lx\n", target));
1806d4fba8b9Smrg	XIconifyWindow(screen->display,
1807d4fba8b9Smrg		       target,
1808d4fba8b9Smrg		       DefaultScreen(screen->display));
1809d4fba8b9Smrg	xevents(xw);
1810d4fba8b9Smrg    }
1811d4fba8b9Smrg}
1812d4fba8b9Smrg
1813d4fba8b9SmrgBoolean
1814d4fba8b9SmrgxtermIsIconified(XtermWidget xw)
1815d4fba8b9Smrg{
1816d4fba8b9Smrg    XWindowAttributes win_attrs;
1817d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1818d4fba8b9Smrg    Window target = VShellWindow(xw);
1819d4fba8b9Smrg    Display *dpy = screen->display;
1820d4fba8b9Smrg    Boolean result = False;
1821d4fba8b9Smrg
1822d4fba8b9Smrg    if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
1823d4fba8b9Smrg	Atom actual_return_type;
1824d4fba8b9Smrg	int actual_format_return = 0;
1825d4fba8b9Smrg	unsigned long nitems_return = 0;
1826d4fba8b9Smrg	unsigned long bytes_after_return = 0;
1827d4fba8b9Smrg	unsigned char *prop_return = 0;
1828d4fba8b9Smrg	long long_length = 1024;
1829d4fba8b9Smrg	Atom requested_type = XA_ATOM;
1830980988aeSmrg	Atom is_hidden = CachedInternAtom(dpy, "_NET_WM_STATE_HIDDEN");
1831980988aeSmrg	Atom wm_state = CachedInternAtom(dpy, "_NET_WM_STATE");
1832d4fba8b9Smrg
1833d4fba8b9Smrg	/* this works with non-EWMH */
1834d4fba8b9Smrg	result = (win_attrs.map_state != IsViewable) ? True : False;
1835d4fba8b9Smrg
1836d4fba8b9Smrg	/* this is a convention used by some EWMH applications */
1837d4fba8b9Smrg	if (xtermGetWinProp(dpy,
1838d4fba8b9Smrg			    target,
1839d4fba8b9Smrg			    wm_state,
1840d4fba8b9Smrg			    0L,
1841d4fba8b9Smrg			    long_length,
1842d4fba8b9Smrg			    requested_type,
1843d4fba8b9Smrg			    &actual_return_type,
1844d4fba8b9Smrg			    &actual_format_return,
1845d4fba8b9Smrg			    &nitems_return,
1846d4fba8b9Smrg			    &bytes_after_return,
184750027b5bSmrg			    &prop_return)) {
184850027b5bSmrg	    if (prop_return != 0
184950027b5bSmrg		&& actual_return_type == requested_type
185050027b5bSmrg		&& actual_format_return == 32) {
185150027b5bSmrg		unsigned long n;
185250027b5bSmrg		for (n = 0; n < nitems_return; ++n) {
185350027b5bSmrg		    unsigned long check = (((unsigned long *)
185450027b5bSmrg					    (void *) prop_return)[n]);
185550027b5bSmrg		    if (check == is_hidden) {
185650027b5bSmrg			result = True;
185750027b5bSmrg			break;
185850027b5bSmrg		    }
1859d4fba8b9Smrg		}
186050027b5bSmrg		XFree(prop_return);
1861d4fba8b9Smrg	    }
1862d4fba8b9Smrg	}
1863d4fba8b9Smrg    }
1864d4fba8b9Smrg    TRACE(("...window %#lx is%s iconified\n",
1865d4fba8b9Smrg	   target,
1866d4fba8b9Smrg	   result ? "" : " not"));
1867d4fba8b9Smrg    return result;
1868d4fba8b9Smrg}
1869d4fba8b9Smrg
1870d522f475Smrg#if OPT_MAXIMIZE
1871d522f475Smrg/*ARGSUSED*/
1872d522f475Smrgvoid
1873b7c89284SsnjHandleDeIconify(Widget w,
18749a64e1c5Smrg		XEvent *event GCC_UNUSED,
1875fa3f02f3Smrg		String *params GCC_UNUSED,
1876d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1877d522f475Smrg{
1878b7c89284Ssnj    XtermWidget xw;
1879b7c89284Ssnj
1880b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1881d4fba8b9Smrg	xtermDeiconify(xw);
1882d522f475Smrg    }
1883d522f475Smrg}
1884d522f475Smrg
1885d522f475Smrg/*ARGSUSED*/
1886d522f475Smrgvoid
1887b7c89284SsnjHandleIconify(Widget w,
18889a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1889fa3f02f3Smrg	      String *params GCC_UNUSED,
1890d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1891d522f475Smrg{
1892b7c89284Ssnj    XtermWidget xw;
1893b7c89284Ssnj
1894b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1895d4fba8b9Smrg	xtermIconify(xw);
1896d522f475Smrg    }
1897d522f475Smrg}
1898d522f475Smrg
1899d522f475Smrgint
1900c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1901d522f475Smrg{
1902c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1903d522f475Smrg    XSizeHints hints;
1904d522f475Smrg    long supp = 0;
1905d522f475Smrg    Window root_win;
1906d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1907d522f475Smrg    int root_y = -1;
1908d522f475Smrg    unsigned root_border;
1909d522f475Smrg    unsigned root_depth;
19103367019cSmrg    int code;
1911d522f475Smrg
1912d522f475Smrg    if (XGetGeometry(screen->display,
1913c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1914d522f475Smrg		     &root_win,
1915d522f475Smrg		     &root_x,
1916d522f475Smrg		     &root_y,
1917d522f475Smrg		     width,
1918d522f475Smrg		     height,
1919d522f475Smrg		     &root_border,
1920d522f475Smrg		     &root_depth)) {
1921d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1922d522f475Smrg	       root_x,
1923d522f475Smrg	       root_y,
1924d522f475Smrg	       *width,
1925d522f475Smrg	       *height,
1926d522f475Smrg	       root_border));
1927d522f475Smrg
1928d522f475Smrg	*width -= (root_border * 2);
1929d522f475Smrg	*height -= (root_border * 2);
1930d522f475Smrg
1931d522f475Smrg	hints.flags = PMaxSize;
1932d522f475Smrg	if (XGetWMNormalHints(screen->display,
1933c219fbebSmrg			      VShellWindow(xw),
1934d522f475Smrg			      &hints,
1935d522f475Smrg			      &supp)
1936d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1937d522f475Smrg
1938d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1939d522f475Smrg		   hints.max_width,
1940d522f475Smrg		   hints.max_height));
1941d522f475Smrg
1942d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1943b7c89284Ssnj		*width = (unsigned) hints.max_width;
1944d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1945b7c89284Ssnj		*height = (unsigned) hints.max_height;
1946d522f475Smrg	}
19473367019cSmrg	code = 1;
19483367019cSmrg    } else {
19493367019cSmrg	*width = 0;
19503367019cSmrg	*height = 0;
19513367019cSmrg	code = 0;
1952d522f475Smrg    }
19533367019cSmrg    return code;
1954d522f475Smrg}
1955d522f475Smrg
1956d522f475Smrgvoid
1957c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1958d522f475Smrg{
1959c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1960d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1961d4fba8b9Smrg    unsigned root_width = 0, root_height = 0;
19623367019cSmrg    Boolean success = False;
1963d522f475Smrg
19643367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
19653367019cSmrg	   maximize,
19663367019cSmrg	   (maximize
19673367019cSmrg	    ? "maximize"
19683367019cSmrg	    : "restore")));
1969d522f475Smrg
19703367019cSmrg    /*
19713367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
19723367019cSmrg     * as well as the estimated root-window size.
19733367019cSmrg     */
19743367019cSmrg    if (maximize
19753367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
19763367019cSmrg	&& xtermGetWinAttrs(screen->display,
19773367019cSmrg			    WMFrameWindow(xw),
19783367019cSmrg			    &wm_attrs)
19793367019cSmrg	&& xtermGetWinAttrs(screen->display,
19803367019cSmrg			    VShellWindow(xw),
19813367019cSmrg			    &vshell_attrs)) {
19823367019cSmrg
19833367019cSmrg	if (screen->restore_data != True
19843367019cSmrg	    || screen->restore_width != root_width
19853367019cSmrg	    || screen->restore_height != root_height) {
19863367019cSmrg	    screen->restore_data = True;
1987d4fba8b9Smrg	    screen->restore_x = wm_attrs.x;
1988d4fba8b9Smrg	    screen->restore_y = wm_attrs.y;
19893367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
19903367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
19913367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1992d522f475Smrg		   screen->restore_x,
1993d522f475Smrg		   screen->restore_y,
1994d522f475Smrg		   screen->restore_width,
1995d522f475Smrg		   screen->restore_height));
19963367019cSmrg	}
1997d522f475Smrg
19983367019cSmrg	/* subtract wm decoration dimensions */
1999d4fba8b9Smrg	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
2000d4fba8b9Smrg	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
20013367019cSmrg	success = True;
20023367019cSmrg    } else if (screen->restore_data) {
20033367019cSmrg	success = True;
20043367019cSmrg	maximize = 0;
20053367019cSmrg    }
20063367019cSmrg
20073367019cSmrg    if (success) {
20083367019cSmrg	switch (maximize) {
20093367019cSmrg	case 3:
20103367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
20113367019cSmrg	    break;
20123367019cSmrg	case 2:
20133367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
20143367019cSmrg	    break;
20153367019cSmrg	case 1:
20163367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
2017d4fba8b9Smrg	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
2018d4fba8b9Smrg		   0,
2019d4fba8b9Smrg		   0,
2020d4fba8b9Smrg		   root_width,
2021d4fba8b9Smrg		   root_height));
20223367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
2023d4fba8b9Smrg			      0,	/* x */
2024d4fba8b9Smrg			      0,	/* y */
20253367019cSmrg			      root_width,
20263367019cSmrg			      root_height);
20273367019cSmrg	    break;
20283367019cSmrg
20293367019cSmrg	default:
20303367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
20313367019cSmrg	    if (screen->restore_data) {
20323367019cSmrg		screen->restore_data = False;
20333367019cSmrg
2034d4fba8b9Smrg		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
20353367019cSmrg		       screen->restore_x,
20363367019cSmrg		       screen->restore_y,
20373367019cSmrg		       screen->restore_width,
20383367019cSmrg		       screen->restore_height));
20393367019cSmrg
20403367019cSmrg		XMoveResizeWindow(screen->display,
20413367019cSmrg				  VShellWindow(xw),
20423367019cSmrg				  screen->restore_x,
20433367019cSmrg				  screen->restore_y,
20443367019cSmrg				  screen->restore_width,
20453367019cSmrg				  screen->restore_height);
20463367019cSmrg	    }
20473367019cSmrg	    break;
2048d522f475Smrg	}
2049d522f475Smrg    }
2050d522f475Smrg}
2051d522f475Smrg
2052d522f475Smrg/*ARGSUSED*/
2053d522f475Smrgvoid
2054b7c89284SsnjHandleMaximize(Widget w,
20559a64e1c5Smrg	       XEvent *event GCC_UNUSED,
2056fa3f02f3Smrg	       String *params GCC_UNUSED,
2057d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
2058d522f475Smrg{
2059b7c89284Ssnj    XtermWidget xw;
2060b7c89284Ssnj
2061b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2062b7c89284Ssnj	RequestMaximize(xw, 1);
2063d522f475Smrg    }
2064d522f475Smrg}
2065d522f475Smrg
2066d522f475Smrg/*ARGSUSED*/
2067d522f475Smrgvoid
2068b7c89284SsnjHandleRestoreSize(Widget w,
20699a64e1c5Smrg		  XEvent *event GCC_UNUSED,
2070fa3f02f3Smrg		  String *params GCC_UNUSED,
2071d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
2072d522f475Smrg{
2073b7c89284Ssnj    XtermWidget xw;
2074b7c89284Ssnj
2075b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
2076b7c89284Ssnj	RequestMaximize(xw, 0);
2077d522f475Smrg    }
2078d522f475Smrg}
2079d522f475Smrg#endif /* OPT_MAXIMIZE */
2080d522f475Smrg
2081d522f475Smrgvoid
2082d522f475SmrgRedraw(void)
2083d522f475Smrg{
2084d4fba8b9Smrg    XtermWidget xw = term;
2085d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2086d522f475Smrg    XExposeEvent event;
2087d522f475Smrg
2088d522f475Smrg    TRACE(("Redraw\n"));
2089d522f475Smrg
2090d522f475Smrg    event.type = Expose;
2091d522f475Smrg    event.display = screen->display;
2092d522f475Smrg    event.x = 0;
2093d522f475Smrg    event.y = 0;
2094d522f475Smrg    event.count = 0;
2095d522f475Smrg
2096d522f475Smrg    if (VWindow(screen)) {
2097d522f475Smrg	event.window = VWindow(screen);
2098d4fba8b9Smrg	event.width = xw->core.width;
2099d4fba8b9Smrg	event.height = xw->core.height;
2100d4fba8b9Smrg	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
2101d4fba8b9Smrg						     (XEvent *) &event,
2102d4fba8b9Smrg						     NULL);
2103d522f475Smrg	if (ScrollbarWidth(screen)) {
2104d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
21059a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
2106d522f475Smrg	}
2107d522f475Smrg    }
2108d522f475Smrg#if OPT_TEK4014
2109d4fba8b9Smrg    if (TEK4014_SHOWN(xw)) {
2110cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
2111d522f475Smrg	event.window = TWindow(tekscr);
2112d522f475Smrg	event.width = tekWidget->core.width;
2113d522f475Smrg	event.height = tekWidget->core.height;
21149a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
2115d522f475Smrg    }
2116d522f475Smrg#endif
2117d522f475Smrg}
2118d522f475Smrg
2119d522f475Smrg#ifdef VMS
2120d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
2121d522f475Smrg#else
2122d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
2123d522f475Smrg#endif
2124d522f475Smrg
2125d522f475Smrgvoid
2126d522f475Smrgtimestamp_filename(char *dst, const char *src)
2127d522f475Smrg{
2128d522f475Smrg    time_t tstamp;
2129d522f475Smrg    struct tm *tstruct;
2130d522f475Smrg
2131d522f475Smrg    tstamp = time((time_t *) 0);
2132d522f475Smrg    tstruct = localtime(&tstamp);
2133d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
2134d522f475Smrg	    src,
21353367019cSmrg	    (int) tstruct->tm_year + 1900,
2136d522f475Smrg	    tstruct->tm_mon + 1,
2137d522f475Smrg	    tstruct->tm_mday,
2138d522f475Smrg	    tstruct->tm_hour,
2139d522f475Smrg	    tstruct->tm_min,
2140d522f475Smrg	    tstruct->tm_sec);
2141d522f475Smrg}
2142d522f475Smrg
2143d1603babSmrg#if OPT_SCREEN_DUMPS
2144d4fba8b9SmrgFILE *
2145d4fba8b9Smrgcreate_printfile(XtermWidget xw, const char *suffix)
2146d4fba8b9Smrg{
2147d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
2148d4fba8b9Smrg    char fname[1024];
2149d4fba8b9Smrg    int fd;
2150d4fba8b9Smrg    FILE *fp;
2151d4fba8b9Smrg
2152d4fba8b9Smrg#ifdef VMS
2153d4fba8b9Smrg    sprintf(fname, "sys$scratch:xterm%s", suffix);
2154d4fba8b9Smrg#elif defined(HAVE_STRFTIME)
2155d4fba8b9Smrg    {
2156d4fba8b9Smrg	char format[1024];
2157d4fba8b9Smrg	time_t now;
2158d4fba8b9Smrg	struct tm *ltm;
2159d4fba8b9Smrg
2160d4fba8b9Smrg	now = time((time_t *) 0);
2161d4fba8b9Smrg	ltm = localtime(&now);
2162d4fba8b9Smrg
2163d4fba8b9Smrg	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2164d4fba8b9Smrg	if (strftime(fname, sizeof fname, format, ltm) == 0) {
2165d4fba8b9Smrg	    sprintf(fname, "xterm%s", suffix);
2166d4fba8b9Smrg	}
2167d4fba8b9Smrg    }
2168d4fba8b9Smrg#else
2169d4fba8b9Smrg    sprintf(fname, "xterm%s", suffix);
2170d4fba8b9Smrg#endif
2171d4fba8b9Smrg    fd = open_userfile(screen->uid, screen->gid, fname, False);
2172d4fba8b9Smrg    fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2173d4fba8b9Smrg    return fp;
2174d4fba8b9Smrg}
2175d1603babSmrg#endif /* OPT_SCREEN_DUMPS */
2176d4fba8b9Smrg
2177d1603babSmrg#if OPT_SCREEN_DUMPS || defined(ALLOWLOGGING)
2178d522f475Smrgint
2179d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2180d522f475Smrg{
2181d522f475Smrg    int fd;
2182d522f475Smrg    struct stat sb;
2183d522f475Smrg
2184d522f475Smrg#ifdef VMS
2185d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
2186d522f475Smrg	int the_error = errno;
21873367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
21883367019cSmrg		     path,
21893367019cSmrg		     the_error,
21903367019cSmrg		     SysErrorMsg(the_error));
2191d522f475Smrg	return -1;
2192d522f475Smrg    }
2193d522f475Smrg    chown(path, uid, gid);
2194d522f475Smrg#else
2195d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
2196d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
2197d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2198d522f475Smrg	int the_error = errno;
21993367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
22003367019cSmrg		     path,
22013367019cSmrg		     the_error,
22023367019cSmrg		     SysErrorMsg(the_error));
2203d522f475Smrg	return -1;
2204d522f475Smrg    }
2205d522f475Smrg#endif
2206d522f475Smrg
2207d522f475Smrg    /*
2208d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
2209d522f475Smrg     * we do any damage, and that it is not world-writable.
2210d522f475Smrg     */
2211d522f475Smrg    if (fstat(fd, &sb) < 0
2212d522f475Smrg	|| sb.st_uid != uid
2213d522f475Smrg	|| (sb.st_mode & 022) != 0) {
22143367019cSmrg	xtermWarning("you do not own %s\n", path);
2215d522f475Smrg	close(fd);
2216d522f475Smrg	return -1;
2217d522f475Smrg    }
2218d522f475Smrg    return fd;
2219d522f475Smrg}
2220d522f475Smrg
2221d522f475Smrg#ifndef VMS
2222d522f475Smrg/*
2223d522f475Smrg * Create a file only if we could with the permissions of the real user id.
2224d522f475Smrg * We could emulate this with careful use of access() and following
2225d522f475Smrg * symbolic links, but that is messy and has race conditions.
2226d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2227d522f475Smrg * being available.
2228d522f475Smrg *
2229d522f475Smrg * Note: When called for user logging, we have ensured that the real and
2230d522f475Smrg * effective user ids are the same, so this remains as a convenience function
2231d522f475Smrg * for the debug logs.
2232d522f475Smrg *
2233d522f475Smrg * Returns
2234d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
2235d522f475Smrg *	-1 on error, e.g., cannot fork
2236d522f475Smrg *	 0 otherwise.
2237d522f475Smrg */
2238d522f475Smrgint
2239712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2240d522f475Smrg{
2241d522f475Smrg    int fd;
2242d522f475Smrg    pid_t pid;
2243d522f475Smrg    int retval = 0;
2244d522f475Smrg    int childstat = 0;
2245d522f475Smrg#ifndef HAVE_WAITPID
2246d522f475Smrg    int waited;
22473367019cSmrg    void (*chldfunc) (int);
2248d522f475Smrg
2249d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
2250d522f475Smrg#endif /* HAVE_WAITPID */
2251d522f475Smrg
2252d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2253d522f475Smrg	   (int) uid, (int) geteuid(),
2254d522f475Smrg	   (int) gid, (int) getegid(),
2255d522f475Smrg	   append,
2256d522f475Smrg	   pathname,
2257d522f475Smrg	   mode));
2258d522f475Smrg
2259d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
2260d522f475Smrg	fd = open(pathname,
2261d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2262d522f475Smrg		  mode);
2263d522f475Smrg	if (fd >= 0)
2264d522f475Smrg	    close(fd);
2265d522f475Smrg	return (fd >= 0);
2266d522f475Smrg    }
2267d522f475Smrg
2268d522f475Smrg    pid = fork();
2269d522f475Smrg    switch (pid) {
2270d522f475Smrg    case 0:			/* child */
2271d522f475Smrg	if (setgid(gid) == -1
2272d522f475Smrg	    || setuid(uid) == -1) {
2273d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
2274d522f475Smrg	    retval = 1;
2275d522f475Smrg	} else {
2276d522f475Smrg	    fd = open(pathname,
2277d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2278d522f475Smrg		      mode);
2279d522f475Smrg	    if (fd >= 0) {
2280d522f475Smrg		close(fd);
2281d522f475Smrg		retval = 0;
2282d522f475Smrg	    } else {
2283d522f475Smrg		retval = 1;
2284d522f475Smrg	    }
2285d522f475Smrg	}
2286d522f475Smrg	_exit(retval);
2287d522f475Smrg	/* NOTREACHED */
2288d522f475Smrg    case -1:			/* error */
2289d522f475Smrg	return retval;
2290d522f475Smrg    default:			/* parent */
2291d522f475Smrg#ifdef HAVE_WAITPID
2292d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
2293d522f475Smrg#ifdef EINTR
2294d522f475Smrg	    if (errno == EINTR)
2295d522f475Smrg		continue;
2296d522f475Smrg#endif /* EINTR */
2297d522f475Smrg#ifdef ERESTARTSYS
2298d522f475Smrg	    if (errno == ERESTARTSYS)
2299d522f475Smrg		continue;
2300d522f475Smrg#endif /* ERESTARTSYS */
2301d522f475Smrg	    break;
2302d522f475Smrg	}
2303d522f475Smrg#else /* HAVE_WAITPID */
2304d522f475Smrg	waited = wait(&childstat);
2305d522f475Smrg	signal(SIGCHLD, chldfunc);
2306d522f475Smrg	/*
2307d522f475Smrg	   Since we had the signal handler uninstalled for a while,
2308d522f475Smrg	   we might have missed the termination of our screen child.
2309d522f475Smrg	   If we can check for this possibility without hanging, do so.
2310d522f475Smrg	 */
2311d522f475Smrg	do
2312cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
23133367019cSmrg		NormalExit();
2314d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
2315d522f475Smrg#endif /* HAVE_WAITPID */
2316d522f475Smrg#ifndef WIFEXITED
2317d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
2318d522f475Smrg#endif
2319d522f475Smrg	if (WIFEXITED(childstat))
2320d522f475Smrg	    retval = 1;
2321d522f475Smrg	return retval;
2322d522f475Smrg    }
2323d522f475Smrg}
2324d522f475Smrg#endif /* !VMS */
2325d1603babSmrg#endif /* OPT_SCREEN_DUMPS || defined(ALLOWLOGGING) */
2326d522f475Smrg
2327d522f475Smrgint
2328fa3f02f3SmrgxtermResetIds(TScreen *screen)
2329d522f475Smrg{
2330d522f475Smrg    int result = 0;
2331d522f475Smrg    if (setgid(screen->gid) == -1) {
23323367019cSmrg	xtermWarning("unable to reset group-id\n");
2333d522f475Smrg	result = -1;
2334d522f475Smrg    }
2335d522f475Smrg    if (setuid(screen->uid) == -1) {
23363367019cSmrg	xtermWarning("unable to reset user-id\n");
2337d522f475Smrg	result = -1;
2338d522f475Smrg    }
2339d522f475Smrg    return result;
2340d522f475Smrg}
2341d522f475Smrg
2342d522f475Smrg#ifdef ALLOWLOGGING
2343d522f475Smrg
2344d522f475Smrg/*
2345d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
2346d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
2347d522f475Smrg */
2348d522f475Smrg
2349d522f475Smrg#ifdef ALLOWLOGFILEEXEC
23503367019cSmrgstatic void
2351d4fba8b9Smrghandle_SIGPIPE(int sig GCC_UNUSED)
2352d522f475Smrg{
2353cd3331d0Smrg    XtermWidget xw = term;
2354cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2355d522f475Smrg
23563367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2357d522f475Smrg#ifdef SYSV
2358d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2359d522f475Smrg#endif /* SYSV */
2360d522f475Smrg    if (screen->logging)
2361cd3331d0Smrg	CloseLog(xw);
2362d522f475Smrg}
2363d4fba8b9Smrg
2364d4fba8b9Smrg/*
2365d4fba8b9Smrg * Open a command to pipe log data to it.
2366d4fba8b9Smrg * Warning, enabling this "feature" allows arbitrary programs
2367d4fba8b9Smrg * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2368d4fba8b9Smrg * done through escape sequences....  You have been warned.
2369d4fba8b9Smrg */
2370d4fba8b9Smrgstatic void
2371d4fba8b9SmrgStartLogExec(TScreen *screen)
2372d4fba8b9Smrg{
2373d4fba8b9Smrg    int pid;
2374d4fba8b9Smrg    int p[2];
2375d4fba8b9Smrg    static char *shell;
2376d4fba8b9Smrg    struct passwd pw;
2377d4fba8b9Smrg
2378d4fba8b9Smrg    if ((shell = x_getenv("SHELL")) == NULL) {
2379d4fba8b9Smrg
2380d4fba8b9Smrg	if (x_getpwuid(screen->uid, &pw)) {
2381d4fba8b9Smrg	    char *name = x_getlogin(screen->uid, &pw);
2382d4fba8b9Smrg	    if (*(pw.pw_shell)) {
2383d4fba8b9Smrg		shell = pw.pw_shell;
2384d4fba8b9Smrg	    }
2385d4fba8b9Smrg	    free(name);
2386d4fba8b9Smrg	}
2387d4fba8b9Smrg    }
2388d4fba8b9Smrg
2389d4fba8b9Smrg    if (shell == 0) {
2390d4fba8b9Smrg	static char dummy[] = "/bin/sh";
2391d4fba8b9Smrg	shell = dummy;
2392d4fba8b9Smrg    }
2393d4fba8b9Smrg
2394d4fba8b9Smrg    if (access(shell, X_OK) != 0) {
2395d4fba8b9Smrg	xtermPerror("Can't execute `%s'\n", shell);
2396d4fba8b9Smrg	return;
2397d4fba8b9Smrg    }
2398d4fba8b9Smrg
2399d4fba8b9Smrg    if (pipe(p) < 0) {
2400d4fba8b9Smrg	xtermPerror("Can't make a pipe connection\n");
2401d4fba8b9Smrg	return;
2402d4fba8b9Smrg    } else if ((pid = fork()) < 0) {
2403d4fba8b9Smrg	xtermPerror("Can't fork...\n");
2404d4fba8b9Smrg	return;
2405d4fba8b9Smrg    }
2406d4fba8b9Smrg    if (pid == 0) {		/* child */
2407d4fba8b9Smrg	/*
2408d4fba8b9Smrg	 * Close our output (we won't be talking back to the
2409d4fba8b9Smrg	 * parent), and redirect our child's output to the
2410d4fba8b9Smrg	 * original stderr.
2411d4fba8b9Smrg	 */
2412d4fba8b9Smrg	close(p[1]);
2413d4fba8b9Smrg	dup2(p[0], 0);
2414d4fba8b9Smrg	close(p[0]);
2415d4fba8b9Smrg	dup2(fileno(stderr), 1);
2416d4fba8b9Smrg	dup2(fileno(stderr), 2);
2417d4fba8b9Smrg
2418d4fba8b9Smrg	close(fileno(stderr));
2419d4fba8b9Smrg	close(ConnectionNumber(screen->display));
2420d4fba8b9Smrg	close(screen->respond);
2421d4fba8b9Smrg
2422d4fba8b9Smrg	signal(SIGHUP, SIG_DFL);
2423d4fba8b9Smrg	signal(SIGCHLD, SIG_DFL);
2424d4fba8b9Smrg
2425d4fba8b9Smrg	/* (this is redundant) */
2426d4fba8b9Smrg	if (xtermResetIds(screen) < 0)
2427d4fba8b9Smrg	    exit(ERROR_SETUID);
2428d4fba8b9Smrg
2429d4fba8b9Smrg	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2430d4fba8b9Smrg	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2431d4fba8b9Smrg	exit(ERROR_LOGEXEC);
2432d4fba8b9Smrg    }
2433d4fba8b9Smrg    close(p[0]);
2434d4fba8b9Smrg    screen->logfd = p[1];
2435d4fba8b9Smrg    signal(SIGPIPE, handle_SIGPIPE);
2436d4fba8b9Smrg}
2437d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2438d522f475Smrg
2439d4fba8b9Smrg/*
2440d4fba8b9Smrg * Generate a path for a logfile if no default path is given.
2441d4fba8b9Smrg */
2442d4fba8b9Smrgstatic char *
2443d4fba8b9SmrgGenerateLogPath(void)
2444d4fba8b9Smrg{
2445d4fba8b9Smrg    static char *log_default = NULL;
2446d4fba8b9Smrg
2447d4fba8b9Smrg    /* once opened we just reuse the same log name */
2448d4fba8b9Smrg    if (log_default)
2449d4fba8b9Smrg	return (log_default);
2450d4fba8b9Smrg
2451d4fba8b9Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2452d4fba8b9Smrg    {
2453d4fba8b9Smrg#define LEN_HOSTNAME 255
2454d4fba8b9Smrg	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
2455d4fba8b9Smrg	 * the total length of a domain name (i.e., label octets and label
2456d4fba8b9Smrg	 * length octets) is restricted to 255 octets or less.''
2457d4fba8b9Smrg	 */
2458d4fba8b9Smrg#define LEN_GETPID 9
2459d4fba8b9Smrg	/*
2460d4fba8b9Smrg	 * This is arbitrary...
2461d4fba8b9Smrg	 */
2462d4fba8b9Smrg	const char form[] = "Xterm.log.%s%s.%lu";
2463d4fba8b9Smrg	char where[LEN_HOSTNAME + 1];
2464d4fba8b9Smrg	char when[LEN_TIMESTAMP];
2465d4fba8b9Smrg	time_t now = time((time_t *) 0);
2466d4fba8b9Smrg	struct tm *ltm = (struct tm *) localtime(&now);
2467d4fba8b9Smrg
2468d4fba8b9Smrg	if ((gethostname(where, sizeof(where)) == 0) &&
2469d4fba8b9Smrg	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2470d4fba8b9Smrg	    ((log_default = (char *) malloc((sizeof(form)
2471d4fba8b9Smrg					     + strlen(where)
2472d4fba8b9Smrg					     + strlen(when)
2473d4fba8b9Smrg					     + LEN_GETPID))) != NULL)) {
2474d4fba8b9Smrg	    (void) sprintf(log_default,
2475d4fba8b9Smrg			   form,
2476d4fba8b9Smrg			   where, when,
2477d4fba8b9Smrg			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
2478d4fba8b9Smrg	}
2479d4fba8b9Smrg    }
2480d4fba8b9Smrg#else
2481980988aeSmrg    {
2482980988aeSmrg	static const char log_def_name[] = "XtermLog.XXXXXX";
2483980988aeSmrg	if ((log_default = x_strdup(log_def_name)) != NULL) {
2484980988aeSmrg	    MakeTemp(log_default);
2485980988aeSmrg	}
2486d4fba8b9Smrg    }
2487d4fba8b9Smrg#endif
2488d4fba8b9Smrg
2489d4fba8b9Smrg    return (log_default);
2490d4fba8b9Smrg}
2491d4fba8b9Smrg
2492d522f475Smrgvoid
2493cd3331d0SmrgStartLog(XtermWidget xw)
2494d522f475Smrg{
2495cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2496d522f475Smrg
2497d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2498d522f475Smrg	return;
2499d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
2500d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
2501d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2502d522f475Smrg			 0640);
2503d522f475Smrg    if (screen->logfd < 0)
2504d522f475Smrg	return;			/* open failed */
2505d522f475Smrg#else /*VMS */
25063367019cSmrg
2507d4fba8b9Smrg    /* if we weren't supplied with a logfile path, generate one */
2508d4fba8b9Smrg    if (IsEmpty(screen->logfile))
2509d4fba8b9Smrg	screen->logfile = GenerateLogPath();
25103367019cSmrg
2511d4fba8b9Smrg    /* give up if we were unable to allocate the filename */
2512d4fba8b9Smrg    if (!screen->logfile)
2513d4fba8b9Smrg	return;
2514d522f475Smrg
2515d4fba8b9Smrg    if (*screen->logfile == '|') {	/* exec command */
2516d4fba8b9Smrg#ifdef ALLOWLOGFILEEXEC
2517d4fba8b9Smrg	StartLogExec(screen);
2518d522f475Smrg#else
2519cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2520cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2521d522f475Smrg	return;
2522d522f475Smrg#endif
2523d4fba8b9Smrg    } else if (strcmp(screen->logfile, "-") == 0) {
2524d4fba8b9Smrg	screen->logfd = STDOUT_FILENO;
2525d522f475Smrg    } else {
2526d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2527d522f475Smrg					   screen->gid,
2528d522f475Smrg					   screen->logfile,
2529d4fba8b9Smrg					   True)) < 0)
2530d522f475Smrg	    return;
2531d522f475Smrg    }
2532d522f475Smrg#endif /*VMS */
2533d522f475Smrg    screen->logstart = VTbuffer->next;
2534d522f475Smrg    screen->logging = True;
2535d522f475Smrg    update_logging();
2536d522f475Smrg}
2537d522f475Smrg
2538d522f475Smrgvoid
2539cd3331d0SmrgCloseLog(XtermWidget xw)
2540d522f475Smrg{
2541cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2542cd3331d0Smrg
2543d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2544d522f475Smrg	return;
2545cd3331d0Smrg    FlushLog(xw);
2546d522f475Smrg    close(screen->logfd);
2547d522f475Smrg    screen->logging = False;
2548d522f475Smrg    update_logging();
2549d522f475Smrg}
2550d522f475Smrg
2551d522f475Smrgvoid
2552cd3331d0SmrgFlushLog(XtermWidget xw)
2553d522f475Smrg{
2554cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2555cd3331d0Smrg
2556d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2557d522f475Smrg	Char *cp;
2558d1603babSmrg	size_t i;
2559d522f475Smrg
2560d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2561d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2562d522f475Smrg	if (!tt_new_output)
2563d522f475Smrg	    return;
2564d522f475Smrg	tt_new_output = False;
2565d522f475Smrg#endif /* VMS */
2566d522f475Smrg	cp = VTbuffer->next;
2567d522f475Smrg	if (screen->logstart != 0
2568d1603babSmrg	    && (i = (size_t) (cp - screen->logstart)) > 0) {
2569d1603babSmrg	    IGNORE_RC(write(screen->logfd, screen->logstart, i));
2570d522f475Smrg	}
2571d522f475Smrg	screen->logstart = VTbuffer->next;
2572d522f475Smrg    }
2573d522f475Smrg}
2574d522f475Smrg
2575d522f475Smrg#endif /* ALLOWLOGGING */
2576d522f475Smrg
2577d522f475Smrg/***====================================================================***/
2578d522f475Smrg
2579d4fba8b9Smrgstatic unsigned
2580d4fba8b9SmrgmaskToShift(unsigned long mask)
2581d4fba8b9Smrg{
2582d4fba8b9Smrg    unsigned result = 0;
2583d4fba8b9Smrg    if (mask != 0) {
2584d4fba8b9Smrg	while ((mask & 1) == 0) {
2585d4fba8b9Smrg	    mask >>= 1;
2586d4fba8b9Smrg	    ++result;
2587d4fba8b9Smrg	}
2588d4fba8b9Smrg    }
2589d4fba8b9Smrg    return result;
2590d4fba8b9Smrg}
2591d4fba8b9Smrg
2592d4fba8b9Smrgstatic unsigned
2593d4fba8b9SmrgmaskToWidth(unsigned long mask)
2594d4fba8b9Smrg{
2595d4fba8b9Smrg    unsigned result = 0;
2596d4fba8b9Smrg    while (mask != 0) {
2597d4fba8b9Smrg	if ((mask & 1) != 0)
2598d4fba8b9Smrg	    ++result;
2599d4fba8b9Smrg	mask >>= 1;
2600d4fba8b9Smrg    }
2601d4fba8b9Smrg    return result;
2602d4fba8b9Smrg}
2603d4fba8b9Smrg
2604c48a5815SmrgXVisualInfo *
2605fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2606fa3f02f3Smrg{
2607fa3f02f3Smrg#define MYFMT "getVisualInfo \
2608fa3f02f3Smrgdepth %d, \
2609fa3f02f3Smrgtype %d (%s), \
2610fa3f02f3Smrgsize %d \
2611fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2612fa3f02f3Smrg#define MYARG \
2613fa3f02f3Smrg       vi->depth,\
2614fa3f02f3Smrg       vi->class,\
2615fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2616fa3f02f3Smrg       vi->colormap_size,\
2617fa3f02f3Smrg       vi->red_mask,\
2618fa3f02f3Smrg       vi->green_mask,\
2619fa3f02f3Smrg       vi->blue_mask
2620d522f475Smrg
2621fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2622fa3f02f3Smrg    Display *dpy = screen->display;
2623fa3f02f3Smrg    XVisualInfo myTemplate;
2624fa3f02f3Smrg
2625fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2626fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2627fa3f02f3Smrg								XDefaultScreen(dpy)));
2628fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2629fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2630fa3f02f3Smrg
2631fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2632fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2633d4fba8b9Smrg	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2634d4fba8b9Smrg	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2635d4fba8b9Smrg	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2636d4fba8b9Smrg	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2637d4fba8b9Smrg	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2638d4fba8b9Smrg	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2639d4fba8b9Smrg
2640d4fba8b9Smrg	    xw->has_rgb = ((vi->red_mask != 0) &&
2641d4fba8b9Smrg			   (vi->green_mask != 0) &&
2642d4fba8b9Smrg			   (vi->blue_mask != 0) &&
2643d4fba8b9Smrg			   ((vi->red_mask & vi->green_mask) == 0) &&
2644d4fba8b9Smrg			   ((vi->green_mask & vi->blue_mask) == 0) &&
2645c48a5815Smrg			   ((vi->blue_mask & vi->red_mask) == 0) &&
2646d1603babSmrg			   xw->rgb_widths[0] <= (unsigned) vi->bits_per_rgb &&
2647d1603babSmrg			   xw->rgb_widths[1] <= (unsigned) vi->bits_per_rgb &&
2648d1603babSmrg			   xw->rgb_widths[2] <= (unsigned) vi->bits_per_rgb &&
2649c48a5815Smrg			   (vi->class == TrueColor
2650c48a5815Smrg			    || vi->class == DirectColor));
2651d4fba8b9Smrg
2652fa3f02f3Smrg	    if (resource.reportColors) {
2653fa3f02f3Smrg		printf(MYFMT, MYARG);
2654fa3f02f3Smrg	    }
2655fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2656d4fba8b9Smrg	    TRACE(("...shifts %u/%u/%u\n",
2657d4fba8b9Smrg		   xw->rgb_shifts[0],
2658d4fba8b9Smrg		   xw->rgb_shifts[1],
2659d4fba8b9Smrg		   xw->rgb_shifts[2]));
2660c48a5815Smrg	    TRACE(("...widths %u/%u/%u\n",
2661c48a5815Smrg		   xw->rgb_widths[0],
2662c48a5815Smrg		   xw->rgb_widths[1],
2663c48a5815Smrg		   xw->rgb_widths[2]));
2664fa3f02f3Smrg	}
2665fa3f02f3Smrg    }
2666c48a5815Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL;
2667fa3f02f3Smrg#undef MYFMT
2668fa3f02f3Smrg#undef MYARG
2669fa3f02f3Smrg}
26703367019cSmrg
26719a64e1c5Smrg#if OPT_ISO_COLORS
2672d4fba8b9Smrgstatic Bool
2673d4fba8b9SmrgReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
26749a64e1c5Smrg{
2675d4fba8b9Smrg    Bool result = False;
2676d4fba8b9Smrg
26779a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
26789a64e1c5Smrg	XColor color;
26799a64e1c5Smrg	char buffer[80];
26809a64e1c5Smrg
26819a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
26829a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
2683c48a5815Smrg	(void) QueryOneColor(xw, &color);
2684d4fba8b9Smrg	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2685d4fba8b9Smrg		opcode,
2686d4fba8b9Smrg		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
26879a64e1c5Smrg		color.red,
26889a64e1c5Smrg		color.green,
26899a64e1c5Smrg		color.blue);
26909a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
26919a64e1c5Smrg	unparseputs(xw, buffer);
26929a64e1c5Smrg	unparseputc1(xw, final);
2693d4fba8b9Smrg	result = True;
26949a64e1c5Smrg    }
2695d4fba8b9Smrg    return result;
26969a64e1c5Smrg}
26979a64e1c5Smrg
2698fa3f02f3Smrgstatic void
2699fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2700fa3f02f3Smrg{
2701fa3f02f3Smrg    if (getVisualInfo(xw)) {
2702fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2703fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2704fa3f02f3Smrg    } else {
2705fa3f02f3Smrg	*typep = 0;
2706fa3f02f3Smrg	*sizep = 0;
2707fa3f02f3Smrg    }
27083367019cSmrg}
27093367019cSmrg
27103367019cSmrg#define MAX_COLORTABLE 4096
27113367019cSmrg
27123367019cSmrg/*
27133367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
27143367019cSmrg */
27153367019cSmrgstatic Boolean
27163367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
27173367019cSmrg{
27183367019cSmrg    Colormap cmap = xw->core.colormap;
27193367019cSmrg    TScreen *screen = TScreenOf(xw);
2720fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
27213367019cSmrg
2722fa3f02f3Smrg    if (!result
27233367019cSmrg	&& length != 0
27243367019cSmrg	&& length < MAX_COLORTABLE) {
27253367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2726037a25ddSmrg
27273367019cSmrg	if (screen->cmap_data != 0) {
2728037a25ddSmrg	    unsigned i;
2729d4fba8b9Smrg	    unsigned shift;
2730d4fba8b9Smrg
2731d4fba8b9Smrg	    if (getVisualInfo(xw))
2732d4fba8b9Smrg		shift = xw->rgb_shifts[2];
2733d4fba8b9Smrg	    else
2734d4fba8b9Smrg		shift = 0;
2735037a25ddSmrg
27363367019cSmrg	    screen->cmap_size = length;
27373367019cSmrg
27383367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
2739d4fba8b9Smrg		screen->cmap_data[i].pixel = (unsigned long) i << shift;
27403367019cSmrg	    }
27413367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
27423367019cSmrg					     cmap,
27433367019cSmrg					     screen->cmap_data,
27443367019cSmrg					     (int) screen->cmap_size) != 0);
27453367019cSmrg	}
27463367019cSmrg    }
2747d522f475Smrg    return result;
2748d522f475Smrg}
2749d522f475Smrg
2750c48a5815Smrg/***====================================================================***/
2751c48a5815Smrg
2752c48a5815Smrg/*
2753c48a5815Smrg * Call this function with def->{red,green,blue} initialized, to obtain a pixel
2754c48a5815Smrg * value.
2755c48a5815Smrg */
2756c48a5815SmrgBoolean
2757c48a5815SmrgAllocOneColor(XtermWidget xw, XColor *def)
2758c48a5815Smrg{
2759c48a5815Smrg    TScreen *screen = TScreenOf(xw);
2760c48a5815Smrg    Boolean result = True;
2761c48a5815Smrg
2762c48a5815Smrg#define MaskIt(name,nn) \
2763c48a5815Smrg	((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \
2764c48a5815Smrg	             << xw->rgb_shifts[nn]) \
2765c48a5815Smrg	 & xw->visInfo->name ##_mask)
2766c48a5815Smrg
2767d1603babSmrg#define VisualIsRGB(xw) (getVisualInfo(xw) != NULL && xw->has_rgb && xw->visInfo->bits_per_rgb <= 8)
2768d1603babSmrg
2769d1603babSmrg    if (VisualIsRGB(xw)) {
2770c48a5815Smrg	def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2);
2771c48a5815Smrg    } else {
2772c48a5815Smrg	Display *dpy = screen->display;
2773c48a5815Smrg	if (!XAllocColor(dpy, xw->core.colormap, def)) {
2774c48a5815Smrg	    /*
2775c48a5815Smrg	     * Decide between foreground and background by a grayscale
2776c48a5815Smrg	     * approximation.
2777c48a5815Smrg	     */
2778c48a5815Smrg	    int bright = def->red * 3 + def->green * 10 + def->blue;
2779c48a5815Smrg	    int levels = 14 * 0x8000;
2780c48a5815Smrg	    def->pixel = ((bright >= levels)
2781c48a5815Smrg			  ? xw->dft_background
2782c48a5815Smrg			  : xw->dft_foreground);
2783d1603babSmrg	    TRACE(("XAllocColor failed, for %04x/%04x/%04x: choose %08lx (%d vs %d)\n",
2784d1603babSmrg		   def->red, def->green, def->blue,
2785d1603babSmrg		   def->pixel, bright, levels));
2786c48a5815Smrg	    result = False;
2787c48a5815Smrg	}
2788c48a5815Smrg    }
2789c48a5815Smrg    return result;
2790c48a5815Smrg}
2791c48a5815Smrg
2792c48a5815Smrg/***====================================================================***/
2793c48a5815Smrg
2794c48a5815Smrg/*
2795c48a5815Smrg * Call this function with def->pixel set to the color that we want to convert
2796c48a5815Smrg * to separate red/green/blue.
2797c48a5815Smrg */
2798c48a5815SmrgBoolean
2799c48a5815SmrgQueryOneColor(XtermWidget xw, XColor *def)
2800c48a5815Smrg{
2801c48a5815Smrg    Boolean result = True;
2802c48a5815Smrg
2803c48a5815Smrg#define UnMaskIt(name,nn) \
2804c48a5815Smrg	((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn]))
2805c48a5815Smrg#define UnMaskIt2(name,nn) \
2806c48a5815Smrg	(unsigned short)((((UnMaskIt(name,nn) << 8) \
2807c48a5815Smrg			   |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn]))
2808c48a5815Smrg
2809d1603babSmrg    if (VisualIsRGB(xw)) {
2810c48a5815Smrg	/* *INDENT-EQLS* */
2811c48a5815Smrg	def->red   = UnMaskIt2(red, 0);
2812c48a5815Smrg	def->green = UnMaskIt2(green, 1);
2813c48a5815Smrg	def->blue  = UnMaskIt2(blue, 2);
2814d1603babSmrg    } else {
2815d1603babSmrg	Display *dpy = TScreenOf(xw)->display;
2816d1603babSmrg	if (!XQueryColor(dpy, xw->core.colormap, def)) {
2817d1603babSmrg	    TRACE(("XQueryColor failed, given %08lx\n", def->pixel));
2818d1603babSmrg	    result     = False;
2819d1603babSmrg	}
2820c48a5815Smrg    }
2821c48a5815Smrg    return result;
2822c48a5815Smrg}
2823c48a5815Smrg
2824c48a5815Smrg/***====================================================================***/
2825c48a5815Smrg
2826d522f475Smrg/*
2827d522f475Smrg * Find closest color for "def" in "cmap".
2828d522f475Smrg * Set "def" to the resulting color.
2829d522f475Smrg *
2830d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2831d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2832d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2833d522f475Smrg *
2834d522f475Smrg * Return False if not able to find or allocate a color.
2835d522f475Smrg */
2836d522f475Smrgstatic Boolean
2837c48a5815SmrgallocateClosestRGB(XtermWidget xw, XColor *def)
2838d522f475Smrg{
28393367019cSmrg    TScreen *screen = TScreenOf(xw);
2840d522f475Smrg    Boolean result = False;
28413367019cSmrg    unsigned cmap_type;
2842d522f475Smrg    unsigned cmap_size;
2843d522f475Smrg
2844fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2845d522f475Smrg
28463367019cSmrg    if ((cmap_type & 1) != 0) {
28473367019cSmrg
28483367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2849037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2850d522f475Smrg
2851d522f475Smrg	    if (tried != 0) {
2852037a25ddSmrg		unsigned attempts;
2853d522f475Smrg
2854d522f475Smrg		/*
2855d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2856d522f475Smrg		 * approximation to the requested color.
2857d522f475Smrg		 */
2858d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2859d522f475Smrg		    Boolean first = True;
2860037a25ddSmrg		    double bestRGB = 0.0;
2861037a25ddSmrg		    unsigned bestInx = 0;
2862037a25ddSmrg		    unsigned i;
2863d522f475Smrg
2864d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2865d522f475Smrg			if (!tried[bestInx]) {
2866037a25ddSmrg			    double diff, thisRGB = 0.0;
2867037a25ddSmrg
2868d522f475Smrg			    /*
2869d522f475Smrg			     * Look for the best match based on luminance.
2870d522f475Smrg			     * Measure this by the least-squares difference of
2871d522f475Smrg			     * the weighted R/G/B components from the color map
2872d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2873d522f475Smrg			     * component of the YIQ color space model for
2874d522f475Smrg			     * weights that correspond to the luminance.
2875d522f475Smrg			     */
2876d522f475Smrg#define AddColorWeight(weight, color) \
28773367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2878037a25ddSmrg			    thisRGB += diff * diff
2879d522f475Smrg
2880d522f475Smrg			    AddColorWeight(0.30, red);
2881d522f475Smrg			    AddColorWeight(0.61, green);
2882d522f475Smrg			    AddColorWeight(0.11, blue);
2883d522f475Smrg
2884d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2885d522f475Smrg				first = False;
2886d522f475Smrg				bestInx = i;
2887d522f475Smrg				bestRGB = thisRGB;
2888d522f475Smrg			    }
2889d522f475Smrg			}
2890d522f475Smrg		    }
2891c48a5815Smrg		    if (AllocOneColor(xw, &screen->cmap_data[bestInx])) {
28923367019cSmrg			*def = screen->cmap_data[bestInx];
28933367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
28943367019cSmrg			       def->green, def->blue));
2895d522f475Smrg			result = True;
2896d522f475Smrg			break;
2897d522f475Smrg		    }
2898d522f475Smrg		    /*
2899d522f475Smrg		     * It failed - either the color map entry was readonly, or
2900d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2901d522f475Smrg		     * so we will ignore it
2902d522f475Smrg		     */
2903d522f475Smrg		    tried[bestInx] = True;
2904d522f475Smrg		}
2905d522f475Smrg		free(tried);
2906d522f475Smrg	    }
2907d522f475Smrg	}
2908d522f475Smrg    }
2909d522f475Smrg    return result;
2910d522f475Smrg}
2911d522f475Smrg
29123367019cSmrg#ifndef ULONG_MAX
29133367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
29143367019cSmrg#endif
29153367019cSmrg
2916d522f475Smrg/*
2917d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2918d522f475Smrg * to 256.
2919d522f475Smrg *
2920d522f475Smrg * Returns
2921d522f475Smrg *	-1 on error
2922d522f475Smrg *	0 on no change
2923d522f475Smrg *	1 if a new color was allocated.
2924d522f475Smrg */
2925d522f475Smrgstatic int
2926d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2927d522f475Smrg		  ColorRes * res,
2928cd3331d0Smrg		  const char *spec)
2929d522f475Smrg{
2930d522f475Smrg    int result;
2931d522f475Smrg    XColor def;
2932d522f475Smrg
29333367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2934c48a5815Smrg	if (res->mode == True &&
2935c48a5815Smrg	    EQL_COLOR_RES(res, def.pixel)) {
2936d522f475Smrg	    result = 0;
2937d522f475Smrg	} else {
2938d522f475Smrg	    result = 1;
2939d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
29403367019cSmrg	    res->red = def.red;
29413367019cSmrg	    res->green = def.green;
29423367019cSmrg	    res->blue = def.blue;
29433367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
29443367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
29453367019cSmrg		   def.red,
29463367019cSmrg		   def.green,
29473367019cSmrg		   def.blue,
29483367019cSmrg		   def.pixel));
2949d522f475Smrg	    if (!res->mode)
2950d522f475Smrg		result = 0;
2951d522f475Smrg	    res->mode = True;
2952d522f475Smrg	}
2953d522f475Smrg    } else {
2954d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2955d522f475Smrg	result = -1;
2956d522f475Smrg    }
2957d522f475Smrg    return (result);
2958d522f475Smrg}
2959d522f475Smrg
2960d522f475SmrgPixel
2961cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2962d522f475Smrg{
2963d522f475Smrg    Pixel result = 0;
2964d522f475Smrg
2965d522f475Smrg    if (res->mode) {
2966d522f475Smrg	result = res->value;
2967d522f475Smrg    } else {
2968d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2969cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2970d522f475Smrg
2971cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2972cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2973d522f475Smrg
2974cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2975cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2976d522f475Smrg		res->mode = -True;
29773367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
29783367019cSmrg			     NonNull(res->resource));
2979d522f475Smrg	    }
2980d522f475Smrg	    result = res->value;
2981d522f475Smrg	} else {
2982d522f475Smrg	    result = 0;
2983d522f475Smrg	}
2984d522f475Smrg    }
2985d522f475Smrg    return result;
2986d522f475Smrg}
2987d522f475Smrg
2988cd3331d0Smrgstatic int
2989cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2990cd3331d0Smrg{
2991cd3331d0Smrg    int code;
2992cd3331d0Smrg
2993cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2994cd3331d0Smrg	code = -1;
2995cd3331d0Smrg    } else {
2996cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2997cd3331d0Smrg
2998cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2999cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
3000cd3331d0Smrg    }
3001cd3331d0Smrg    return code;
3002cd3331d0Smrg}
3003cd3331d0Smrg
3004cd3331d0Smrg/*
3005cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
3006cd3331d0Smrg * values from the given buffer.
3007cd3331d0Smrg *
3008cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
3009cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
3010cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
3011cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
3012cd3331d0Smrg * 'first' set to the beginning of those indices.
3013cd3331d0Smrg *
3014cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
3015cd3331d0Smrg */
3016d522f475Smrgstatic Bool
3017d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
3018d4fba8b9Smrg		       int opcode,
3019d522f475Smrg		       char *buf,
3020cd3331d0Smrg		       int first,
3021d522f475Smrg		       int final)
3022d522f475Smrg{
3023d522f475Smrg    int repaint = False;
3024d522f475Smrg    int code;
3025cd3331d0Smrg    int last = (MAXCOLORS - first);
3026d4fba8b9Smrg    int queried = 0;
3027d522f475Smrg
3028d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
3029d522f475Smrg
3030d522f475Smrg    while (buf && *buf) {
3031037a25ddSmrg	int color;
3032037a25ddSmrg	char *name = strchr(buf, ';');
3033037a25ddSmrg
3034d522f475Smrg	if (name == NULL)
3035d522f475Smrg	    break;
3036d522f475Smrg	*name = '\0';
3037d522f475Smrg	name++;
3038d522f475Smrg	color = atoi(buf);
3039cd3331d0Smrg	if (color < 0 || color >= last)
3040cd3331d0Smrg	    break;		/* quit on any error */
3041d522f475Smrg	buf = strchr(name, ';');
3042d522f475Smrg	if (buf) {
3043d522f475Smrg	    *buf = '\0';
3044d522f475Smrg	    buf++;
3045d522f475Smrg	}
3046cd3331d0Smrg	if (!strcmp(name, "?")) {
3047d4fba8b9Smrg	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3048d4fba8b9Smrg		++queried;
3049cd3331d0Smrg	} else {
3050cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
3051d522f475Smrg	    if (code < 0) {
3052d522f475Smrg		/* stop on any error */
3053d522f475Smrg		break;
3054d522f475Smrg	    } else if (code > 0) {
3055d522f475Smrg		repaint = True;
3056d522f475Smrg	    }
3057d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
3058d522f475Smrg	     * change style (dynamic colors).
3059d522f475Smrg	     */
3060d522f475Smrg	}
3061d522f475Smrg    }
3062d4fba8b9Smrg    if (queried)
3063d4fba8b9Smrg	unparse_end(xw);
3064d522f475Smrg
3065d522f475Smrg    return (repaint);
3066d522f475Smrg}
3067cd3331d0Smrg
3068cd3331d0Smrgstatic Bool
3069cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
3070cd3331d0Smrg{
3071cd3331d0Smrg    Bool repaint = False;
3072cd3331d0Smrg    int last = MAXCOLORS - start;
3073cd3331d0Smrg
3074cd3331d0Smrg    if (color >= 0 && color < last) {
3075cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3076cd3331d0Smrg
3077cd3331d0Smrg	if (res->mode) {
3078cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
3079cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3080cd3331d0Smrg		repaint = True;
3081cd3331d0Smrg	    }
3082cd3331d0Smrg	}
3083cd3331d0Smrg    }
3084cd3331d0Smrg    return repaint;
3085cd3331d0Smrg}
3086cd3331d0Smrg
3087cd3331d0Smrgint
3088cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3089cd3331d0Smrg{
3090cd3331d0Smrg    int repaint = 0;
3091cd3331d0Smrg    int color;
3092cd3331d0Smrg
3093cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3094cd3331d0Smrg    if (*buf != '\0') {
3095cd3331d0Smrg	/* reset specific colors */
3096cd3331d0Smrg	while (!IsEmpty(buf)) {
3097cd3331d0Smrg	    char *next;
3098cd3331d0Smrg
3099037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
3100037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
3101cd3331d0Smrg		break;		/* no number at all */
3102cd3331d0Smrg	    if (next != 0) {
3103cd3331d0Smrg		if (strchr(";", *next) == 0)
3104cd3331d0Smrg		    break;	/* unexpected delimiter */
3105cd3331d0Smrg		++next;
3106cd3331d0Smrg	    }
3107cd3331d0Smrg
3108cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3109cd3331d0Smrg		++repaint;
3110cd3331d0Smrg	    }
3111cd3331d0Smrg	    buf = next;
3112cd3331d0Smrg	}
3113cd3331d0Smrg    } else {
3114cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3115cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
3116cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3117cd3331d0Smrg		++repaint;
3118cd3331d0Smrg	    }
3119cd3331d0Smrg	}
3120cd3331d0Smrg    }
3121cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3122cd3331d0Smrg    return repaint;
3123cd3331d0Smrg}
3124d522f475Smrg#else
3125c48a5815Smrg#define allocateClosestRGB(xw, def) 0
3126d522f475Smrg#endif /* OPT_ISO_COLORS */
3127d522f475Smrg
3128fa3f02f3SmrgBoolean
31299a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
3130fa3f02f3Smrg{
3131c48a5815Smrg    (void) xw;
3132c48a5815Smrg    (void) def;
3133c48a5815Smrg    return AllocOneColor(xw, def) || allocateClosestRGB(xw, def);
3134fa3f02f3Smrg}
3135fa3f02f3Smrg
31363367019cSmrgstatic Boolean
31379a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
31383367019cSmrg{
31393367019cSmrg    Boolean result = False;
31403367019cSmrg    TScreen *screen = TScreenOf(xw);
31413367019cSmrg    Colormap cmap = xw->core.colormap;
31428f44fb3bSmrg    size_t have = strlen(spec);
31433367019cSmrg
31448f44fb3bSmrg    if (have == 0 || have > MAX_U_STRING) {
31458f44fb3bSmrg	if (resource.reportColors) {
3146c48a5815Smrg	    printf("color  (ignored, length %lu)\n", (unsigned long) have);
31478f44fb3bSmrg	}
31488f44fb3bSmrg    } else if (XParseColor(screen->display, cmap, spec, def)) {
3149fa3f02f3Smrg	XColor save_def = *def;
3150fa3f02f3Smrg	if (resource.reportColors) {
3151fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3152fa3f02f3Smrg		   def->red, def->green, def->blue,
3153fa3f02f3Smrg		   spec);
3154fa3f02f3Smrg	}
3155fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
3156fa3f02f3Smrg	    if (resource.reportColors) {
3157fa3f02f3Smrg		if (def->red != save_def.red ||
3158fa3f02f3Smrg		    def->green != save_def.green ||
3159fa3f02f3Smrg		    def->blue != save_def.blue) {
3160fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3161fa3f02f3Smrg			   def->red, def->green, def->blue,
3162fa3f02f3Smrg			   spec);
3163fa3f02f3Smrg		}
3164fa3f02f3Smrg	    }
3165fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3166fa3f02f3Smrg		   def->red, def->green, def->blue));
3167fa3f02f3Smrg	    result = True;
3168fa3f02f3Smrg	}
31693367019cSmrg    }
31703367019cSmrg    return result;
31713367019cSmrg}
31723367019cSmrg
31733367019cSmrg/*
31743367019cSmrg * This provides an approximation (the closest color from xterm's palette)
31753367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
31763367019cSmrg * because of the context in which it is used.
31773367019cSmrg */
31783367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
31793367019cSmrgint
31803367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
31813367019cSmrg{
31823367019cSmrg    int result = -1;
3183c48a5815Smrg#if OPT_ISO_COLORS
31843367019cSmrg    int n;
31853367019cSmrg    int best_index = -1;
31863367019cSmrg    unsigned long best_value = 0;
31873367019cSmrg    unsigned long this_value;
31883367019cSmrg    long diff_red, diff_green, diff_blue;
31893367019cSmrg
31903367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
31913367019cSmrg
31923367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
31933367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
31943367019cSmrg
31953367019cSmrg	/* ensure that we have a value for each of the colors */
31963367019cSmrg	if (!res->mode) {
31973367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
31983367019cSmrg	}
31993367019cSmrg
32003367019cSmrg	/* find the closest match */
32013367019cSmrg	if (res->mode == True) {
32023367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
32033367019cSmrg		    res->value, res->red, res->green, res->blue));
32043367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
32053367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
32063367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
32073367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
32083367019cSmrg					  + (diff_green * diff_green)
32093367019cSmrg					  + (diff_blue * diff_blue));
32103367019cSmrg	    if (best_index < 0 || this_value < best_value) {
32113367019cSmrg		best_index = n;
32123367019cSmrg		best_value = this_value;
32133367019cSmrg	    }
32143367019cSmrg	}
32153367019cSmrg    }
32163367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
32173367019cSmrg    result = best_index;
3218c48a5815Smrg
32193367019cSmrg#else
32203367019cSmrg    (void) xw;
32213367019cSmrg    (void) find_red;
32223367019cSmrg    (void) find_green;
32233367019cSmrg    (void) find_blue;
32243367019cSmrg#endif
32253367019cSmrg    return result;
32263367019cSmrg}
32273367019cSmrg
3228d4fba8b9Smrg#if OPT_DIRECT_COLOR
3229d4fba8b9Smrgint
3230d4fba8b9SmrggetDirectColor(XtermWidget xw, int red, int green, int blue)
3231d4fba8b9Smrg{
3232c48a5815Smrg    Pixel result = 0;
3233c48a5815Smrg
3234c48a5815Smrg#define getRGB(name,shift) \
3235c48a5815Smrg    do { \
3236c48a5815Smrg	Pixel value = (Pixel) name & 0xff; \
3237c48a5815Smrg	if (xw->rgb_widths[shift] < 8) { \
3238c48a5815Smrg	    value >>= (int) (8 - xw->rgb_widths[shift]); \
3239c48a5815Smrg	} \
3240c48a5815Smrg	value <<= xw->rgb_shifts[shift]; \
3241c48a5815Smrg	value &= xw->visInfo->name ##_mask; \
3242c48a5815Smrg	result |= value; \
3243c48a5815Smrg    } while (0)
3244c48a5815Smrg
3245c48a5815Smrg    getRGB(red, 0);
3246c48a5815Smrg    getRGB(green, 1);
3247c48a5815Smrg    getRGB(blue, 2);
3248c48a5815Smrg
3249c48a5815Smrg#undef getRGB
3250c48a5815Smrg
3251d4fba8b9Smrg    return (int) result;
3252d4fba8b9Smrg}
3253d4fba8b9Smrg
3254d4fba8b9Smrgstatic void
3255d4fba8b9SmrgformatDirectColor(char *target, XtermWidget xw, unsigned value)
3256d4fba8b9Smrg{
3257c48a5815Smrg    Pixel result[3];
3258c48a5815Smrg
3259c48a5815Smrg#define getRGB(name, shift) \
3260c48a5815Smrg    do { \
3261c48a5815Smrg	result[shift] = value & xw->visInfo->name ## _mask; \
3262c48a5815Smrg	result[shift] >>= xw->rgb_shifts[shift]; \
3263c48a5815Smrg	if (xw->rgb_widths[shift] < 8) \
3264c48a5815Smrg	    result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \
3265c48a5815Smrg    } while(0)
3266c48a5815Smrg
3267c48a5815Smrg    getRGB(red, 0);
3268c48a5815Smrg    getRGB(green, 1);
3269c48a5815Smrg    getRGB(blue, 2);
3270c48a5815Smrg
3271c48a5815Smrg#undef getRGB
3272c48a5815Smrg
3273c48a5815Smrg    sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]);
3274d4fba8b9Smrg}
3275d4fba8b9Smrg#endif /* OPT_DIRECT_COLOR */
3276d4fba8b9Smrg
3277d4fba8b9Smrg#define fg2SGR(n) \
3278d4fba8b9Smrg		(n) >= 8 ? 9 : 3, \
3279d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3280d4fba8b9Smrg#define bg2SGR(n) \
3281d4fba8b9Smrg		(n) >= 8 ? 10 : 4, \
3282d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3283d4fba8b9Smrg
3284d4fba8b9Smrg#define EndOf(s) (s) + strlen(s)
3285d4fba8b9Smrg
3286d4fba8b9Smrgchar *
3287d4fba8b9SmrgxtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3288d4fba8b9Smrg{
3289d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
3290d4fba8b9Smrg    char *msg = target;
3291d4fba8b9Smrg
3292d4fba8b9Smrg    strcpy(target, "0");
3293d4fba8b9Smrg    if (attr & BOLD)
3294d4fba8b9Smrg	strcat(msg, ";1");
3295d4fba8b9Smrg    if (attr & UNDERLINE)
3296d4fba8b9Smrg	strcat(msg, ";4");
3297d4fba8b9Smrg    if (attr & BLINK)
3298d4fba8b9Smrg	strcat(msg, ";5");
3299d4fba8b9Smrg    if (attr & INVERSE)
3300d4fba8b9Smrg	strcat(msg, ";7");
3301d4fba8b9Smrg    if (attr & INVISIBLE)
3302d4fba8b9Smrg	strcat(msg, ";8");
3303d4fba8b9Smrg#if OPT_WIDE_ATTRS
3304d4fba8b9Smrg    if (attr & ATR_FAINT)
3305d4fba8b9Smrg	strcat(msg, ";2");
3306d4fba8b9Smrg    if (attr & ATR_ITALIC)
3307d4fba8b9Smrg	strcat(msg, ";3");
3308d4fba8b9Smrg    if (attr & ATR_STRIKEOUT)
3309d4fba8b9Smrg	strcat(msg, ";9");
3310d4fba8b9Smrg    if (attr & ATR_DBL_UNDER)
3311d4fba8b9Smrg	strcat(msg, ";21");
3312d4fba8b9Smrg#endif
3313d4fba8b9Smrg#if OPT_256_COLORS || OPT_88_COLORS
3314d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3315d4fba8b9Smrg	if (attr & FG_COLOR) {
3316d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3317d4fba8b9Smrg		strcat(msg, ";38:2::");
3318d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3319d4fba8b9Smrg	    }) if (fg >= 16) {
3320d4fba8b9Smrg		sprintf(EndOf(msg), ";38:5:%d", fg);
3321d4fba8b9Smrg	    } else {
3322d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3323d4fba8b9Smrg	    }
3324d4fba8b9Smrg	}
3325d4fba8b9Smrg	if (attr & BG_COLOR) {
3326d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3327d4fba8b9Smrg		strcat(msg, ";48:2::");
3328d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3329d4fba8b9Smrg	    }) if (bg >= 16) {
3330d4fba8b9Smrg		sprintf(EndOf(msg), ";48:5:%d", bg);
3331d4fba8b9Smrg	    } else {
3332d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3333d4fba8b9Smrg	    }
3334d4fba8b9Smrg	}
3335d4fba8b9Smrg    });
3336d4fba8b9Smrg#elif OPT_ISO_COLORS
3337d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3338d4fba8b9Smrg	if (attr & FG_COLOR) {
3339d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3340d4fba8b9Smrg	}
3341d4fba8b9Smrg	if (attr & BG_COLOR) {
3342d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3343d4fba8b9Smrg	}
3344d4fba8b9Smrg    });
3345d4fba8b9Smrg#else
3346d4fba8b9Smrg    (void) screen;
3347d4fba8b9Smrg    (void) fg;
3348d4fba8b9Smrg    (void) bg;
3349d4fba8b9Smrg#endif
3350d4fba8b9Smrg    return target;
3351d4fba8b9Smrg}
3352d4fba8b9Smrg
3353d522f475Smrg#if OPT_PASTE64
3354d522f475Smrgstatic void
3355fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3356d522f475Smrg{
3357d522f475Smrg#define PDATA(a,b) { a, #b }
3358d522f475Smrg    static struct {
3359d522f475Smrg	char given;
3360cd3331d0Smrg	String result;
3361d522f475Smrg    } table[] = {
3362d522f475Smrg	PDATA('s', SELECT),
3363d522f475Smrg	    PDATA('p', PRIMARY),
3364d4fba8b9Smrg	    PDATA('q', SECONDARY),
3365d522f475Smrg	    PDATA('c', CLIPBOARD),
3366d522f475Smrg	    PDATA('0', CUT_BUFFER0),
3367d522f475Smrg	    PDATA('1', CUT_BUFFER1),
3368d522f475Smrg	    PDATA('2', CUT_BUFFER2),
3369d522f475Smrg	    PDATA('3', CUT_BUFFER3),
3370d522f475Smrg	    PDATA('4', CUT_BUFFER4),
3371d522f475Smrg	    PDATA('5', CUT_BUFFER5),
3372d522f475Smrg	    PDATA('6', CUT_BUFFER6),
3373d522f475Smrg	    PDATA('7', CUT_BUFFER7),
3374d522f475Smrg    };
3375d1603babSmrg    char target_used[XtNumber(table)];
3376d1603babSmrg    char select_code[XtNumber(table) + 1];
3377d1603babSmrg    String select_args[XtNumber(table) + 1];
3378d522f475Smrg
3379cd3331d0Smrg    const char *base = buf;
3380d1603babSmrg    Cardinal j;
3381d1603babSmrg    Cardinal num_targets = 0;
3382d522f475Smrg
3383d522f475Smrg    TRACE(("Manipulate selection data\n"));
3384d522f475Smrg
3385d1603babSmrg    memset(target_used, 0, sizeof(target_used));
3386d522f475Smrg    while (*buf != ';' && *buf != '\0') {
3387d522f475Smrg	++buf;
3388d522f475Smrg    }
3389d522f475Smrg
3390d522f475Smrg    if (*buf == ';') {
3391037a25ddSmrg
3392d522f475Smrg	*buf++ = '\0';
3393d522f475Smrg	if (*base == '\0')
3394d522f475Smrg	    base = "s0";
3395d522f475Smrg
3396d1603babSmrg	while (*base != '\0') {
3397d1603babSmrg	    for (j = 0; j < XtNumber(table); ++j) {
3398d1603babSmrg		if (*base == table[j].given) {
3399d1603babSmrg		    if (!target_used[j]) {
3400d1603babSmrg			target_used[j] = 1;
3401d1603babSmrg			select_code[num_targets] = *base;
3402d1603babSmrg			select_args[num_targets++] = table[j].result;
3403d1603babSmrg			TRACE(("atom[%d] %s\n", num_targets, table[j].result));
34043367019cSmrg		    }
3405d1603babSmrg		    break;
34063367019cSmrg		}
3407d1603babSmrg	    }
3408d1603babSmrg	    ++base;
3409d1603babSmrg	}
3410d1603babSmrg	select_code[num_targets] = '\0';
3411d1603babSmrg
3412d1603babSmrg	if (!strcmp(buf, "?")) {
3413d1603babSmrg	    if (AllowWindowOps(xw, ewGetSelection)) {
3414d1603babSmrg		TRACE(("Getting selection\n"));
3415d1603babSmrg		unparseputc1(xw, ANSI_OSC);
3416d1603babSmrg		unparseputs(xw, "52");
3417d1603babSmrg		unparseputc(xw, ';');
3418d1603babSmrg
3419d1603babSmrg		unparseputs(xw, select_code);
3420d1603babSmrg		unparseputc(xw, ';');
3421d1603babSmrg
3422d1603babSmrg		/* Tell xtermGetSelection data is base64 encoded */
3423d1603babSmrg		screen->base64_paste = num_targets;
3424d1603babSmrg		screen->base64_final = final;
3425d1603babSmrg
3426d1603babSmrg		screen->selection_time =
3427d1603babSmrg		    XtLastTimestampProcessed(TScreenOf(xw)->display);
3428d1603babSmrg
3429d1603babSmrg		/* terminator will be written in this call */
3430d1603babSmrg		xtermGetSelection((Widget) xw,
3431d1603babSmrg				  screen->selection_time,
3432d1603babSmrg				  select_args, num_targets,
3433d1603babSmrg				  NULL);
3434d1603babSmrg	    }
3435d1603babSmrg	} else {
3436d1603babSmrg	    if (AllowWindowOps(xw, ewSetSelection)) {
3437d1603babSmrg		char *old = buf;
3438d1603babSmrg
3439d1603babSmrg		TRACE(("Setting selection(%s) with %s\n", select_code, buf));
3440d1603babSmrg		screen->selection_time =
3441d1603babSmrg		    XtLastTimestampProcessed(TScreenOf(xw)->display);
3442d1603babSmrg
3443d1603babSmrg		for (j = 0; j < num_targets; ++j) {
3444d1603babSmrg		    buf = old;
3445d1603babSmrg		    ClearSelectionBuffer(screen, select_args[j]);
3446d1603babSmrg		    while (*buf != '\0') {
3447d1603babSmrg			AppendToSelectionBuffer(screen,
3448d1603babSmrg						CharOf(*buf++),
3449d1603babSmrg						select_args[j]);
34503367019cSmrg		    }
34513367019cSmrg		}
3452d1603babSmrg		CompleteSelection(xw, select_args, num_targets);
3453cd3331d0Smrg	    }
3454d522f475Smrg	}
3455d522f475Smrg    }
3456d522f475Smrg}
3457d522f475Smrg#endif /* OPT_PASTE64 */
3458d522f475Smrg
3459d522f475Smrg/***====================================================================***/
3460d522f475Smrg
3461d4fba8b9Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3462d4fba8b9Smrg			 || (xw->screen.utf8_title) \
3463d4fba8b9Smrg			 || (xw->screen.c1_printable))
3464cd3331d0Smrg
3465d522f475Smrgstatic Bool
3466fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3467d522f475Smrg{
3468cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3469d522f475Smrg    Bool result = False;
3470d522f475Smrg    Char *cp = *bufp;
3471d522f475Smrg    Char *next = cp;
3472d522f475Smrg
3473d522f475Smrg    (void) screen;
3474d522f475Smrg    (void) last;
3475d522f475Smrg
3476d522f475Smrg#if OPT_WIDE_CHARS
3477cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3478d522f475Smrg	PtyData data;
3479d522f475Smrg
34809a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3481980988aeSmrg	    if (!is_UCS_SPECIAL(data.utf_data)
3482d522f475Smrg		&& (data.utf_data >= 128 ||
3483d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3484d522f475Smrg		next += (data.utf_size - 1);
3485d522f475Smrg		result = True;
3486d522f475Smrg	    } else {
3487d522f475Smrg		result = False;
3488d522f475Smrg	    }
3489d522f475Smrg	} else {
3490d522f475Smrg	    result = False;
3491d522f475Smrg	}
3492d522f475Smrg    } else
3493d522f475Smrg#endif
3494d522f475Smrg#if OPT_C1_PRINT
3495d522f475Smrg	if (screen->c1_printable
3496d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3497d522f475Smrg	result = True;
3498d522f475Smrg    } else
3499d522f475Smrg#endif
3500d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3501d522f475Smrg	result = True;
3502d522f475Smrg    }
3503d522f475Smrg    *bufp = next;
3504d522f475Smrg    return result;
3505d522f475Smrg}
3506d522f475Smrg
3507d522f475Smrg/***====================================================================***/
3508d522f475Smrg
3509d522f475Smrg/*
3510d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3511cd3331d0Smrg * array indices.  Compare with TermColors.
3512d522f475Smrg */
3513d522f475Smrgtypedef enum {
3514d522f475Smrg    OSC_TEXT_FG = 10
3515d522f475Smrg    ,OSC_TEXT_BG
3516d522f475Smrg    ,OSC_TEXT_CURSOR
3517d522f475Smrg    ,OSC_MOUSE_FG
3518d522f475Smrg    ,OSC_MOUSE_BG
3519d522f475Smrg#if OPT_TEK4014
3520d522f475Smrg    ,OSC_TEK_FG = 15
3521d522f475Smrg    ,OSC_TEK_BG
3522d522f475Smrg#endif
3523d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3524d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3525d522f475Smrg#endif
3526d522f475Smrg#if OPT_TEK4014
3527d522f475Smrg    ,OSC_TEK_CURSOR = 18
3528d522f475Smrg#endif
3529d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3530d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3531d522f475Smrg#endif
3532d522f475Smrg    ,OSC_NCOLORS
3533d522f475Smrg} OscTextColors;
3534d522f475Smrg
3535cd3331d0Smrg/*
3536cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3537cd3331d0Smrg */
3538cd3331d0Smrg#define OSC_RESET 100
3539cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3540cd3331d0Smrg
3541d1603babSmrg/*
3542d1603babSmrg * Other (non-color) OSC controls
3543d1603babSmrg */
3544d1603babSmrgtypedef enum {
3545d1603babSmrg    OSC_IconBoth = 0
3546d1603babSmrg    ,OSC_IconOnly = 1
3547d1603babSmrg    ,OSC_TitleOnly = 2
3548d1603babSmrg    ,OSC_X_Property = 3
3549d1603babSmrg    ,OSC_SetAnsiColor = 4
3550d1603babSmrg    ,OSC_GetAnsiColors = 5
3551d1603babSmrg    ,OSC_ColorMode = 6
3552d1603babSmrg    ,OSC_SetupPointer = 22
3553d1603babSmrg    ,OSC_Unused_30 = 30		/* Konsole (unused) */
3554d1603babSmrg    ,OSC_Unused_31 = 31		/* Konsole (unused) */
3555d1603babSmrg    ,OSC_NewLogFile = 46
3556d1603babSmrg    ,OSC_FontOps = 50
3557d1603babSmrg    ,OSC_Unused_51		/* Emacs (unused) */
3558d1603babSmrg    ,OSC_SelectionData = 52
3559d1603babSmrg    ,OSC_AllowedOps = 60
3560d1603babSmrg    ,OSC_DisallowedOps = 61
3561d1603babSmrg} OscMiscOps;
3562d1603babSmrg
3563d522f475Smrgstatic Bool
3564d522f475SmrgGetOldColors(XtermWidget xw)
3565d522f475Smrg{
35669a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3567037a25ddSmrg	int i;
3568037a25ddSmrg
35699a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
35709a64e1c5Smrg	if (xw->work.oldColors == NULL) {
35713367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3572d522f475Smrg	    return (False);
3573d522f475Smrg	}
35749a64e1c5Smrg	xw->work.oldColors->which = 0;
3575d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
35769a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
35779a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3578d522f475Smrg	}
35799a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3580d522f475Smrg    }
3581d522f475Smrg    return (True);
3582d522f475Smrg}
3583d522f475Smrg
3584d522f475Smrgstatic int
3585d4fba8b9SmrgoppositeColor(XtermWidget xw, int n)
3586d522f475Smrg{
3587d4fba8b9Smrg    Boolean reversed = (xw->misc.re_verse);
3588d4fba8b9Smrg
3589d522f475Smrg    switch (n) {
3590d522f475Smrg    case TEXT_FG:
3591d4fba8b9Smrg	n = reversed ? TEXT_FG : TEXT_BG;
3592d522f475Smrg	break;
3593d522f475Smrg    case TEXT_BG:
3594d4fba8b9Smrg	n = reversed ? TEXT_BG : TEXT_FG;
3595d522f475Smrg	break;
3596d522f475Smrg    case MOUSE_FG:
3597d522f475Smrg	n = MOUSE_BG;
3598d522f475Smrg	break;
3599d522f475Smrg    case MOUSE_BG:
3600d522f475Smrg	n = MOUSE_FG;
3601d522f475Smrg	break;
3602d522f475Smrg#if OPT_TEK4014
3603d522f475Smrg    case TEK_FG:
3604d4fba8b9Smrg	n = reversed ? TEK_FG : TEK_BG;
3605d522f475Smrg	break;
3606d522f475Smrg    case TEK_BG:
3607d4fba8b9Smrg	n = reversed ? TEK_BG : TEK_FG;
3608d522f475Smrg	break;
3609d522f475Smrg#endif
3610d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3611d522f475Smrg    case HIGHLIGHT_FG:
3612d522f475Smrg	n = HIGHLIGHT_BG;
3613d522f475Smrg	break;
3614d522f475Smrg    case HIGHLIGHT_BG:
3615d522f475Smrg	n = HIGHLIGHT_FG;
3616d522f475Smrg	break;
3617d522f475Smrg#endif
3618d522f475Smrg    default:
3619d522f475Smrg	break;
3620d522f475Smrg    }
3621d522f475Smrg    return n;
3622d522f475Smrg}
3623d522f475Smrg
3624d4fba8b9Smrgstatic Bool
3625d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3626d522f475Smrg{
3627d4fba8b9Smrg    Bool result = False;
3628d4fba8b9Smrg
3629cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3630cd3331d0Smrg	XColor color;
3631cd3331d0Smrg	char buffer[80];
3632d522f475Smrg
3633cd3331d0Smrg	/*
3634cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3635cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3636cd3331d0Smrg	 * reporting the opposite color which would be used.
3637cd3331d0Smrg	 */
3638d4fba8b9Smrg	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3639cd3331d0Smrg
3640cd3331d0Smrg	GetOldColors(xw);
36419a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3642c48a5815Smrg	(void) QueryOneColor(xw, &color);
3643cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3644cd3331d0Smrg		color.red,
3645cd3331d0Smrg		color.green,
3646cd3331d0Smrg		color.blue);
3647712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
36489a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3649cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3650cd3331d0Smrg	unparseputs(xw, buffer);
3651cd3331d0Smrg	unparseputc1(xw, final);
3652d4fba8b9Smrg	result = True;
3653cd3331d0Smrg    }
3654d4fba8b9Smrg    return result;
3655d522f475Smrg}
3656d522f475Smrg
3657d522f475Smrgstatic Bool
3658d4fba8b9SmrgUpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3659d522f475Smrg{
3660d522f475Smrg    int i;
3661d522f475Smrg
3662d522f475Smrg    /* if we were going to free old colors, this would be the place to
3663d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3664d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3665d522f475Smrg     * we could save some overhead this way.   The only case in which this
3666d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3667d522f475Smrg     * which case they can restart xterm
3668d522f475Smrg     */
3669d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3670d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
36719a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
36729a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
36739a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3674d522f475Smrg	    }
3675d522f475Smrg	    if (pNew->names[i]) {
36769a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3677d522f475Smrg	    }
36789a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3679d522f475Smrg	}
3680d522f475Smrg    }
3681d522f475Smrg    return (True);
3682d522f475Smrg}
3683d522f475Smrg
3684d522f475Smrg/*
3685d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3686d522f475Smrg * xterm is compiled.
3687d522f475Smrg */
3688d522f475Smrgstatic int
3689d522f475SmrgOscToColorIndex(OscTextColors mode)
3690d522f475Smrg{
3691d522f475Smrg    int result = 0;
3692d522f475Smrg
3693d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3694d522f475Smrg    switch (mode) {
3695d522f475Smrg	CASE(TEXT_FG);
3696d522f475Smrg	CASE(TEXT_BG);
3697d522f475Smrg	CASE(TEXT_CURSOR);
3698d522f475Smrg	CASE(MOUSE_FG);
3699d522f475Smrg	CASE(MOUSE_BG);
3700d522f475Smrg#if OPT_TEK4014
3701d522f475Smrg	CASE(TEK_FG);
3702d522f475Smrg	CASE(TEK_BG);
3703d522f475Smrg#endif
3704d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3705d522f475Smrg	CASE(HIGHLIGHT_BG);
3706d522f475Smrg	CASE(HIGHLIGHT_FG);
3707d522f475Smrg#endif
3708d522f475Smrg#if OPT_TEK4014
3709d522f475Smrg	CASE(TEK_CURSOR);
3710d522f475Smrg#endif
3711d522f475Smrg    case OSC_NCOLORS:
3712d522f475Smrg	break;
3713d522f475Smrg    }
3714d1603babSmrg#undef CASE
3715d522f475Smrg    return result;
3716d522f475Smrg}
3717d522f475Smrg
3718d522f475Smrgstatic Bool
3719d522f475SmrgChangeColorsRequest(XtermWidget xw,
3720d522f475Smrg		    int start,
3721d522f475Smrg		    char *names,
3722d522f475Smrg		    int final)
3723d522f475Smrg{
3724d522f475Smrg    Bool result = False;
3725d522f475Smrg    ScrnColors newColors;
3726d522f475Smrg
3727d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3728d522f475Smrg
3729d522f475Smrg    if (GetOldColors(xw)) {
3730037a25ddSmrg	int i;
3731d4fba8b9Smrg	int queried = 0;
3732037a25ddSmrg
3733d522f475Smrg	newColors.which = 0;
3734d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3735d522f475Smrg	    newColors.names[i] = NULL;
3736d522f475Smrg	}
3737d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3738037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3739d522f475Smrg	    if (xw->misc.re_verse)
3740d4fba8b9Smrg		ndx = oppositeColor(xw, ndx);
3741d522f475Smrg
3742cd3331d0Smrg	    if (IsEmpty(names)) {
3743d522f475Smrg		newColors.names[ndx] = NULL;
3744d522f475Smrg	    } else {
3745037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3746037a25ddSmrg
3747d522f475Smrg		names = strchr(names, ';');
3748d522f475Smrg		if (names != NULL) {
3749d522f475Smrg		    *names++ = '\0';
3750d522f475Smrg		}
3751fa3f02f3Smrg		if (thisName != 0) {
3752fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3753d4fba8b9Smrg			if (ReportColorRequest(xw, ndx, final))
3754d4fba8b9Smrg			    ++queried;
37559a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
37569a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3757fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3758fa3f02f3Smrg		    }
3759d522f475Smrg		}
3760d522f475Smrg	    }
3761d522f475Smrg	}
3762d522f475Smrg
3763d522f475Smrg	if (newColors.which != 0) {
3764d522f475Smrg	    ChangeColors(xw, &newColors);
3765d522f475Smrg	    UpdateOldColors(xw, &newColors);
3766d4fba8b9Smrg	} else if (queried) {
3767d4fba8b9Smrg	    unparse_end(xw);
3768d522f475Smrg	}
3769d522f475Smrg	result = True;
3770d522f475Smrg    }
3771d522f475Smrg    return result;
3772d522f475Smrg}
3773d522f475Smrg
3774cd3331d0Smrgstatic Bool
3775cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3776cd3331d0Smrg		   int code)
3777cd3331d0Smrg{
3778cd3331d0Smrg    Bool result = False;
3779cd3331d0Smrg
3780dfb07bc7Smrg    (void) xw;
3781dfb07bc7Smrg    (void) code;
3782dfb07bc7Smrg
3783cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3784cd3331d0Smrg    if (GetOldColors(xw)) {
3785037a25ddSmrg	ScrnColors newColors;
3786037a25ddSmrg	const char *thisName;
3787037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3788037a25ddSmrg
3789cd3331d0Smrg	if (xw->misc.re_verse)
3790d4fba8b9Smrg	    ndx = oppositeColor(xw, ndx);
3791cd3331d0Smrg
3792cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3793cd3331d0Smrg
3794cd3331d0Smrg	newColors.which = 0;
3795cd3331d0Smrg	newColors.names[ndx] = NULL;
3796cd3331d0Smrg
3797cd3331d0Smrg	if (thisName != 0
37989a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
37999a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3800cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3801cd3331d0Smrg
3802cd3331d0Smrg	    if (newColors.which != 0) {
3803cd3331d0Smrg		ChangeColors(xw, &newColors);
3804cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3805cd3331d0Smrg	    }
3806cd3331d0Smrg	}
3807cd3331d0Smrg	result = True;
3808cd3331d0Smrg    }
3809cd3331d0Smrg    return result;
3810cd3331d0Smrg}
3811cd3331d0Smrg
3812cd3331d0Smrg#if OPT_SHIFT_FONTS
3813cd3331d0Smrg/*
3814cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3815cd3331d0Smrg *
3816cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3817cd3331d0Smrg * the corresponding menu font entry.
3818cd3331d0Smrg */
3819cd3331d0Smrgstatic int
3820fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3821cd3331d0Smrg{
3822cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3823cd3331d0Smrg    int num = screen->menu_font_number;
3824cd3331d0Smrg    int rel = 0;
3825cd3331d0Smrg
3826cd3331d0Smrg    if (*++source == '+') {
3827cd3331d0Smrg	rel = 1;
3828cd3331d0Smrg	source++;
3829cd3331d0Smrg    } else if (*source == '-') {
3830cd3331d0Smrg	rel = -1;
3831cd3331d0Smrg	source++;
3832cd3331d0Smrg    }
3833cd3331d0Smrg
3834cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3835cd3331d0Smrg	int val = atoi(source);
3836cd3331d0Smrg	if (rel > 0)
3837cd3331d0Smrg	    rel = val;
3838cd3331d0Smrg	else if (rel < 0)
3839cd3331d0Smrg	    rel = -val;
3840cd3331d0Smrg	else
3841cd3331d0Smrg	    num = val;
3842cd3331d0Smrg    }
3843cd3331d0Smrg
3844cd3331d0Smrg    if (rel != 0) {
3845cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3846cd3331d0Smrg				     screen->menu_font_number, rel);
3847cd3331d0Smrg
3848cd3331d0Smrg    }
3849cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3850cd3331d0Smrg    *target = source;
3851cd3331d0Smrg    return num;
3852cd3331d0Smrg}
3853cd3331d0Smrg
3854cd3331d0Smrgstatic void
3855cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3856cd3331d0Smrg{
3857cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3858cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3859cd3331d0Smrg	Bool success = True;
3860cd3331d0Smrg	int num;
3861cb4a1343Smrg	String base = buf + 1;
3862cd3331d0Smrg	const char *name = 0;
3863cd3331d0Smrg
3864cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3865cd3331d0Smrg	if (num < 0
3866cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3867cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3868cd3331d0Smrg	    success = False;
3869cd3331d0Smrg	} else {
3870cd3331d0Smrg#if OPT_RENDERFONT
3871cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3872cd3331d0Smrg		name = getFaceName(xw, False);
3873cd3331d0Smrg	    } else
3874cd3331d0Smrg#endif
3875cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3876cd3331d0Smrg		success = False;
3877cd3331d0Smrg	    }
3878cd3331d0Smrg	}
3879cd3331d0Smrg
3880cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3881cd3331d0Smrg	unparseputs(xw, "50");
3882cd3331d0Smrg
3883cd3331d0Smrg	if (success) {
3884cd3331d0Smrg	    unparseputc(xw, ';');
3885cd3331d0Smrg	    if (buf >= base) {
3886cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3887cd3331d0Smrg		if (*buf != '\0') {
3888037a25ddSmrg		    char temp[10];
3889037a25ddSmrg
3890cd3331d0Smrg		    unparseputc(xw, '#');
3891cd3331d0Smrg		    sprintf(temp, "%d", num);
3892cd3331d0Smrg		    unparseputs(xw, temp);
3893cd3331d0Smrg		    if (*name != '\0')
3894cd3331d0Smrg			unparseputc(xw, ' ');
3895cd3331d0Smrg		}
3896cd3331d0Smrg	    }
3897cd3331d0Smrg	    unparseputs(xw, name);
3898cd3331d0Smrg	}
3899cd3331d0Smrg
3900cd3331d0Smrg	unparseputc1(xw, final);
3901cd3331d0Smrg	unparse_end(xw);
3902cd3331d0Smrg    }
3903cd3331d0Smrg}
3904cd3331d0Smrg
3905cd3331d0Smrgstatic void
3906cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3907cd3331d0Smrg{
3908cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3909cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3910cd3331d0Smrg	Bool success = True;
3911cd3331d0Smrg	int num;
3912cd3331d0Smrg	VTFontNames fonts;
3913cd3331d0Smrg	char *name;
3914cd3331d0Smrg
3915cd3331d0Smrg	/*
3916cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3917cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3918cd3331d0Smrg	 *
3919cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3920cd3331d0Smrg	 * to load the font entry.
3921cd3331d0Smrg	 */
3922cd3331d0Smrg	if (*buf == '#') {
3923cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3924cd3331d0Smrg
3925cd3331d0Smrg	    if (num < 0
3926cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3927cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3928cd3331d0Smrg		success = False;
3929cd3331d0Smrg	    } else {
3930cd3331d0Smrg		/*
3931cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3932cd3331d0Smrg		 * for a font specification within the control.
3933cd3331d0Smrg		 */
3934cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3935cd3331d0Smrg		    ++buf;
3936cd3331d0Smrg		}
3937cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3938cd3331d0Smrg		    ++buf;
3939cd3331d0Smrg		}
3940cd3331d0Smrg#if OPT_RENDERFONT
3941cd3331d0Smrg		if (UsingRenderFont(xw)) {
3942c219fbebSmrg		    /* EMPTY */
3943c219fbebSmrg		    /* there is only one font entry to load */
3944c219fbebSmrg		    ;
3945cd3331d0Smrg		} else
3946cd3331d0Smrg#endif
3947cd3331d0Smrg		{
3948cd3331d0Smrg		    /*
3949cd3331d0Smrg		     * Normally there is no font specified in the control.
3950cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3951cd3331d0Smrg		     */
3952cd3331d0Smrg		    if (*buf == '\0') {
3953cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3954cd3331d0Smrg			    success = False;
3955cd3331d0Smrg			}
3956cd3331d0Smrg		    }
3957cd3331d0Smrg		}
3958cd3331d0Smrg	    }
3959cd3331d0Smrg	} else {
3960cd3331d0Smrg	    num = screen->menu_font_number;
3961cd3331d0Smrg	}
3962cd3331d0Smrg	name = x_strtrim(buf);
396394644356Smrg	if (screen->EscapeFontName()) {
396494644356Smrg	    FREE_STRING(screen->EscapeFontName());
396594644356Smrg	    screen->EscapeFontName() = 0;
396694644356Smrg	}
3967cd3331d0Smrg	if (success && !IsEmpty(name)) {
3968cd3331d0Smrg#if OPT_RENDERFONT
3969cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3970cd3331d0Smrg		setFaceName(xw, name);
3971cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3972cd3331d0Smrg	    } else
3973cd3331d0Smrg#endif
3974cd3331d0Smrg	    {
3975cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3976cd3331d0Smrg		fonts.f_n = name;
3977d1603babSmrg		if (SetVTFont(xw, num, True, &fonts)
3978d1603babSmrg		    && num == screen->menu_font_number
3979d1603babSmrg		    && num != fontMenu_fontescape) {
398094644356Smrg		    screen->EscapeFontName() = x_strdup(name);
398194644356Smrg		}
3982cd3331d0Smrg	    }
3983cd3331d0Smrg	} else {
3984cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3985cd3331d0Smrg	}
398694644356Smrg	update_font_escape();
3987cd3331d0Smrg	free(name);
3988cd3331d0Smrg    }
3989cd3331d0Smrg}
3990cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3991cd3331d0Smrg
3992d522f475Smrg/***====================================================================***/
3993d522f475Smrg
3994d1603babSmrgstatic void
3995d1603babSmrgreport_allowed_ops(XtermWidget xw, int final)
3996d1603babSmrg{
3997d1603babSmrg    TScreen *screen = TScreenOf(xw);
3998d1603babSmrg    char delimiter = ';';
3999d1603babSmrg
4000d1603babSmrg    unparseputc1(xw, ANSI_OSC);
4001d1603babSmrg    unparseputn(xw, OSC_AllowedOps);
4002d1603babSmrg
4003d1603babSmrg#define CASE(name) \
4004d1603babSmrg    if (screen->name) { \
4005d1603babSmrg	unparseputc(xw, delimiter); \
4006d1603babSmrg	unparseputs(xw, #name); \
4007d1603babSmrg	delimiter = ','; \
4008d1603babSmrg    }
4009d1603babSmrg    CASE(allowColorOps);
4010d1603babSmrg    CASE(allowFontOps);
4011d1603babSmrg    CASE(allowMouseOps);
4012d1603babSmrg    CASE(allowPasteControls);
4013d1603babSmrg    CASE(allowTcapOps);
4014d1603babSmrg    CASE(allowTitleOps);
4015d1603babSmrg    CASE(allowWindowOps);
4016d1603babSmrg#undef CASE
4017d1603babSmrg
4018d1603babSmrg    unparseputc1(xw, final);
4019d1603babSmrg}
4020d1603babSmrg
4021d1603babSmrgstatic void
4022d1603babSmrgreport_disallowed_ops(XtermWidget xw, char *value, int final)
4023d1603babSmrg{
4024d1603babSmrg    unparseputc1(xw, ANSI_OSC);
4025d1603babSmrg    unparseputn(xw, OSC_DisallowedOps);
4026d1603babSmrg    unparse_disallowed_ops(xw, value);
4027d1603babSmrg    unparseputc1(xw, final);
4028d1603babSmrg}
4029d1603babSmrg
4030d1603babSmrg/***====================================================================***/
4031d1603babSmrg
4032d522f475Smrgvoid
4033fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
4034d522f475Smrg{
4035cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4036d522f475Smrg    int mode;
4037d522f475Smrg    Char *cp;
4038d522f475Smrg    int state = 0;
4039d522f475Smrg    char *buf = 0;
404050027b5bSmrg    char temp[20];
4041cd3331d0Smrg#if OPT_ISO_COLORS
4042cd3331d0Smrg    int ansi_colors = 0;
4043cd3331d0Smrg#endif
4044cd3331d0Smrg    Bool need_data = True;
4045fa3f02f3Smrg    Bool optional_data = False;
4046d522f475Smrg
4047d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
4048d522f475Smrg
4049712a7ff4Smrg    (void) screen;
4050712a7ff4Smrg
4051d522f475Smrg    /*
4052d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
4053d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
4054d522f475Smrg     * with the same final character as the application sends to make this
4055d522f475Smrg     * work better with shell scripts, which may have trouble reading an
4056d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
4057d522f475Smrg     */
4058d522f475Smrg    mode = 0;
4059d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
4060d522f475Smrg	switch (state) {
4061d522f475Smrg	case 0:
4062d522f475Smrg	    if (isdigit(*cp)) {
4063d522f475Smrg		mode = 10 * mode + (*cp - '0');
4064d522f475Smrg		if (mode > 65535) {
4065d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
4066d522f475Smrg		    return;
4067d522f475Smrg		}
4068d522f475Smrg		break;
4069d4fba8b9Smrg	    } else {
4070d4fba8b9Smrg		switch (*cp) {
4071d4fba8b9Smrg		case 'I':
4072d4fba8b9Smrg		    xtermLoadIcon(xw, (char *) ++cp);
4073d4fba8b9Smrg		    return;
4074d4fba8b9Smrg		case 'l':
4075d4fba8b9Smrg		    ChangeTitle(xw, (char *) ++cp);
4076d4fba8b9Smrg		    return;
4077d4fba8b9Smrg		case 'L':
4078d4fba8b9Smrg		    ChangeIconName(xw, (char *) ++cp);
4079d4fba8b9Smrg		    return;
4080d4fba8b9Smrg		}
4081d522f475Smrg	    }
4082d522f475Smrg	    /* FALLTHRU */
4083d522f475Smrg	case 1:
4084d522f475Smrg	    if (*cp != ';') {
4085d1603babSmrg		TRACE(("do_osc did not find semicolon offset %lu\n",
4086d1603babSmrg		       (unsigned long) (cp - oscbuf)));
4087d522f475Smrg		return;
4088d522f475Smrg	    }
4089d522f475Smrg	    state = 2;
4090d522f475Smrg	    break;
4091d522f475Smrg	case 2:
4092d522f475Smrg	    buf = (char *) cp;
4093d522f475Smrg	    state = 3;
4094d522f475Smrg	    /* FALLTHRU */
4095d522f475Smrg	default:
4096cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
4097d522f475Smrg		switch (mode) {
4098d522f475Smrg		case 0:
4099d522f475Smrg		case 1:
4100d522f475Smrg		case 2:
4101d522f475Smrg		    break;
4102d522f475Smrg		default:
4103d1603babSmrg		    TRACE(("do_osc found nonprinting char %02X offset %lu\n",
4104d522f475Smrg			   CharOf(*cp),
4105d1603babSmrg			   (unsigned long) (cp - oscbuf)));
4106d522f475Smrg		    return;
4107d522f475Smrg		}
4108d522f475Smrg	    }
4109d522f475Smrg	}
4110d522f475Smrg    }
4111cd3331d0Smrg
41123367019cSmrg    /*
41133367019cSmrg     * Check if the palette changed and there are no more immediate changes
41143367019cSmrg     * that could be deferred to the next repaint.
41153367019cSmrg     */
4116dfb07bc7Smrg    if (xw->work.palette_changed) {
41173367019cSmrg	switch (mode) {
4118d1603babSmrg	case OSC_AllowedOps:
4119d1603babSmrg	case OSC_DisallowedOps:
4120d1603babSmrg	case OSC_FontOps:
4121d1603babSmrg	case OSC_NewLogFile:
4122d1603babSmrg	case OSC_SelectionData:
4123d1603babSmrg	case OSC_Unused_30:
4124d1603babSmrg	case OSC_Unused_31:
4125d1603babSmrg	case OSC_Unused_51:
4126d1603babSmrg	case OSC_X_Property:
41273367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
4128dfb07bc7Smrg	    xw->work.palette_changed = False;
41293367019cSmrg	    xtermRepaint(xw);
41303367019cSmrg	    break;
4131d4fba8b9Smrg	default:
4132d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
4133d4fba8b9Smrg	    break;
41343367019cSmrg	}
41353367019cSmrg    }
41363367019cSmrg
4137cd3331d0Smrg    /*
4138cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
4139cd3331d0Smrg     * a special case.
4140cd3331d0Smrg     */
4141cd3331d0Smrg    switch (mode) {
4142d1603babSmrg    case OSC_FontOps:
4143cd3331d0Smrg#if OPT_ISO_COLORS
4144d1603babSmrg    case OSC_Reset(OSC_SetAnsiColor):
4145d1603babSmrg    case OSC_Reset(OSC_GetAnsiColors):
4146fa3f02f3Smrg	need_data = False;
4147fa3f02f3Smrg	optional_data = True;
4148fa3f02f3Smrg	break;
4149cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4150cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4151cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4152cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4153cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4154cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4155cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4156cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4157cd3331d0Smrg#endif
4158cd3331d0Smrg#if OPT_TEK4014
4159cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4160cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4161cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4162cd3331d0Smrg#endif
4163d1603babSmrg    case OSC_AllowedOps:
4164cd3331d0Smrg	need_data = False;
4165cd3331d0Smrg	break;
4166cd3331d0Smrg#endif
4167cd3331d0Smrg    default:
4168cd3331d0Smrg	break;
4169cd3331d0Smrg    }
4170cd3331d0Smrg
4171cd3331d0Smrg    /*
4172cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
4173cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
4174cd3331d0Smrg     */
4175cd3331d0Smrg    if (IsEmpty(buf)) {
4176cd3331d0Smrg	if (need_data) {
417750027b5bSmrg	    switch (mode) {
417850027b5bSmrg	    case 0:
417950027b5bSmrg	    case 1:
418050027b5bSmrg	    case 2:
418150027b5bSmrg		buf = strcpy(temp, "xterm");
418250027b5bSmrg		break;
418350027b5bSmrg	    default:
418450027b5bSmrg		TRACE(("do_osc found no data\n"));
418550027b5bSmrg		return;
418650027b5bSmrg	    }
418750027b5bSmrg	} else {
418850027b5bSmrg	    temp[0] = '\0';
418950027b5bSmrg	    buf = temp;
4190cd3331d0Smrg	}
4191fa3f02f3Smrg    } else if (!need_data && !optional_data) {
4192fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
4193d522f475Smrg	return;
41940d92cbfdSchristos    }
4195d522f475Smrg
4196d522f475Smrg    switch (mode) {
4197d1603babSmrg    case OSC_IconBoth:		/* new icon name and title */
4198b7c89284Ssnj	ChangeIconName(xw, buf);
4199b7c89284Ssnj	ChangeTitle(xw, buf);
4200d522f475Smrg	break;
4201d522f475Smrg
4202d1603babSmrg    case OSC_IconOnly:		/* new icon name only */
4203b7c89284Ssnj	ChangeIconName(xw, buf);
4204d522f475Smrg	break;
4205d522f475Smrg
4206d1603babSmrg    case OSC_TitleOnly:	/* new title only */
4207b7c89284Ssnj	ChangeTitle(xw, buf);
4208d522f475Smrg	break;
4209d522f475Smrg
421022d8e007Schristos#ifdef notdef
4211d1603babSmrg    case OSC_X_Property:	/* change X property */
4212cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
42130d92cbfdSchristos	    ChangeXprop(buf);
4214d522f475Smrg	break;
421522d8e007Schristos#endif
4216d522f475Smrg#if OPT_ISO_COLORS
4217d1603babSmrg    case OSC_GetAnsiColors:
4218cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4219cd3331d0Smrg	/* FALLTHRU */
4220d1603babSmrg    case OSC_SetAnsiColor:
4221d4fba8b9Smrg	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4222dfb07bc7Smrg	    xw->work.palette_changed = True;
4223cd3331d0Smrg	break;
4224d1603babSmrg    case OSC_ColorMode:
422594644356Smrg	/* FALLTHRU */
4226d1603babSmrg    case OSC_Reset(OSC_ColorMode):
422794644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
422894644356Smrg	while (*buf != '\0') {
422994644356Smrg	    long which = 0;
423094644356Smrg	    long value = 0;
423194644356Smrg	    char *next;
423294644356Smrg	    if (*buf == ';') {
423394644356Smrg		++buf;
423494644356Smrg	    } else {
423594644356Smrg		which = strtol(buf, &next, 10);
4236037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
423794644356Smrg		    break;
423894644356Smrg		buf = next;
423994644356Smrg		if (*buf == ';')
424094644356Smrg		    ++buf;
424194644356Smrg	    }
424294644356Smrg	    if (*buf == ';') {
424394644356Smrg		++buf;
424494644356Smrg	    } else {
424594644356Smrg		value = strtol(buf, &next, 10);
4246dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
424794644356Smrg		    break;
424894644356Smrg		buf = next;
424994644356Smrg		if (*buf == ';')
425094644356Smrg		    ++buf;
425194644356Smrg	    }
425294644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
425394644356Smrg	    switch (which) {
425494644356Smrg	    case 0:
425594644356Smrg		screen->colorBDMode = (value != 0);
425694644356Smrg		break;
425794644356Smrg	    case 1:
425894644356Smrg		screen->colorULMode = (value != 0);
425994644356Smrg		break;
426094644356Smrg	    case 2:
426194644356Smrg		screen->colorBLMode = (value != 0);
426294644356Smrg		break;
426394644356Smrg	    case 3:
426494644356Smrg		screen->colorRVMode = (value != 0);
426594644356Smrg		break;
426694644356Smrg#if OPT_WIDE_ATTRS
426794644356Smrg	    case 4:
426894644356Smrg		screen->colorITMode = (value != 0);
426994644356Smrg		break;
427094644356Smrg#endif
427194644356Smrg	    default:
427294644356Smrg		TRACE(("...unknown colorXXMode\n"));
427394644356Smrg		break;
427494644356Smrg	    }
427594644356Smrg	}
427694644356Smrg	break;
4277d1603babSmrg    case OSC_Reset(OSC_GetAnsiColors):
4278cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4279cd3331d0Smrg	/* FALLTHRU */
4280d1603babSmrg    case OSC_Reset(OSC_SetAnsiColor):
4281cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4282dfb07bc7Smrg	    xw->work.palette_changed = True;
4283d522f475Smrg	break;
4284d522f475Smrg#endif
4285d522f475Smrg    case OSC_TEXT_FG:
4286d522f475Smrg    case OSC_TEXT_BG:
4287d522f475Smrg    case OSC_TEXT_CURSOR:
4288d522f475Smrg    case OSC_MOUSE_FG:
4289d522f475Smrg    case OSC_MOUSE_BG:
4290d522f475Smrg#if OPT_HIGHLIGHT_COLOR
4291d522f475Smrg    case OSC_HIGHLIGHT_BG:
4292cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
4293d522f475Smrg#endif
4294d522f475Smrg#if OPT_TEK4014
4295d522f475Smrg    case OSC_TEK_FG:
4296d522f475Smrg    case OSC_TEK_BG:
4297d522f475Smrg    case OSC_TEK_CURSOR:
4298d522f475Smrg#endif
4299cd3331d0Smrg	if (xw->misc.dynamicColors) {
4300d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
4301cd3331d0Smrg	}
4302cd3331d0Smrg	break;
4303cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4304cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4305cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4306cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4307cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4308cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4309cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4310cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4311cd3331d0Smrg#endif
4312cd3331d0Smrg#if OPT_TEK4014
4313cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4314cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4315cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4316cd3331d0Smrg#endif
4317cd3331d0Smrg	if (xw->misc.dynamicColors) {
4318cd3331d0Smrg	    ResetColorsRequest(xw, mode);
4319cd3331d0Smrg	}
4320d522f475Smrg	break;
4321d522f475Smrg
4322d1603babSmrg    case OSC_SetupPointer:
43238f44fb3bSmrg	xtermSetupPointer(xw, buf);
43248f44fb3bSmrg	break;
43258f44fb3bSmrg
4326d522f475Smrg#ifdef ALLOWLOGGING
4327d1603babSmrg    case OSC_NewLogFile:
4328d522f475Smrg#ifdef ALLOWLOGFILECHANGES
4329d522f475Smrg	/*
4330d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
4331d522f475Smrg	 * arbitrary files accessible to the person running xterm.
4332d522f475Smrg	 */
4333037a25ddSmrg	if (strcmp(buf, "?")) {
4334037a25ddSmrg	    char *bp;
4335dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
4336d4fba8b9Smrg		free(screen->logfile);
4337037a25ddSmrg		screen->logfile = bp;
4338037a25ddSmrg		break;
4339037a25ddSmrg	    }
4340d522f475Smrg	}
4341d522f475Smrg#endif
4342cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4343cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4344d522f475Smrg	break;
4345d522f475Smrg#endif /* ALLOWLOGGING */
4346d522f475Smrg
4347d1603babSmrg    case OSC_FontOps:
4348d522f475Smrg#if OPT_SHIFT_FONTS
4349cd3331d0Smrg	if (*buf == '?') {
4350cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
4351cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
4352cd3331d0Smrg	    ChangeFontRequest(xw, buf);
4353d522f475Smrg	}
4354d522f475Smrg#endif /* OPT_SHIFT_FONTS */
4355d522f475Smrg	break;
4356d522f475Smrg
4357d522f475Smrg#if OPT_PASTE64
4358d1603babSmrg    case OSC_SelectionData:
4359cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
4360d522f475Smrg	break;
4361d522f475Smrg#endif
4362d1603babSmrg
4363d1603babSmrg    case OSC_AllowedOps:	/* XTQALLOWED */
4364d1603babSmrg	report_allowed_ops(xw, final);
4365d1603babSmrg	break;
4366d1603babSmrg
4367d1603babSmrg    case OSC_DisallowedOps:	/* XTQDISALLOWED */
4368d1603babSmrg	report_disallowed_ops(xw, buf, final);
4369d1603babSmrg	break;
4370d1603babSmrg
4371d1603babSmrg    case OSC_Unused_30:
4372d1603babSmrg    case OSC_Unused_31:
4373d1603babSmrg    case OSC_Unused_51:
4374cd3331d0Smrg    default:
4375cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
4376cd3331d0Smrg	break;
4377d522f475Smrg    }
4378d522f475Smrg    unparse_end(xw);
4379d522f475Smrg}
4380d522f475Smrg
4381d522f475Smrg/*
4382d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
4383d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
4384d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4385d522f475Smrg * "real" terminals accept commas in the string definitions).
4386d522f475Smrg */
4387d522f475Smrgstatic int
4388cd3331d0Smrgudk_value(const char **cp)
4389d522f475Smrg{
4390cd3331d0Smrg    int result = -1;
4391d522f475Smrg
4392d522f475Smrg    for (;;) {
4393037a25ddSmrg	int c;
4394037a25ddSmrg
4395d522f475Smrg	if ((c = **cp) != '\0')
4396d522f475Smrg	    *cp = *cp + 1;
4397d522f475Smrg	if (c == ';' || c == '\0')
4398cd3331d0Smrg	    break;
4399cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
4400cd3331d0Smrg	    break;
4401d522f475Smrg    }
4402cd3331d0Smrg
4403cd3331d0Smrg    return result;
4404d522f475Smrg}
4405d522f475Smrg
4406d522f475Smrgvoid
44079a64e1c5Smrgreset_decudk(XtermWidget xw)
4408d522f475Smrg{
4409d522f475Smrg    int n;
4410d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
4411d4fba8b9Smrg	FreeAndNull(xw->work.user_keys[n].str);
4412d4fba8b9Smrg	xw->work.user_keys[n].len = 0;
4413d522f475Smrg    }
4414d522f475Smrg}
4415d522f475Smrg
4416d522f475Smrg/*
4417d522f475Smrg * Parse the data for DECUDK (user-defined keys).
4418d522f475Smrg */
4419d522f475Smrgstatic void
44209a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
4421d522f475Smrg{
4422d522f475Smrg    while (*cp) {
4423cd3331d0Smrg	const char *base = cp;
4424d4fba8b9Smrg	char *str = malloc(strlen(cp) + 3);
4425d522f475Smrg	unsigned key = 0;
4426d522f475Smrg	int len = 0;
4427d522f475Smrg
442894644356Smrg	if (str == NULL)
442994644356Smrg	    break;
443094644356Smrg
4431d522f475Smrg	while (isdigit(CharOf(*cp)))
44320d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
4433037a25ddSmrg
4434d522f475Smrg	if (*cp == '/') {
4435037a25ddSmrg	    int lo, hi;
4436037a25ddSmrg
4437d522f475Smrg	    cp++;
4438d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
4439d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
44400d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
4441d522f475Smrg	    }
4442d522f475Smrg	}
4443d522f475Smrg	if (len > 0 && key < MAX_UDK) {
44443367019cSmrg	    str[len] = '\0';
4445d4fba8b9Smrg	    free(xw->work.user_keys[key].str);
44469a64e1c5Smrg	    xw->work.user_keys[key].str = str;
44479a64e1c5Smrg	    xw->work.user_keys[key].len = len;
4448d4fba8b9Smrg	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4449d522f475Smrg	} else {
4450d522f475Smrg	    free(str);
4451d522f475Smrg	}
4452d522f475Smrg	if (*cp == ';')
4453d522f475Smrg	    cp++;
4454d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
4455d522f475Smrg	    break;
4456d522f475Smrg    }
4457d522f475Smrg}
4458d522f475Smrg
4459fa3f02f3Smrg/*
4460fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
4461fa3f02f3Smrg * interspersing with control characters, but have the string already.
4462fa3f02f3Smrg */
4463fa3f02f3Smrgstatic void
4464fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
4465fa3f02f3Smrg{
4466fa3f02f3Smrg    const char *cp = *string;
4467fa3f02f3Smrg    ParmType nparam = 0;
4468fa3f02f3Smrg    int last_empty = 1;
4469fa3f02f3Smrg
4470fa3f02f3Smrg    memset(params, 0, sizeof(*params));
4471fa3f02f3Smrg    while (*cp != '\0') {
4472fa3f02f3Smrg	Char ch = CharOf(*cp++);
4473fa3f02f3Smrg
4474fa3f02f3Smrg	if (isdigit(ch)) {
4475fa3f02f3Smrg	    last_empty = 0;
4476fa3f02f3Smrg	    if (nparam < NPARAM) {
4477fa3f02f3Smrg		params->a_param[nparam] =
4478fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
4479fa3f02f3Smrg				+ (ch - '0'));
4480fa3f02f3Smrg	    }
4481fa3f02f3Smrg	} else if (ch == ';') {
4482fa3f02f3Smrg	    last_empty = 1;
4483fa3f02f3Smrg	    nparam++;
4484fa3f02f3Smrg	} else if (ch < 32) {
4485fa3f02f3Smrg	    /* EMPTY */ ;
4486fa3f02f3Smrg	} else {
4487fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
4488fa3f02f3Smrg	    params->a_final = ch;
4489fa3f02f3Smrg	    break;
4490fa3f02f3Smrg	}
4491fa3f02f3Smrg    }
4492fa3f02f3Smrg
4493fa3f02f3Smrg    *string = cp;
4494fa3f02f3Smrg    if (!last_empty)
4495fa3f02f3Smrg	nparam++;
4496fa3f02f3Smrg    if (nparam > NPARAM)
4497fa3f02f3Smrg	params->a_nparam = NPARAM;
4498fa3f02f3Smrg    else
4499fa3f02f3Smrg	params->a_nparam = nparam;
4500fa3f02f3Smrg}
4501fa3f02f3Smrg
4502d522f475Smrg#if OPT_TRACE
4503d522f475Smrg#define SOFT_WIDE 10
4504d522f475Smrg#define SOFT_HIGH 20
4505d522f475Smrg
4506d522f475Smrgstatic void
4507fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
4508d522f475Smrg{
4509d522f475Smrg    char DscsName[8];
4510d522f475Smrg    int len;
4511d522f475Smrg    int Pfn = params->a_param[0];
4512d522f475Smrg    int Pcn = params->a_param[1];
4513d522f475Smrg    int Pe = params->a_param[2];
4514d522f475Smrg    int Pcmw = params->a_param[3];
4515d522f475Smrg    int Pw = params->a_param[4];
4516d522f475Smrg    int Pt = params->a_param[5];
4517d522f475Smrg    int Pcmh = params->a_param[6];
4518d522f475Smrg    int Pcss = params->a_param[7];
4519d522f475Smrg
4520d522f475Smrg    int start_char = Pcn + 0x20;
4521d522f475Smrg    int char_wide = ((Pcmw == 0)
4522d522f475Smrg		     ? (Pcss ? 6 : 10)
4523d522f475Smrg		     : (Pcmw > 4
4524d522f475Smrg			? Pcmw
4525d522f475Smrg			: (Pcmw + 3)));
4526d522f475Smrg    int char_high = ((Pcmh == 0)
45273367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
4528d522f475Smrg			? 10
4529d522f475Smrg			: 20)
4530d522f475Smrg		     : Pcmh);
4531d522f475Smrg    Char ch;
4532d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
4533d522f475Smrg    Bool first = True;
4534d522f475Smrg    Bool prior = False;
4535d522f475Smrg    int row = 0, col = 0;
4536d522f475Smrg
4537d522f475Smrg    TRACE(("Parsing DECDLD\n"));
4538d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
4539d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
4540d522f475Smrg    TRACE(("  erase control %d\n", Pe));
4541d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
4542d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
4543d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
4544d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
4545d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
4546d522f475Smrg
4547d522f475Smrg    if (Pfn > 1
4548d522f475Smrg	|| Pcn > 95
4549d522f475Smrg	|| Pe > 2
4550d522f475Smrg	|| Pcmw > 10
4551d522f475Smrg	|| Pcmw == 1
4552d522f475Smrg	|| Pt > 2
4553d522f475Smrg	|| Pcmh > 20
4554d522f475Smrg	|| Pcss > 1
4555d522f475Smrg	|| char_wide > SOFT_WIDE
4556d522f475Smrg	|| char_high > SOFT_HIGH) {
4557d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4558d522f475Smrg	return;
4559d522f475Smrg    }
4560d522f475Smrg
4561d522f475Smrg    len = 0;
4562d522f475Smrg    while (*string != '\0') {
4563d522f475Smrg	ch = CharOf(*string++);
4564d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4565d522f475Smrg	    if (len < 2)
4566b7c89284Ssnj		DscsName[len++] = (char) ch;
4567d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4568b7c89284Ssnj	    DscsName[len++] = (char) ch;
4569d522f475Smrg	    break;
4570d522f475Smrg	}
4571d522f475Smrg    }
4572d522f475Smrg    DscsName[len] = 0;
4573d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4574d522f475Smrg
4575d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4576d522f475Smrg    while (*string != '\0') {
4577d522f475Smrg	if (first) {
4578d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4579d522f475Smrg	    if (prior) {
4580d522f475Smrg		for (row = 0; row < char_high; ++row) {
4581d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4582d522f475Smrg		}
4583d522f475Smrg	    }
4584d522f475Smrg	    prior = False;
4585d522f475Smrg	    first = False;
4586d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4587d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4588d522f475Smrg		    bits[row][col] = '.';
4589d522f475Smrg		}
4590d522f475Smrg	    }
4591d522f475Smrg	    row = col = 0;
4592d522f475Smrg	}
4593d522f475Smrg	ch = CharOf(*string++);
4594d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4595d522f475Smrg	    int n;
4596d522f475Smrg
4597b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4598d522f475Smrg	    for (n = 0; n < 6; ++n) {
4599b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4600d522f475Smrg	    }
4601d522f475Smrg	    col += 1;
4602d522f475Smrg	    prior = True;
4603d522f475Smrg	} else if (ch == '/') {
4604d522f475Smrg	    row += 6;
4605d522f475Smrg	    col = 0;
4606d522f475Smrg	} else if (ch == ';') {
4607d522f475Smrg	    first = True;
4608d522f475Smrg	    ++start_char;
4609d522f475Smrg	}
4610d522f475Smrg    }
4611d522f475Smrg}
4612d522f475Smrg#else
4613d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4614d522f475Smrg#endif
4615d522f475Smrg
4616d4fba8b9Smrgstatic const char *
4617d4fba8b9Smrgskip_params(const char *cp)
4618d4fba8b9Smrg{
4619d4fba8b9Smrg    while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4620d4fba8b9Smrg	++cp;
4621d4fba8b9Smrg    return cp;
4622d4fba8b9Smrg}
4623d4fba8b9Smrg
4624980988aeSmrg#if OPT_MOD_FKEYS || OPT_DEC_RECTOPS || (OPT_VT525_COLORS && OPT_ISO_COLORS)
4625d4fba8b9Smrgstatic int
4626d4fba8b9Smrgparse_int_param(const char **cp)
4627d4fba8b9Smrg{
4628980988aeSmrg    Boolean found = False;
4629d4fba8b9Smrg    int result = 0;
4630d4fba8b9Smrg    const char *s = *cp;
4631d4fba8b9Smrg    while (*s != '\0') {
4632d4fba8b9Smrg	if (*s == ';') {
4633d4fba8b9Smrg	    ++s;
4634d4fba8b9Smrg	    break;
4635d4fba8b9Smrg	} else if (*s >= '0' && *s <= '9') {
4636d4fba8b9Smrg	    result = (result * 10) + (*s++ - '0');
4637980988aeSmrg	    found = True;
4638d4fba8b9Smrg	} else {
4639d4fba8b9Smrg	    s += strlen(s);
4640d4fba8b9Smrg	}
4641d4fba8b9Smrg    }
4642980988aeSmrg    TRACE(("parse-int \"%s\" ->%d, %#x->\"%s\"\n", *cp, result, result, s));
4643d4fba8b9Smrg    *cp = s;
4644980988aeSmrg    return found ? result : -1;
4645d4fba8b9Smrg}
4646980988aeSmrg#endif
4647d4fba8b9Smrg
4648980988aeSmrg#if OPT_DEC_RECTOPS
4649d4fba8b9Smrgstatic int
4650d4fba8b9Smrgparse_chr_param(const char **cp)
4651d4fba8b9Smrg{
4652d4fba8b9Smrg    int result = 0;
4653d4fba8b9Smrg    const char *s = *cp;
4654d4fba8b9Smrg    if (*s != '\0') {
4655d4fba8b9Smrg	if ((result = CharOf(*s++)) != 0) {
4656d4fba8b9Smrg	    if (*s == ';') {
4657d4fba8b9Smrg		++s;
4658d4fba8b9Smrg	    } else if (*s != '\0') {
4659d4fba8b9Smrg		result = 0;
4660d4fba8b9Smrg	    }
4661d4fba8b9Smrg	}
4662d4fba8b9Smrg    }
4663980988aeSmrg    TRACE(("parse-chr %s ->%#x, %#x->%s\n", *cp, result, result, s));
4664d4fba8b9Smrg    *cp = s;
4665d4fba8b9Smrg    return result;
4666d4fba8b9Smrg}
4667d4fba8b9Smrg
4668980988aeSmrg#if OPT_TRACE
4669980988aeSmrg#define done_DECCIR()	do { TRACE(("...quit DECCIR @%d\n", __LINE__)); return; } while(0)
4670980988aeSmrg#else
4671980988aeSmrg#define done_DECCIR()	return
4672980988aeSmrg#endif
4673980988aeSmrg
4674d4fba8b9Smrgstatic void
4675d4fba8b9Smrgrestore_DECCIR(XtermWidget xw, const char *cp)
4676d4fba8b9Smrg{
4677d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
4678d4fba8b9Smrg    int value;
4679d4fba8b9Smrg
4680d4fba8b9Smrg    /* row */
4681d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4682980988aeSmrg	done_DECCIR();
4683d4fba8b9Smrg    screen->cur_row = (value - 1);
4684d4fba8b9Smrg
4685d4fba8b9Smrg    /* column */
4686d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4687980988aeSmrg	done_DECCIR();
4688d4fba8b9Smrg    screen->cur_col = (value - 1);
4689d4fba8b9Smrg
4690d4fba8b9Smrg    /* page */
4691d4fba8b9Smrg    if (parse_int_param(&cp) != 1)
4692980988aeSmrg	done_DECCIR();
4693d4fba8b9Smrg
4694d4fba8b9Smrg    /* rendition */
4695980988aeSmrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) {
4696980988aeSmrg	if (value & 0x10) {
4697980988aeSmrg	    /*
4698980988aeSmrg	     * VT420 is documented for bit 5 always reset; VT520/VT525 are not
4699980988aeSmrg	     * documented, but do use the bit for setting invisible mode.
4700980988aeSmrg	     */
4701980988aeSmrg	    if (screen->vtXX_level <= 4)
4702980988aeSmrg		done_DECCIR();
4703980988aeSmrg	} else if (!(value & 0x40)) {
4704980988aeSmrg	    done_DECCIR();
4705980988aeSmrg	}
4706980988aeSmrg    }
4707d4fba8b9Smrg    UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4708980988aeSmrg    xw->flags |= (value & 16) ? INVISIBLE : 0;
4709d4fba8b9Smrg    xw->flags |= (value & 8) ? INVERSE : 0;
4710d4fba8b9Smrg    xw->flags |= (value & 4) ? BLINK : 0;
4711d4fba8b9Smrg    xw->flags |= (value & 2) ? UNDERLINE : 0;
4712d4fba8b9Smrg    xw->flags |= (value & 1) ? BOLD : 0;
4713d4fba8b9Smrg
4714d4fba8b9Smrg    /* attributes */
4715d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4716980988aeSmrg	done_DECCIR();
4717d4fba8b9Smrg    screen->protected_mode &= ~DEC_PROTECT;
4718d4fba8b9Smrg    screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4719d4fba8b9Smrg
4720d4fba8b9Smrg    /* flags */
4721d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4722980988aeSmrg	done_DECCIR();
4723d4fba8b9Smrg    screen->do_wrap = (value & 8) ? True : False;
4724d4fba8b9Smrg    screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4725d4fba8b9Smrg    UIntClr(xw->flags, ORIGIN);
4726d4fba8b9Smrg    xw->flags |= (value & 1) ? ORIGIN : 0;
4727d4fba8b9Smrg
4728d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4729980988aeSmrg	done_DECCIR();
4730d4fba8b9Smrg    screen->curgl = (Char) value;
4731d4fba8b9Smrg
4732d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4733980988aeSmrg	done_DECCIR();
4734d4fba8b9Smrg    screen->curgr = (Char) value;
4735d4fba8b9Smrg
4736d4fba8b9Smrg    /* character-set size */
4737980988aeSmrg    if (parse_chr_param(&cp) == 0xffff)		/* FIXME: limit SCS? */
4738980988aeSmrg	done_DECCIR();
4739d4fba8b9Smrg
4740d4fba8b9Smrg    /* SCS designators */
4741d4fba8b9Smrg    for (value = 0; value < NUM_GSETS; ++value) {
4742980988aeSmrg	if (*cp == '\0') {
4743980988aeSmrg	    done_DECCIR();
4744980988aeSmrg	} else if (strchr("%&\"", *cp) != NULL) {
4745980988aeSmrg	    int prefix = *cp++;
4746980988aeSmrg	    xtermDecodeSCS(xw, value, 0, prefix, *cp);
4747d4fba8b9Smrg	} else {
4748980988aeSmrg	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4749d4fba8b9Smrg	}
4750d4fba8b9Smrg	cp++;
4751d4fba8b9Smrg    }
4752d4fba8b9Smrg
4753d4fba8b9Smrg    TRACE(("...done DECCIR\n"));
4754d4fba8b9Smrg}
4755d4fba8b9Smrg
4756d4fba8b9Smrgstatic void
4757d4fba8b9Smrgrestore_DECTABSR(XtermWidget xw, const char *cp)
4758d4fba8b9Smrg{
4759d4fba8b9Smrg    int stop = 0;
4760d4fba8b9Smrg    Bool fail = False;
4761d4fba8b9Smrg
4762d4fba8b9Smrg    TabZonk(xw->tabs);
4763d4fba8b9Smrg    while (*cp != '\0' && !fail) {
4764d4fba8b9Smrg	if ((*cp) >= '0' && (*cp) <= '9') {
4765d4fba8b9Smrg	    stop = (stop * 10) + ((*cp) - '0');
4766d4fba8b9Smrg	} else if (*cp == '/') {
4767d4fba8b9Smrg	    --stop;
4768d4fba8b9Smrg	    if (OkTAB(stop)) {
4769d4fba8b9Smrg		TabSet(xw->tabs, stop);
4770d4fba8b9Smrg	    }
4771980988aeSmrg	    stop = 0;
4772d4fba8b9Smrg	} else {
4773d4fba8b9Smrg	    fail = True;
4774d4fba8b9Smrg	}
4775d4fba8b9Smrg	++cp;
4776d4fba8b9Smrg    }
4777d4fba8b9Smrg    --stop;
4778d4fba8b9Smrg    if (OkTAB(stop))
4779d4fba8b9Smrg	TabSet(xw->tabs, stop);
4780d4fba8b9Smrg
4781d4fba8b9Smrg    TRACE(("...done DECTABSR\n"));
4782d4fba8b9Smrg}
4783980988aeSmrg#endif /* OPT_DEC_RECTOPS */
4784980988aeSmrg
4785980988aeSmrg/*
4786980988aeSmrg * VT510 and VT520 reference manual have the same explanation for Pn (params),
4787980988aeSmrg * but it does not agree with the possible values for Dscs because it refers
4788980988aeSmrg * to "ISO Latin-7" (ISO 8859-13 aka "Baltic Rim"), and omits ISO Greek
4789980988aeSmrg * (ISO 8859-7):
4790980988aeSmrg *
4791980988aeSmrg * ------------------------------------------------------------------------
4792980988aeSmrg * Pn  Meaning
4793980988aeSmrg * ------------------------------------------------------------------------
4794980988aeSmrg * 0   DEC, ISO Latin-1, ISO Latin-2
4795980988aeSmrg * 1   ISO Latin-5, ISO Latin-7, ISO Cyrillic, ISO Hebrew
4796980988aeSmrg * ------------------------------------------------------------------------
4797980988aeSmrg *
4798980988aeSmrg * versus
4799980988aeSmrg *
4800980988aeSmrg * ------------------------------------------------------------------------
4801980988aeSmrg * Dscs   Character Set
4802980988aeSmrg * ------------------------------------------------------------------------
4803980988aeSmrg * %5     DEC Supplemental
4804980988aeSmrg * "?     DEC Greek
4805980988aeSmrg * "4     DEC Hebrew
4806980988aeSmrg * %0     DEC Turkish
4807980988aeSmrg * &4     DEC Cyrillic
4808980988aeSmrg * <      User-preferred Supplemental
4809980988aeSmrg * A      ISO Latin-1 Supplemental
4810980988aeSmrg * B      ISO Latin-2 Supplemental
4811980988aeSmrg * F      ISO Greek Supplemental
4812980988aeSmrg * H      ISO Hebrew Supplemental
4813980988aeSmrg * M      ISO Latin-5 Supplemental
4814980988aeSmrg * L      ISO Latin-Cyrillic
4815980988aeSmrg * ------------------------------------------------------------------------
4816980988aeSmrg *
4817980988aeSmrg * DEC 070, page 5-123 explains that Pn ("Ps" in the text) selects 94 or 96
4818980988aeSmrg * character sets (0 or 1, respectively), and on the next page states that
4819980988aeSmrg * the valid combinations are 0 (DEC Supplemental) and 1 (ISO Latin-1
4820980988aeSmrg * supplemental).  The document comments in regard to LS0 that (applications)
4821980988aeSmrg * should not assume that they can use 96-character sets for G0, but that it
4822980988aeSmrg * is possible to do this using UPSS.
4823980988aeSmrg *
4824980988aeSmrg * The VT510/VT520 reference manuals under SCS Select Character Set show
4825980988aeSmrg * a list of 94- and 96-character sets with "DEC" and "NRCS" as 94-characters,
4826980988aeSmrg * and the "ISO" as 96-characters.  A few 94-character sets are added, based
4827980988aeSmrg * on testing VT520/VT525 that shows that DEC Special Graphics also is allowed.
4828980988aeSmrg */
4829980988aeSmrgstatic Bool
4830980988aeSmrgdecode_upss(XtermWidget xw, const char *cp, char psarg, DECNRCM_codes * upss)
4831980988aeSmrg{
4832980988aeSmrg    /* *INDENT-OFF* */
4833980988aeSmrg    static const struct {
4834980988aeSmrg	DECNRCM_codes code;
4835980988aeSmrg	int params;	/* 0 for 94-characters, 1 for 96-characters */
4836980988aeSmrg	int prefix;
4837980988aeSmrg	int suffix;
4838980988aeSmrg	int min_level;
4839980988aeSmrg	int max_level;
4840980988aeSmrg    } upss_table[] = {
4841980988aeSmrg	{ DFT_UPSS,               0,  '%', '5', 3, 9 },
4842980988aeSmrg    	{ nrc_ASCII,              0,  0,   'A', 1, 9 },	/* undocumented */
4843980988aeSmrg    	{ nrc_DEC_Spec_Graphic,   0,  0,   '0', 1, 9 },	/* undocumented */
4844980988aeSmrg    	{ nrc_DEC_Technical,      0,  0,   '>', 3, 9 },	/* undocumented */
4845980988aeSmrg	{ nrc_DEC_Greek_Supp,     0,  '"', '?', 5, 9 },
4846980988aeSmrg	{ nrc_DEC_Hebrew_Supp,    0,  '"', '4', 5, 9 },
4847980988aeSmrg	{ nrc_DEC_Turkish_Supp,   0,  '%', '0', 5, 9 },
4848980988aeSmrg	{ nrc_DEC_Cyrillic,       0,  '&', '4', 5, 9 },
4849980988aeSmrg	{ ALT_UPSS,               1,  0,   'A', 3, 9 },
4850980988aeSmrg	{ nrc_ISO_Latin_2_Supp,   1,  0,   'B', 5, 9 },
4851980988aeSmrg	{ nrc_ISO_Greek_Supp,     1,  0,   'F', 5, 9 },
4852980988aeSmrg	{ nrc_ISO_Hebrew_Supp,    1,  0,   'H', 5, 9 },
4853980988aeSmrg	{ nrc_ISO_Latin_5_Supp,   1,  0,   'M', 5, 9 },
4854980988aeSmrg	{ nrc_ISO_Latin_Cyrillic, 1,  0,   'L', 5, 9 },
4855980988aeSmrg    };
4856980988aeSmrg    /* *INDENT-ON* */
4857980988aeSmrg    TScreen *screen = TScreenOf(xw);
4858980988aeSmrg    Bool result = False;
4859980988aeSmrg
4860980988aeSmrg    *upss = nrc_ASCII;
4861980988aeSmrg    if (screen->vtXX_level >= 3) {
4862980988aeSmrg	Cardinal n;
4863980988aeSmrg	for (n = 0; n < XtNumber(upss_table); ++n) {
4864980988aeSmrg	    if (((int) psarg - '0') != upss_table[n].params)
4865980988aeSmrg		continue;
4866980988aeSmrg
4867980988aeSmrg	    if (cp[1] == '\0') {
4868980988aeSmrg		if (upss_table[n].suffix != cp[0])
4869980988aeSmrg		    continue;
4870980988aeSmrg	    } else if (cp[2] == '\0') {
4871980988aeSmrg		if (upss_table[n].prefix != cp[0])
4872980988aeSmrg		    continue;
4873980988aeSmrg		if (upss_table[n].suffix != cp[1])
4874980988aeSmrg		    continue;
4875980988aeSmrg	    } else {
4876980988aeSmrg		continue;
4877980988aeSmrg	    }
4878980988aeSmrg
4879980988aeSmrg	    result = True;
4880980988aeSmrg	    *upss = upss_table[n].code;
4881980988aeSmrg	    if (*upss == DFT_UPSS) {
4882980988aeSmrg		TRACE(("DECAUPSS (default)\n"));
4883980988aeSmrg	    } else if (*upss == ALT_UPSS) {
4884980988aeSmrg		TRACE(("DECAUPSS (alternate)\n"));
4885980988aeSmrg	    }
4886980988aeSmrg	    break;
4887980988aeSmrg	}
4888980988aeSmrg	TRACE(("DECAUPSS %ssuccessful %s\n",
4889980988aeSmrg	       result ? "" : "not ", visibleScsCode(*upss)));
4890980988aeSmrg    }
4891980988aeSmrg    return result;
4892980988aeSmrg}
4893d4fba8b9Smrg
4894d522f475Smrgvoid
4895fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4896d522f475Smrg{
4897cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4898d522f475Smrg    char reply[BUFSIZ];
4899cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4900d522f475Smrg    Bool okay;
4901d522f475Smrg    ANSI params;
4902d4fba8b9Smrg    char psarg = '0';
4903980988aeSmrg#if OPT_VT525_COLORS && OPT_ISO_COLORS
4904980988aeSmrg    const char *cp2;
4905980988aeSmrg#endif
4906980988aeSmrg#if (OPT_VT525_COLORS && OPT_ISO_COLORS) || OPT_MOD_FKEYS
4907980988aeSmrg    int ival;
4908d4fba8b9Smrg#endif
4909d522f475Smrg
4910cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4911d522f475Smrg
4912d522f475Smrg    if (dcslen != strlen(cp))
4913d522f475Smrg	/* shouldn't have nulls in the string */
4914d522f475Smrg	return;
4915d522f475Smrg
4916d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4917d522f475Smrg    case '$':			/* DECRQSS */
4918d522f475Smrg	okay = True;
4919d522f475Smrg
4920d522f475Smrg	cp++;
4921d4fba8b9Smrg	if (*cp == 'q') {
4922d4fba8b9Smrg	    *reply = '\0';
4923d4fba8b9Smrg	    cp++;
4924d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4925d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCA\n"));
4926d522f475Smrg		sprintf(reply, "%d%s",
4927d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4928d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4929d522f475Smrg			cp);
4930d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
49313367019cSmrg		if (screen->vtXX_level < 2) {
49323367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
49333367019cSmrg		    break;
49343367019cSmrg		}
4935d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCL\n"));
4936d522f475Smrg		sprintf(reply, "%d%s%s",
4937d522f475Smrg			(screen->vtXX_level ?
4938d522f475Smrg			 screen->vtXX_level : 1) + 60,
4939d522f475Smrg			(screen->vtXX_level >= 2)
4940d522f475Smrg			? (screen->control_eight_bits
4941d522f475Smrg			   ? ";0" : ";1")
4942d522f475Smrg			: "",
4943d522f475Smrg			cp);
4944d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4945d4fba8b9Smrg		TRACE(("DECRQSS -> DECSTBM\n"));
4946d522f475Smrg		sprintf(reply, "%d;%dr",
4947d522f475Smrg			screen->top_marg + 1,
4948d522f475Smrg			screen->bot_marg + 1);
49493367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
49503367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
4951d4fba8b9Smrg		    TRACE(("DECRQSS -> DECSLRM\n"));
49523367019cSmrg		    sprintf(reply, "%d;%ds",
49533367019cSmrg			    screen->lft_marg + 1,
49543367019cSmrg			    screen->rgt_marg + 1);
4955037a25ddSmrg		} else {
4956037a25ddSmrg		    okay = False;
49573367019cSmrg		}
4958d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4959d4fba8b9Smrg		TRACE(("DECRQSS -> SGR\n"));
4960d4fba8b9Smrg		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4961d522f475Smrg		strcat(reply, "m");
4962712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
49633367019cSmrg		int code = STEADY_BLOCK;
49643367019cSmrg		if (isCursorUnderline(screen))
49653367019cSmrg		    code = STEADY_UNDERLINE;
49663367019cSmrg		else if (isCursorBar(screen))
49673367019cSmrg		    code = STEADY_BAR;
49683367019cSmrg#if OPT_BLINK_CURS
496994644356Smrg		if (screen->cursor_blink_esc != 0)
49703367019cSmrg		    code -= 1;
49713367019cSmrg#endif
4972d4fba8b9Smrg		TRACE(("reply DECSCUSR\n"));
49733367019cSmrg		sprintf(reply, "%d%s", code, cp);
4974d4fba8b9Smrg	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4975d4fba8b9Smrg		sprintf(reply, "%d%s",
4976d4fba8b9Smrg			((screen->max_row > 24) ? screen->max_row : 24),
4977d4fba8b9Smrg			cp);
4978d4fba8b9Smrg		TRACE(("reply DECSLPP\n"));
4979d4fba8b9Smrg	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4980d4fba8b9Smrg		TRACE(("reply DECSCPP\n"));
4981d4fba8b9Smrg		sprintf(reply, "%d%s",
4982d4fba8b9Smrg			((xw->flags & IN132COLUMNS) ? 132 : 80),
4983d4fba8b9Smrg			cp);
498450027b5bSmrg	    } else
498550027b5bSmrg#if OPT_STATUS_LINE
498650027b5bSmrg	    if (!strcmp(cp, "$}")) {	/* DECSASD */
498750027b5bSmrg		TRACE(("reply DECSASD\n"));
498850027b5bSmrg		sprintf(reply, "%d%s",
498950027b5bSmrg			screen->status_active,
499050027b5bSmrg			cp);
499150027b5bSmrg	    } else if (!strcmp(cp, "$~")) {	/* DECSSDT */
499250027b5bSmrg		TRACE(("reply DECSASD\n"));
499350027b5bSmrg		sprintf(reply, "%d%s",
499450027b5bSmrg			screen->status_type,
499550027b5bSmrg			cp);
499650027b5bSmrg	    } else
4997980988aeSmrg#endif
4998980988aeSmrg#if OPT_DEC_RECTOPS
4999980988aeSmrg	    if (!strcmp(cp, "*x")) {	/* DECSACE */
5000980988aeSmrg		TRACE(("reply DECSACE\n"));
5001980988aeSmrg		sprintf(reply, "%d%s",
5002980988aeSmrg			screen->cur_decsace,
5003980988aeSmrg			cp);
5004980988aeSmrg	    } else
500550027b5bSmrg#endif
500650027b5bSmrg	    if (!strcmp(cp, "*|")) {	/* DECSNLS */
5007d4fba8b9Smrg		TRACE(("reply DECSNLS\n"));
5008d4fba8b9Smrg		sprintf(reply, "%d%s",
5009d4fba8b9Smrg			screen->max_row + 1,
5010d4fba8b9Smrg			cp);
5011980988aeSmrg	    } else
5012980988aeSmrg#if OPT_VT525_COLORS && OPT_ISO_COLORS
5013980988aeSmrg		if (screen->terminal_id == 525
5014980988aeSmrg		    && !strcmp((cp2 = skip_params(cp)), ",}")) {	/* DECATC */
5015980988aeSmrg		ival = parse_int_param(&cp);
5016980988aeSmrg		TRACE(("reply DECATC:%s\n", cp));
5017980988aeSmrg		if (ival >= 0 && ival < 16 && *cp2 == ',') {
5018980988aeSmrg		    sprintf(reply, "%d;%d;%d%s", ival,
5019980988aeSmrg			    screen->alt_colors[ival].fg,
5020980988aeSmrg			    screen->alt_colors[ival].bg,
5021980988aeSmrg			    cp2);
5022980988aeSmrg		} else {
5023980988aeSmrg		    okay = False;
5024980988aeSmrg		}
5025980988aeSmrg	    } else if (screen->terminal_id == 525
5026980988aeSmrg		       && !strcmp((cp2 = skip_params(cp)), ",|")) {	/* DECAC */
5027980988aeSmrg		ival = parse_int_param(&cp);
5028980988aeSmrg		TRACE(("reply DECAC\n"));
5029980988aeSmrg		switch (ival) {
5030980988aeSmrg		case 1:	/* normal text */
5031980988aeSmrg		    sprintf(reply, "%d,%d%s",
5032980988aeSmrg			    screen->assigned_fg,
5033980988aeSmrg			    screen->assigned_bg,
5034980988aeSmrg			    cp2);
5035980988aeSmrg		    break;
5036980988aeSmrg		case 2:	/* window frame (not implemented) */
5037980988aeSmrg		    /* FALLTHRU */
5038980988aeSmrg		default:
5039980988aeSmrg		    okay = False;
5040980988aeSmrg		    break;
5041980988aeSmrg		}
5042980988aeSmrg	    } else
5043980988aeSmrg#endif
5044980988aeSmrg#if OPT_MOD_FKEYS
5045980988aeSmrg	    if (*cp == '>' && !strcmp(skip_params(1 + cp), "m")) {	/* XTQMODKEYS */
5046980988aeSmrg		++cp;
5047980988aeSmrg		okay = True;
5048980988aeSmrg		ival = parse_int_param(&cp);
5049980988aeSmrg#define GET_MOD_FKEYS(field) xw->keyboard.modify_now.field
5050980988aeSmrg#define FMT_MOD_FKEYS(field) sprintf(reply, ">%d;%dm", ival, GET_MOD_FKEYS(field))
5051980988aeSmrg		switch (ival) {
5052980988aeSmrg		case 0:
5053980988aeSmrg		    FMT_MOD_FKEYS(allow_keys);
5054980988aeSmrg		    break;
5055980988aeSmrg		case 1:
5056980988aeSmrg		    FMT_MOD_FKEYS(cursor_keys);
5057980988aeSmrg		    break;
5058980988aeSmrg		case 2:
5059980988aeSmrg		    FMT_MOD_FKEYS(function_keys);
5060980988aeSmrg		    break;
5061980988aeSmrg		case 4:
5062980988aeSmrg		    FMT_MOD_FKEYS(other_keys);
5063980988aeSmrg		    break;
5064980988aeSmrg		default:
5065980988aeSmrg		    okay = False;
5066980988aeSmrg		    break;
5067980988aeSmrg		}
5068980988aeSmrg	    } else
5069980988aeSmrg#endif
5070980988aeSmrg	    {
5071d4fba8b9Smrg		okay = False;
507222d8e007Schristos	    }
5073d4fba8b9Smrg
5074d4fba8b9Smrg	    unparseputc1(xw, ANSI_DCS);
5075d4fba8b9Smrg	    unparseputc(xw, okay ? '1' : '0');
5076d4fba8b9Smrg	    unparseputc(xw, '$');
5077d4fba8b9Smrg	    unparseputc(xw, 'r');
5078d4fba8b9Smrg	    cp = reply;
5079d4fba8b9Smrg	    unparseputs(xw, cp);
5080d4fba8b9Smrg	    unparseputc1(xw, ANSI_ST);
5081d522f475Smrg	} else {
5082d522f475Smrg	    unparseputc(xw, ANSI_CAN);
5083d522f475Smrg	}
5084d522f475Smrg	break;
5085d522f475Smrg    case '+':
5086d522f475Smrg	cp++;
5087cd3331d0Smrg	switch (*cp) {
5088d4fba8b9Smrg#if OPT_TCAP_QUERY
5089d1603babSmrg	case 'p':		/* XTSETTCAP */
5090cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
5091cd3331d0Smrg		set_termcap(xw, cp + 1);
5092cd3331d0Smrg	    }
5093cd3331d0Smrg	    break;
5094d1603babSmrg	case 'q':		/* XTGETTCAP */
5095cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
5096cd3331d0Smrg		Bool fkey;
5097cd3331d0Smrg		unsigned state;
5098cd3331d0Smrg		int code;
5099cd3331d0Smrg		const char *tmp;
5100cd3331d0Smrg		const char *parsed = ++cp;
5101d522f475Smrg
5102cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
5103d522f475Smrg
5104cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
5105b7c89284Ssnj
5106cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
5107d522f475Smrg
5108cd3331d0Smrg		unparseputc(xw, '+');
5109cd3331d0Smrg		unparseputc(xw, 'r');
5110d522f475Smrg
5111cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
5112cd3331d0Smrg		    if (cp == parsed)
5113cd3331d0Smrg			break;	/* no data found, error */
5114d522f475Smrg
5115cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
5116cd3331d0Smrg			unparseputc(xw, *tmp);
5117d522f475Smrg
5118cd3331d0Smrg		    if (code >= 0) {
5119cd3331d0Smrg			unparseputc(xw, '=');
5120cd3331d0Smrg			screen->tc_query_code = code;
5121cd3331d0Smrg			screen->tc_query_fkey = fkey;
5122d522f475Smrg#if OPT_ISO_COLORS
5123cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
5124cd3331d0Smrg			 * number of colors) */
5125cd3331d0Smrg			if (code == XK_COLORS) {
5126d4fba8b9Smrg			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
5127d4fba8b9Smrg			} else
5128d4fba8b9Smrg#if OPT_DIRECT_COLOR
5129d4fba8b9Smrg			if (code == XK_RGB) {
5130d4fba8b9Smrg			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
5131d4fba8b9Smrg				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
5132d4fba8b9Smrg				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
5133d4fba8b9Smrg				    unparseputn(xw, xw->rgb_widths[0]);
5134d4fba8b9Smrg				} else {
5135d4fba8b9Smrg				    char temp[1024];
5136d1603babSmrg				    sprintf(temp, "%u/%u/%u",
5137d4fba8b9Smrg					    xw->rgb_widths[0],
5138d4fba8b9Smrg					    xw->rgb_widths[1],
5139d4fba8b9Smrg					    xw->rgb_widths[2]);
5140d4fba8b9Smrg				    unparseputs(xw, temp);
5141d4fba8b9Smrg				}
5142d4fba8b9Smrg			    } else {
5143d4fba8b9Smrg				unparseputs(xw, "-1");
5144d4fba8b9Smrg			    }
5145cd3331d0Smrg			} else
5146d4fba8b9Smrg#endif
5147cd3331d0Smrg#endif
5148cd3331d0Smrg			if (code == XK_TCAPNAME) {
5149c219fbebSmrg			    unparseputs(xw, resource.term_name);
5150cd3331d0Smrg			} else {
5151cd3331d0Smrg			    XKeyEvent event;
5152d4fba8b9Smrg			    memset(&event, 0, sizeof(event));
5153cd3331d0Smrg			    event.state = state;
5154cd3331d0Smrg			    Input(xw, &event, False);
5155cd3331d0Smrg			}
5156cd3331d0Smrg			screen->tc_query_code = -1;
5157cd3331d0Smrg		    } else {
5158cd3331d0Smrg			break;	/* no match found, error */
5159d522f475Smrg		    }
5160d522f475Smrg
5161d522f475Smrg		    cp = parsed;
5162cd3331d0Smrg		    if (*parsed == ';') {
5163cd3331d0Smrg			unparseputc(xw, *parsed++);
5164cd3331d0Smrg			cp = parsed;
5165cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
5166cd3331d0Smrg		    }
5167d522f475Smrg		}
5168cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
5169d522f475Smrg	    }
5170cd3331d0Smrg	    break;
5171d4fba8b9Smrg#endif
5172d4fba8b9Smrg#if OPT_XRES_QUERY
5173d1603babSmrg	case 'Q':		/* XTGETXRES */
5174d4fba8b9Smrg	    ++cp;
5175d4fba8b9Smrg	    if (AllowXResOps(xw)) {
5176d4fba8b9Smrg		Boolean first = True;
5177d1603babSmrg		okay = True;
5178d1603babSmrg		while (*cp != '\0' && okay) {
5179d4fba8b9Smrg		    const char *parsed = 0;
5180d4fba8b9Smrg		    const char *tmp;
5181d4fba8b9Smrg		    char *name = x_decode_hex(cp, &parsed);
5182d4fba8b9Smrg		    char *value;
5183d4fba8b9Smrg		    char *result;
5184d4fba8b9Smrg		    if (cp == parsed || name == NULL) {
5185d4fba8b9Smrg			free(name);
5186d4fba8b9Smrg			break;	/* no data found, error */
5187d4fba8b9Smrg		    }
5188d1603babSmrg		    if ((cp - parsed) > 1024) {
5189d1603babSmrg			free(name);
5190d1603babSmrg			break;	/* ignore improbable resource */
5191d1603babSmrg		    }
5192d4fba8b9Smrg		    TRACE(("query-feature '%s'\n", name));
5193d4fba8b9Smrg		    if ((value = vt100ResourceToString(xw, name)) != 0) {
5194d4fba8b9Smrg			okay = True;	/* valid */
5195d4fba8b9Smrg		    } else {
5196d4fba8b9Smrg			okay = False;	/* invalid */
5197d4fba8b9Smrg		    }
5198d4fba8b9Smrg		    if (first) {
5199d4fba8b9Smrg			unparseputc1(xw, ANSI_DCS);
5200d4fba8b9Smrg			unparseputc(xw, okay ? '1' : '0');
5201d4fba8b9Smrg			unparseputc(xw, '+');
5202d4fba8b9Smrg			unparseputc(xw, 'R');
5203d4fba8b9Smrg			first = False;
5204d4fba8b9Smrg		    }
5205d4fba8b9Smrg
5206d4fba8b9Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
5207d4fba8b9Smrg			unparseputc(xw, *tmp);
5208d4fba8b9Smrg
5209d4fba8b9Smrg		    if (value != 0) {
5210d4fba8b9Smrg			unparseputc1(xw, '=');
5211d4fba8b9Smrg			result = x_encode_hex(value);
5212d4fba8b9Smrg			unparseputs(xw, result);
5213d4fba8b9Smrg		    } else {
5214d4fba8b9Smrg			result = NULL;
5215d4fba8b9Smrg		    }
5216d4fba8b9Smrg
5217d4fba8b9Smrg		    free(name);
5218d4fba8b9Smrg		    free(value);
5219d4fba8b9Smrg		    free(result);
5220d4fba8b9Smrg
5221d4fba8b9Smrg		    cp = parsed;
5222d4fba8b9Smrg		    if (*parsed == ';') {
5223d4fba8b9Smrg			unparseputc(xw, *parsed++);
5224d4fba8b9Smrg			cp = parsed;
5225d4fba8b9Smrg		    }
5226d4fba8b9Smrg		}
5227d4fba8b9Smrg		if (!first)
5228d4fba8b9Smrg		    unparseputc1(xw, ANSI_ST);
5229d4fba8b9Smrg	    }
5230d4fba8b9Smrg	    break;
5231d4fba8b9Smrg#endif
5232d522f475Smrg	}
5233d522f475Smrg	break;
5234980988aeSmrg    case '0':
5235980988aeSmrg	/* FALLTHRU */
5236d4fba8b9Smrg    case '1':
5237980988aeSmrg	if (screen->vtXX_level >= 3 && *skip_params(cp) == '!') {
5238980988aeSmrg	    DECNRCM_codes upss;
5239980988aeSmrg	    psarg = *cp++;
5240980988aeSmrg	    if (*cp++ == '!' && *cp++ == 'u') {
5241980988aeSmrg#if OPT_WIDE_CHARS
5242980988aeSmrg		if (screen->wide_chars && screen->utf8_mode) {
5243980988aeSmrg		    ;		/* EMPTY */
5244980988aeSmrg		} else
5245980988aeSmrg#endif
5246980988aeSmrg		if (decode_upss(xw, cp, psarg, &upss)) {
5247980988aeSmrg		    screen->gsets_upss = upss;
5248980988aeSmrg		}
5249980988aeSmrg	    }
5250980988aeSmrg	    break;
5251980988aeSmrg	}
5252980988aeSmrg#if OPT_DEC_RECTOPS
5253d4fba8b9Smrg	/* FALLTHRU */
5254d4fba8b9Smrg    case '2':
5255d4fba8b9Smrg	if (*skip_params(cp) == '$') {
5256d4fba8b9Smrg	    psarg = *cp++;
5257d4fba8b9Smrg	    if ((*cp++ == '$')
5258d4fba8b9Smrg		&& (*cp++ == 't')
5259d4fba8b9Smrg		&& (screen->vtXX_level >= 3)) {
5260d4fba8b9Smrg		switch (psarg) {
5261d4fba8b9Smrg		case '1':
5262d4fba8b9Smrg		    TRACE(("DECRSPS (DECCIR)\n"));
5263d4fba8b9Smrg		    restore_DECCIR(xw, cp);
5264d4fba8b9Smrg		    break;
5265d4fba8b9Smrg		case '2':
5266d4fba8b9Smrg		    TRACE(("DECRSPS (DECTABSR)\n"));
5267d4fba8b9Smrg		    restore_DECTABSR(xw, cp);
5268d4fba8b9Smrg		    break;
5269d4fba8b9Smrg		}
5270d4fba8b9Smrg	    }
5271d4fba8b9Smrg	    break;
5272d4fba8b9Smrg	}
5273d522f475Smrg#endif
5274d4fba8b9Smrg	/* FALLTHRU */
5275d522f475Smrg    default:
5276d4fba8b9Smrg	if (optRegisGraphics(screen) ||
5277d4fba8b9Smrg	    optSixelGraphics(screen) ||
5278fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
52790d92cbfdSchristos	    parse_ansi_params(&params, &cp);
52800d92cbfdSchristos	    switch (params.a_final) {
5281d4fba8b9Smrg	    case 'p':		/* ReGIS */
52829a64e1c5Smrg#if OPT_REGIS_GRAPHICS
5283d4fba8b9Smrg		if (optRegisGraphics(screen)) {
5284fa3f02f3Smrg		    parse_regis(xw, &params, cp);
5285fa3f02f3Smrg		}
52869a64e1c5Smrg#else
52879a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
52889a64e1c5Smrg#endif
5289fa3f02f3Smrg		break;
5290d4fba8b9Smrg	    case 'q':		/* sixel */
52919a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
5292d4fba8b9Smrg		if (optSixelGraphics(screen)) {
5293980988aeSmrg		    parse_sixel_init(xw, &params);
5294980988aeSmrg		    while (*cp) {
5295980988aeSmrg			parse_sixel_char(*cp++);
5296980988aeSmrg		    }
5297980988aeSmrg		    parse_sixel_finished(xw);
5298980988aeSmrg		    TRACE(("DONE parsed sixel data\n"));
5299fa3f02f3Smrg		}
53009a64e1c5Smrg#else
53019a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
5302fa3f02f3Smrg#endif
53039a64e1c5Smrg		break;
53040d92cbfdSchristos	    case '|':		/* DECUDK */
53059a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
53069a64e1c5Smrg		    if (params.a_param[0] == 0)
53079a64e1c5Smrg			reset_decudk(xw);
53089a64e1c5Smrg		    parse_decudk(xw, cp);
53099a64e1c5Smrg		}
53100d92cbfdSchristos		break;
531194644356Smrg	    case L_CURL:	/* DECDLD */
53129a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
53139a64e1c5Smrg		    parse_decdld(&params, cp);
53149a64e1c5Smrg		}
53150d92cbfdSchristos		break;
53160d92cbfdSchristos	    }
5317d522f475Smrg	}
5318d522f475Smrg	break;
5319d522f475Smrg    }
5320d522f475Smrg    unparse_end(xw);
5321d522f475Smrg}
5322d522f475Smrg
5323cb4a1343Smrg#if OPT_DEC_RECTOPS
5324cb4a1343Smrgenum {
5325cb4a1343Smrg    mdUnknown = 0,
5326cb4a1343Smrg    mdMaybeSet = 1,
5327cb4a1343Smrg    mdMaybeReset = 2,
5328cb4a1343Smrg    mdAlwaysSet = 3,
5329cb4a1343Smrg    mdAlwaysReset = 4
5330cb4a1343Smrg};
5331cb4a1343Smrg
5332cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
53333367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
5334cb4a1343Smrg
5335cb4a1343Smrg/*
5336cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
5337cb4a1343Smrg * 0 - not recognized
5338cb4a1343Smrg * 1 - set
5339cb4a1343Smrg * 2 - reset
5340cb4a1343Smrg * 3 - permanently set
5341cb4a1343Smrg * 4 - permanently reset
5342cb4a1343Smrg * Only one mode can be reported at a time.
5343cb4a1343Smrg */
5344cb4a1343Smrgvoid
5345d4fba8b9Smrgdo_ansi_rqm(XtermWidget xw, int nparams, int *params)
5346cb4a1343Smrg{
5347cb4a1343Smrg    ANSI reply;
5348cb4a1343Smrg    int count = 0;
5349cb4a1343Smrg
5350d4fba8b9Smrg    TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
5351cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5352037a25ddSmrg
5353cb4a1343Smrg    if (nparams >= 1) {
5354d4fba8b9Smrg	int result = mdUnknown;
5355037a25ddSmrg
5356d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5357cb4a1343Smrg	switch (params[0]) {
5358cb4a1343Smrg	case 1:		/* GATM */
5359cb4a1343Smrg	    result = mdAlwaysReset;
5360cb4a1343Smrg	    break;
5361cb4a1343Smrg	case 2:
5362cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
5363cb4a1343Smrg	    break;
5364cb4a1343Smrg	case 3:		/* CRM */
5365cb4a1343Smrg	    result = mdMaybeReset;
5366cb4a1343Smrg	    break;
5367cb4a1343Smrg	case 4:
5368cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
5369cb4a1343Smrg	    break;
5370cb4a1343Smrg	case 5:		/* SRTM */
5371cb4a1343Smrg	case 7:		/* VEM */
5372cb4a1343Smrg	case 10:		/* HEM */
5373cb4a1343Smrg	case 11:		/* PUM */
5374cb4a1343Smrg	    result = mdAlwaysReset;
5375cb4a1343Smrg	    break;
5376cb4a1343Smrg	case 12:
5377cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
5378cb4a1343Smrg	    break;
5379cb4a1343Smrg	case 13:		/* FEAM */
5380cb4a1343Smrg	case 14:		/* FETM */
5381cb4a1343Smrg	case 15:		/* MATM */
5382cb4a1343Smrg	case 16:		/* TTM */
5383cb4a1343Smrg	case 17:		/* SATM */
5384cb4a1343Smrg	case 18:		/* TSM */
5385cb4a1343Smrg	case 19:		/* EBM */
5386cb4a1343Smrg	    result = mdAlwaysReset;
5387cb4a1343Smrg	    break;
5388cb4a1343Smrg	case 20:
5389cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
5390cb4a1343Smrg	    break;
5391cb4a1343Smrg	}
5392cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5393cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5394cb4a1343Smrg    }
5395cb4a1343Smrg    reply.a_type = ANSI_CSI;
5396cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5397cb4a1343Smrg    reply.a_inters = '$';
5398cb4a1343Smrg    reply.a_final = 'y';
5399cb4a1343Smrg    unparseseq(xw, &reply);
5400cb4a1343Smrg}
5401cb4a1343Smrg
5402cb4a1343Smrgvoid
5403d4fba8b9Smrgdo_dec_rqm(XtermWidget xw, int nparams, int *params)
5404cb4a1343Smrg{
5405cb4a1343Smrg    ANSI reply;
5406cb4a1343Smrg    int count = 0;
5407cb4a1343Smrg
5408d4fba8b9Smrg    TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5409cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5410037a25ddSmrg
5411cb4a1343Smrg    if (nparams >= 1) {
5412cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
5413d4fba8b9Smrg	int result = mdUnknown;
5414cb4a1343Smrg
5415d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5416d4fba8b9Smrg	switch ((DECSET_codes) params[0]) {
5417fa3f02f3Smrg	case srm_DECCKM:
5418cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5419cb4a1343Smrg	    break;
5420fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
5421cb4a1343Smrg#if OPT_VT52_MODE
54223367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
5423cb4a1343Smrg#else
5424cb4a1343Smrg	    result = mdMaybeSet;
5425cb4a1343Smrg#endif
5426cb4a1343Smrg	    break;
5427fa3f02f3Smrg	case srm_DECCOLM:
5428cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
5429cb4a1343Smrg	    break;
5430fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
5431cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5432cb4a1343Smrg	    break;
5433fa3f02f3Smrg	case srm_DECSCNM:
5434cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5435cb4a1343Smrg	    break;
5436fa3f02f3Smrg	case srm_DECOM:
5437cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
5438cb4a1343Smrg	    break;
5439fa3f02f3Smrg	case srm_DECAWM:
5440cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
5441cb4a1343Smrg	    break;
5442fa3f02f3Smrg	case srm_DECARM:
5443cb4a1343Smrg	    result = mdAlwaysReset;
5444cb4a1343Smrg	    break;
5445fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
5446cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5447cb4a1343Smrg	    break;
5448cb4a1343Smrg#if OPT_TOOLBAR
5449fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
5450cb4a1343Smrg	    result = MdBool(resource.toolBar);
5451cb4a1343Smrg	    break;
5452cb4a1343Smrg#endif
5453cb4a1343Smrg#if OPT_BLINK_CURS
5454d4fba8b9Smrg	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5455d4fba8b9Smrg	    result = MdBool(screen->cursor_blink_esc);
5456d4fba8b9Smrg	    break;
5457d4fba8b9Smrg	case srm_CURSOR_BLINK_OPS:
5458d4fba8b9Smrg	    switch (screen->cursor_blink) {
5459d4fba8b9Smrg	    case cbTrue:
5460d4fba8b9Smrg		result = mdMaybeSet;
5461d4fba8b9Smrg		break;
5462d4fba8b9Smrg	    case cbFalse:
5463d4fba8b9Smrg		result = mdMaybeReset;
5464d4fba8b9Smrg		break;
5465d4fba8b9Smrg	    case cbAlways:
5466d4fba8b9Smrg		result = mdAlwaysSet;
5467d4fba8b9Smrg		break;
5468d4fba8b9Smrg	    case cbLAST:
5469d4fba8b9Smrg		/* FALLTHRU */
5470d4fba8b9Smrg	    case cbNever:
5471d4fba8b9Smrg		result = mdAlwaysReset;
5472d4fba8b9Smrg		break;
5473d4fba8b9Smrg	    }
5474d4fba8b9Smrg	    break;
5475d4fba8b9Smrg	case srm_XOR_CURSOR_BLINKS:
5476d4fba8b9Smrg	    result = (screen->cursor_blink_xor
5477d4fba8b9Smrg		      ? mdAlwaysSet
5478d4fba8b9Smrg		      : mdAlwaysReset);
5479cb4a1343Smrg	    break;
5480cb4a1343Smrg#endif
5481fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
5482712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
5483cb4a1343Smrg	    break;
5484fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
5485712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
5486cb4a1343Smrg	    break;
5487fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5488cb4a1343Smrg	    result = MdBool(screen->cursor_set);
5489cb4a1343Smrg	    break;
5490fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
5491cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5492cb4a1343Smrg	    break;
5493cb4a1343Smrg#if OPT_SHIFT_FONTS
5494fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
5495cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
5496cb4a1343Smrg	    break;
5497cb4a1343Smrg#endif
5498cb4a1343Smrg#if OPT_TEK4014
5499fa3f02f3Smrg	case srm_DECTEK:
5500cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
5501cb4a1343Smrg	    break;
5502cb4a1343Smrg#endif
5503fa3f02f3Smrg	case srm_132COLS:
5504cb4a1343Smrg	    result = MdBool(screen->c132);
5505cb4a1343Smrg	    break;
5506fa3f02f3Smrg	case srm_CURSES_HACK:
5507cb4a1343Smrg	    result = MdBool(screen->curses);
5508cb4a1343Smrg	    break;
5509fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
5510d4fba8b9Smrg	    if (screen->vtXX_level >= 2) {
5511d4fba8b9Smrg		result = MdFlag(xw->flags, NATIONAL);
5512d4fba8b9Smrg	    } else {
5513d4fba8b9Smrg		result = 0;
5514d4fba8b9Smrg	    }
5515cb4a1343Smrg	    break;
5516fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
5517cb4a1343Smrg	    result = MdBool(screen->marginbell);
5518cb4a1343Smrg	    break;
5519d4fba8b9Smrg#if OPT_PRINT_GRAPHICS
5520d4fba8b9Smrg	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5521d4fba8b9Smrg	    result = MdBool(screen->graphics_expanded_print_mode);
5522d4fba8b9Smrg	    break;
5523d4fba8b9Smrg#endif
5524fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
5525d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5526d4fba8b9Smrg		result = MdFlag(xw->flags, REVERSEWRAP);
5527cb4a1343Smrg	    break;
5528980988aeSmrg	case srm_REVERSEWRAP2:	/* extended reverse wraparound   */
5529980988aeSmrg	    result = MdFlag(xw->flags, REVERSEWRAP2);
5530980988aeSmrg	    break;
5531d4fba8b9Smrg#if defined(ALLOWLOGGING)
5532fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
5533d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5534d4fba8b9Smrg#if defined(ALLOWLOGFILEONOFF)
5535d4fba8b9Smrg		result = MdBool(screen->logging);
5536d4fba8b9Smrg#else
5537d4fba8b9Smrg		result = ((MdBool(screen->logging) == mdMaybeSet)
5538d4fba8b9Smrg			  ? mdAlwaysSet
5539d4fba8b9Smrg			  : mdAlwaysReset);
5540d4fba8b9Smrg#endif
5541d4fba8b9Smrg	    break;
5542d4fba8b9Smrg#elif OPT_PRINT_GRAPHICS
5543d4fba8b9Smrg	case srm_DECGPBM:	/* Graphics Print Background Mode */
5544d4fba8b9Smrg	    result = MdBool(screen->graphics_print_background_mode);
5545cb4a1343Smrg	    break;
5546cb4a1343Smrg#endif
5547fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5548cb4a1343Smrg	    /* FALLTHRU */
5549fa3f02f3Smrg	case srm_OPT_ALTBUF:
5550cb4a1343Smrg	    result = MdBool(screen->whichBuf);
5551cb4a1343Smrg	    break;
5552d4fba8b9Smrg	case srm_ALTBUF:
5553d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5554d4fba8b9Smrg		result = MdBool(screen->whichBuf);
5555d4fba8b9Smrg	    break;
5556fa3f02f3Smrg	case srm_DECNKM:
5557cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5558cb4a1343Smrg	    break;
5559fa3f02f3Smrg	case srm_DECBKM:
5560cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5561cb4a1343Smrg	    break;
5562fa3f02f3Smrg	case srm_DECLRMM:
5563d4fba8b9Smrg	    if (screen->vtXX_level >= 4) {	/* VT420 */
5564d4fba8b9Smrg		result = MdFlag(xw->flags, LEFT_RIGHT);
5565d4fba8b9Smrg	    } else {
5566d4fba8b9Smrg		result = 0;
5567d4fba8b9Smrg	    }
55683367019cSmrg	    break;
5569fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
5570fa3f02f3Smrg	case srm_DECSDM:
5571fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5572fa3f02f3Smrg	    break;
5573fa3f02f3Smrg#endif
5574fa3f02f3Smrg	case srm_DECNCSM:
5575d4fba8b9Smrg	    if (screen->vtXX_level >= 5) {	/* VT510 */
5576d4fba8b9Smrg		result = MdFlag(xw->flags, NOCLEAR_COLM);
5577d4fba8b9Smrg	    } else {
5578d4fba8b9Smrg		result = 0;
5579d4fba8b9Smrg	    }
55803367019cSmrg	    break;
5581d4fba8b9Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5582cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5583cb4a1343Smrg	    break;
5584fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5585cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5586cb4a1343Smrg	    break;
5587fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
5588cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5589cb4a1343Smrg	    break;
5590fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
5591cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5592cb4a1343Smrg	    break;
5593cb4a1343Smrg#if OPT_FOCUS_EVENT
5594fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
5595cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
5596cb4a1343Smrg	    break;
5597cb4a1343Smrg#endif
5598fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
55993367019cSmrg	    /* FALLTHRU */
5600fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
56013367019cSmrg	    /* FALLTHRU */
5602fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
5603d4fba8b9Smrg	    /* FALLTHRU */
5604d4fba8b9Smrg	case srm_PIXEL_POSITION_MOUSE:
56053367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
56063367019cSmrg	    break;
5607fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
56083367019cSmrg	    result = MdBool(screen->alternateScroll);
5609cb4a1343Smrg	    break;
5610fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
5611cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
5612cb4a1343Smrg	    break;
5613fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5614cb4a1343Smrg	    result = MdBool(screen->scrollkey);
5615cb4a1343Smrg	    break;
5616fa3f02f3Smrg	case srm_EIGHT_BIT_META:
56173367019cSmrg	    result = MdBool(screen->eight_bit_meta);
5618cb4a1343Smrg	    break;
5619cb4a1343Smrg#if OPT_NUM_LOCK
5620fa3f02f3Smrg	case srm_REAL_NUMLOCK:
5621cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
5622cb4a1343Smrg	    break;
5623fa3f02f3Smrg	case srm_META_SENDS_ESC:
5624cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
5625cb4a1343Smrg	    break;
5626cb4a1343Smrg#endif
5627fa3f02f3Smrg	case srm_DELETE_IS_DEL:
5628d4fba8b9Smrg	    result = MdBool(xtermDeleteIsDEL(xw));
5629cb4a1343Smrg	    break;
5630cb4a1343Smrg#if OPT_NUM_LOCK
5631fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
5632cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
5633cb4a1343Smrg	    break;
5634cb4a1343Smrg#endif
5635fa3f02f3Smrg	case srm_KEEP_SELECTION:
5636cb4a1343Smrg	    result = MdBool(screen->keepSelection);
5637cb4a1343Smrg	    break;
5638fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
5639cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
5640cb4a1343Smrg	    break;
5641fa3f02f3Smrg	case srm_BELL_IS_URGENT:
5642cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
5643cb4a1343Smrg	    break;
5644fa3f02f3Smrg	case srm_POP_ON_BELL:
5645cb4a1343Smrg	    result = MdBool(screen->poponbell);
5646cb4a1343Smrg	    break;
5647d4fba8b9Smrg	case srm_KEEP_CLIPBOARD:
5648d4fba8b9Smrg	    result = MdBool(screen->keepClipboard);
5649d4fba8b9Smrg	    break;
5650d4fba8b9Smrg	case srm_ALLOW_ALTBUF:
5651d4fba8b9Smrg	    result = MdBool(xw->misc.titeInhibit);
5652d4fba8b9Smrg	    break;
5653d4fba8b9Smrg	case srm_SAVE_CURSOR:
5654cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
5655cb4a1343Smrg	    break;
5656980988aeSmrg	case srm_FAST_SCROLL:
5657980988aeSmrg	    result = MdBool(screen->fastscroll);
5658980988aeSmrg	    break;
5659cb4a1343Smrg#if OPT_TCAP_FKEYS
5660fa3f02f3Smrg	case srm_TCAP_FKEYS:
5661cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5662cb4a1343Smrg	    break;
5663cb4a1343Smrg#endif
5664cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
5665fa3f02f3Smrg	case srm_SUN_FKEYS:
5666cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5667cb4a1343Smrg	    break;
5668cb4a1343Smrg#endif
5669cb4a1343Smrg#if OPT_HP_FUNC_KEYS
5670fa3f02f3Smrg	case srm_HP_FKEYS:
5671cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5672cb4a1343Smrg	    break;
5673cb4a1343Smrg#endif
5674cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
5675fa3f02f3Smrg	case srm_SCO_FKEYS:
5676cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5677cb4a1343Smrg	    break;
5678cb4a1343Smrg#endif
5679fa3f02f3Smrg	case srm_LEGACY_FKEYS:
5680cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5681cb4a1343Smrg	    break;
5682cb4a1343Smrg#if OPT_SUNPC_KBD
5683fa3f02f3Smrg	case srm_VT220_FKEYS:
5684cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5685cb4a1343Smrg	    break;
5686cb4a1343Smrg#endif
5687d4fba8b9Smrg#if OPT_PASTE64 || OPT_READLINE
5688d4fba8b9Smrg	case srm_PASTE_IN_BRACKET:
5689d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5690d4fba8b9Smrg	    break;
5691d4fba8b9Smrg#endif
5692cb4a1343Smrg#if OPT_READLINE
5693fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
5694d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5695cb4a1343Smrg	    break;
5696fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
5697d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5698cb4a1343Smrg	    break;
5699fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
5700d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5701cb4a1343Smrg	    break;
5702fa3f02f3Smrg	case srm_PASTE_QUOTE:
5703d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5704cb4a1343Smrg	    break;
5705fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
5706d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5707cb4a1343Smrg	    break;
5708cb4a1343Smrg#endif /* OPT_READLINE */
5709d4fba8b9Smrg#if OPT_GRAPHICS
57109a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
57119a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
57129a64e1c5Smrg	    break;
57139a64e1c5Smrg#endif
57149a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
57159a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
57169a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
57179a64e1c5Smrg	    break;
57189a64e1c5Smrg#endif
5719980988aeSmrg	case srm_DECARSM:	/* ignore */
5720980988aeSmrg	case srm_DECATCBM:	/* ignore */
5721980988aeSmrg	case srm_DECATCUM:	/* ignore */
5722980988aeSmrg	case srm_DECBBSM:	/* ignore */
5723980988aeSmrg	case srm_DECCAAM:	/* ignore */
5724980988aeSmrg	case srm_DECCANSM:	/* ignore */
5725980988aeSmrg	case srm_DECCAPSLK:	/* ignore */
5726980988aeSmrg	case srm_DECCRTSM:	/* ignore */
5727980988aeSmrg	case srm_DECECM:	/* ignore */
5728980988aeSmrg	case srm_DECFWM:	/* ignore */
5729980988aeSmrg	case srm_DECHCCM:	/* ignore */
5730980988aeSmrg	case srm_DECHDPXM:	/* ignore */
5731980988aeSmrg	case srm_DECHEM:	/* ignore */
5732980988aeSmrg	case srm_DECHWUM:	/* ignore */
5733980988aeSmrg	case srm_DECIPEM:	/* ignore */
5734980988aeSmrg	case srm_DECKBUM:	/* ignore */
5735980988aeSmrg	case srm_DECKLHIM:	/* ignore */
5736980988aeSmrg	case srm_DECKPM:	/* ignore */
5737980988aeSmrg	case srm_DECRLM:	/* ignore */
5738980988aeSmrg	case srm_DECMCM:	/* ignore */
5739980988aeSmrg	case srm_DECNAKB:	/* ignore */
5740980988aeSmrg	case srm_DECNULM:	/* ignore */
5741980988aeSmrg	case srm_DECNUMLK:	/* ignore */
5742980988aeSmrg	case srm_DECOSCNM:	/* ignore */
5743980988aeSmrg	case srm_DECPCCM:	/* ignore */
5744980988aeSmrg	case srm_DECRLCM:	/* ignore */
5745980988aeSmrg	case srm_DECRPL:	/* ignore */
5746980988aeSmrg	case srm_DECVCCM:	/* ignore */
5747980988aeSmrg	case srm_DECXRLM:	/* ignore */
57489a64e1c5Smrg	default:
57499a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
57509a64e1c5Smrg		   params[0]));
5751cb4a1343Smrg	}
5752cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5753cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5754d4fba8b9Smrg	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5755cb4a1343Smrg    }
5756cb4a1343Smrg    reply.a_type = ANSI_CSI;
5757cb4a1343Smrg    reply.a_pintro = '?';
5758cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5759cb4a1343Smrg    reply.a_inters = '$';
5760cb4a1343Smrg    reply.a_final = 'y';
5761cb4a1343Smrg    unparseseq(xw, &reply);
5762cb4a1343Smrg}
5763cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
5764cb4a1343Smrg
5765d522f475Smrgchar *
57669a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
5767d522f475Smrg{
5768d4fba8b9Smrg    char *result = NULL;
5769d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
57709a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
5771d4fba8b9Smrg	result = xw->work.user_keys[keycode].str;
5772d4fba8b9Smrg	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5773d4fba8b9Smrg    } else {
5774d4fba8b9Smrg	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5775d4fba8b9Smrg    }
5776d4fba8b9Smrg    return result;
5777d4fba8b9Smrg}
5778d4fba8b9Smrg
5779d4fba8b9Smrg#if OPT_REPORT_ICONS
5780d4fba8b9Smrgvoid
5781d4fba8b9Smrgreport_icons(const char *fmt, ...)
5782d4fba8b9Smrg{
5783d4fba8b9Smrg    if (resource.reportIcons) {
5784d4fba8b9Smrg	va_list ap;
5785d4fba8b9Smrg	va_start(ap, fmt);
5786d4fba8b9Smrg	vfprintf(stdout, fmt, ap);
5787d4fba8b9Smrg	va_end(ap);
5788d4fba8b9Smrg#if OPT_TRACE
5789d4fba8b9Smrg	va_start(ap, fmt);
5790d4fba8b9Smrg	TraceVA(fmt, ap);
5791d4fba8b9Smrg	va_end(ap);
5792d4fba8b9Smrg#endif
5793d522f475Smrg    }
5794d522f475Smrg}
5795d4fba8b9Smrg#endif
5796d522f475Smrg
57973367019cSmrg#ifdef HAVE_LIBXPM
57983367019cSmrg
57993367019cSmrg#ifndef PIXMAP_ROOTDIR
58003367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
58013367019cSmrg#endif
58023367019cSmrg
58033367019cSmrgtypedef struct {
58043367019cSmrg    const char *name;
58053367019cSmrg    const char *const *data;
58063367019cSmrg} XPM_DATA;
58073367019cSmrg
58083367019cSmrgstatic char *
5809d4fba8b9Smrgx_find_icon(char **work, int *state, const char *filename, const char *suffix)
58103367019cSmrg{
58113367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
58123367019cSmrg    const char *larger = "_48x48";
58133367019cSmrg    char *result = 0;
58143367019cSmrg
58153367019cSmrg    if (*state >= 0) {
58163367019cSmrg	if ((*state & 1) == 0)
58173367019cSmrg	    suffix = "";
58183367019cSmrg	if ((*state & 2) == 0)
58193367019cSmrg	    larger = "";
58203367019cSmrg	if ((*state & 4) == 0) {
58213367019cSmrg	    prefix = "";
58223367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
58233367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
58243367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
58253367019cSmrg	    *state = -1;
58263367019cSmrg	} else if (*state >= 8) {
58273367019cSmrg	    *state = -1;
58283367019cSmrg	}
58293367019cSmrg    }
58303367019cSmrg
58313367019cSmrg    if (*state >= 0) {
5832037a25ddSmrg	size_t length;
5833037a25ddSmrg
5834d4fba8b9Smrg	FreeAndNull(*work);
58353367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
58363367019cSmrg	    strlen(suffix);
58373367019cSmrg	if ((result = malloc(length)) != 0) {
58383367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
58393367019cSmrg	    *work = result;
58403367019cSmrg	}
58413367019cSmrg	*state += 1;
58423367019cSmrg    }
5843d4fba8b9Smrg    TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
58443367019cSmrg    return result;
58453367019cSmrg}
58463367019cSmrg
58473367019cSmrg#if OPT_BUILTIN_XPMS
5848d4fba8b9Smrg
58493367019cSmrgstatic const XPM_DATA *
5850d4fba8b9Smrgbuilt_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
58513367019cSmrg{
58523367019cSmrg    const XPM_DATA *result = 0;
58533367019cSmrg    if (!IsEmpty(find)) {
58543367019cSmrg	Cardinal n;
58553367019cSmrg	for (n = 0; n < length; ++n) {
58563367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
58573367019cSmrg		result = table + n;
5858d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[n].name));
58593367019cSmrg		break;
58603367019cSmrg	    }
58613367019cSmrg	}
58623367019cSmrg
58633367019cSmrg	/*
58643367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
58653367019cSmrg	 * which are all _HHxWW format.
58663367019cSmrg	 */
58673367019cSmrg	if (result == 0) {
58683367019cSmrg	    const char *base = table[0].name;
58693367019cSmrg	    const char *last = strchr(base, '_');
58703367019cSmrg	    if (last != 0
58713367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
58723367019cSmrg		result = table + length - 1;
5873d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[0].name));
58743367019cSmrg	    }
58753367019cSmrg	}
58763367019cSmrg    }
58773367019cSmrg    return result;
58783367019cSmrg}
5879d4fba8b9Smrg#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
58803367019cSmrg#endif /* OPT_BUILTIN_XPMS */
58813367019cSmrg
58823367019cSmrgtypedef enum {
58833367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
58843367019cSmrg    ,eHintNone
58853367019cSmrg    ,eHintSearch
58863367019cSmrg} ICON_HINT;
58873367019cSmrg#endif /* HAVE_LIBXPM */
58883367019cSmrg
58893367019cSmrgint
58903367019cSmrggetVisualDepth(XtermWidget xw)
58913367019cSmrg{
58923367019cSmrg    int result = 0;
58933367019cSmrg
5894fa3f02f3Smrg    if (getVisualInfo(xw)) {
5895fa3f02f3Smrg	result = xw->visInfo->depth;
58963367019cSmrg    }
58973367019cSmrg    return result;
58983367019cSmrg}
58993367019cSmrg
59003367019cSmrg/*
59013367019cSmrg * WM_ICON_SIZE should be honored if possible.
59023367019cSmrg */
59033367019cSmrgvoid
5904d4fba8b9SmrgxtermLoadIcon(XtermWidget xw, const char *icon_hint)
59053367019cSmrg{
59063367019cSmrg#ifdef HAVE_LIBXPM
59073367019cSmrg    Display *dpy = XtDisplay(xw);
59083367019cSmrg    Pixmap myIcon = 0;
59093367019cSmrg    Pixmap myMask = 0;
59103367019cSmrg    char *workname = 0;
5911d4fba8b9Smrg    ICON_HINT hint = eHintDefault;
5912fa3f02f3Smrg#include <builtin_icons.h>
59133367019cSmrg
5914d4fba8b9Smrg    ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5915d4fba8b9Smrg    if (!IsEmpty(icon_hint)) {
5916d4fba8b9Smrg	if (!x_strcasecmp(icon_hint, "none")) {
5917d4fba8b9Smrg	    hint = eHintNone;
5918d4fba8b9Smrg	} else {
5919d4fba8b9Smrg	    hint = eHintSearch;
5920d4fba8b9Smrg	}
5921d4fba8b9Smrg    }
59223367019cSmrg
59233367019cSmrg    if (hint == eHintSearch) {
59243367019cSmrg	int state = 0;
5925d4fba8b9Smrg	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) {
59263367019cSmrg	    Pixmap resIcon = 0;
59273367019cSmrg	    Pixmap shapemask = 0;
59283367019cSmrg	    XpmAttributes attributes;
5929d4fba8b9Smrg	    struct stat sb;
59303367019cSmrg
59313367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
59323367019cSmrg	    attributes.valuemask = XpmDepth;
59333367019cSmrg
5934d4fba8b9Smrg	    if (IsEmpty(workname)
5935d4fba8b9Smrg		|| lstat(workname, &sb) != 0
5936d4fba8b9Smrg		|| !S_ISREG(sb.st_mode)) {
5937d4fba8b9Smrg		TRACE(("...failure (no such file)\n"));
5938d4fba8b9Smrg	    } else {
5939d4fba8b9Smrg		int rc = XpmReadFileToPixmap(dpy,
5940d4fba8b9Smrg					     DefaultRootWindow(dpy),
5941d4fba8b9Smrg					     workname,
5942d4fba8b9Smrg					     &resIcon,
5943d4fba8b9Smrg					     &shapemask,
5944d4fba8b9Smrg					     &attributes);
5945d4fba8b9Smrg		if (rc == XpmSuccess) {
5946d4fba8b9Smrg		    myIcon = resIcon;
5947d4fba8b9Smrg		    myMask = shapemask;
5948d4fba8b9Smrg		    TRACE(("...success\n"));
5949d4fba8b9Smrg		    ReportIcons(("found/loaded icon-file %s\n", workname));
5950d4fba8b9Smrg		    break;
5951d4fba8b9Smrg		} else {
5952d4fba8b9Smrg		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5953d4fba8b9Smrg		}
59543367019cSmrg	    }
59553367019cSmrg	}
59563367019cSmrg    }
59573367019cSmrg
59583367019cSmrg    /*
59593367019cSmrg     * If no external file was found, look for the name in the built-in table.
59603367019cSmrg     * If that fails, just use the biggest mini-icon.
59613367019cSmrg     */
59623367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
59633367019cSmrg	char **data;
59643367019cSmrg#if OPT_BUILTIN_XPMS
59653367019cSmrg	const XPM_DATA *myData = 0;
5966d4fba8b9Smrg	myData = BuiltInXPM(mini_xterm_xpms);
59673367019cSmrg	if (myData == 0)
5968d4fba8b9Smrg	    myData = BuiltInXPM(filled_xterm_xpms);
59693367019cSmrg	if (myData == 0)
5970d4fba8b9Smrg	    myData = BuiltInXPM(xterm_color_xpms);
59713367019cSmrg	if (myData == 0)
5972d4fba8b9Smrg	    myData = BuiltInXPM(xterm_xpms);
59733367019cSmrg	if (myData == 0)
59743367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
597594644356Smrg	data = (char **) myData->data;
59763367019cSmrg#else
59773367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
59783367019cSmrg#endif
59793367019cSmrg	if (XpmCreatePixmapFromData(dpy,
59803367019cSmrg				    DefaultRootWindow(dpy),
59813367019cSmrg				    data,
5982d4fba8b9Smrg				    &myIcon, &myMask, 0) == 0) {
5983d4fba8b9Smrg	    ReportIcons(("loaded built-in pixmap icon\n"));
5984d4fba8b9Smrg	} else {
59853367019cSmrg	    myIcon = 0;
59863367019cSmrg	    myMask = 0;
59873367019cSmrg	}
59883367019cSmrg    }
59893367019cSmrg
59903367019cSmrg    if (myIcon != 0) {
59913367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
59923367019cSmrg	if (!hints)
59933367019cSmrg	    hints = XAllocWMHints();
59943367019cSmrg
59953367019cSmrg	if (hints) {
59963367019cSmrg	    hints->flags |= IconPixmapHint;
59973367019cSmrg	    hints->icon_pixmap = myIcon;
59983367019cSmrg	    if (myMask) {
59993367019cSmrg		hints->flags |= IconMaskHint;
60003367019cSmrg		hints->icon_mask = myMask;
60013367019cSmrg	    }
60023367019cSmrg
60033367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
60043367019cSmrg	    XFree(hints);
6005d4fba8b9Smrg	    ReportIcons(("updated window-manager hints\n"));
60063367019cSmrg	}
60073367019cSmrg    }
60083367019cSmrg
6009d4fba8b9Smrg    free(workname);
60103367019cSmrg
60113367019cSmrg#else
60123367019cSmrg    (void) xw;
6013d4fba8b9Smrg    (void) icon_hint;
60143367019cSmrg#endif
60153367019cSmrg}
60163367019cSmrg
60173367019cSmrgvoid
6018cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
6019d522f475Smrg{
6020d522f475Smrg    Arg args[1];
6021cd3331d0Smrg    Boolean changed = True;
6022d522f475Smrg    Widget w = CURRENT_EMU();
6023d522f475Smrg    Widget top = SHELL_OF(w);
6024d522f475Smrg
6025d4fba8b9Smrg    char *my_attr = NULL;
6026d4fba8b9Smrg    char *old_value = value;
6027d4fba8b9Smrg#if OPT_WIDE_CHARS
6028d4fba8b9Smrg    Boolean titleIsUTF8;
6029d4fba8b9Smrg#endif
6030d522f475Smrg
6031b7c89284Ssnj    if (!AllowTitleOps(xw))
6032d522f475Smrg	return;
6033d522f475Smrg
6034d4fba8b9Smrg    /*
6035d4fba8b9Smrg     * Ignore empty or too-long requests.
6036d4fba8b9Smrg     */
6037d4fba8b9Smrg    if (value == 0 || strlen(value) > 1000)
6038d4fba8b9Smrg	return;
6039d4fba8b9Smrg
6040cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
6041cd3331d0Smrg	const char *temp;
6042cd3331d0Smrg	char *test;
6043cd3331d0Smrg
6044d4fba8b9Smrg	/* this allocates a new string, if no error is detected */
6045cd3331d0Smrg	value = x_decode_hex(value, &temp);
6046d4fba8b9Smrg	if (value == 0 || *temp != '\0') {
60473367019cSmrg	    free(value);
6048cd3331d0Smrg	    return;
60493367019cSmrg	}
6050cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
6051cd3331d0Smrg	    if (CharOf(*test) < 32) {
6052cd3331d0Smrg		*test = '\0';
6053cd3331d0Smrg		break;
6054cd3331d0Smrg	    }
6055cd3331d0Smrg	}
6056cd3331d0Smrg    }
6057d4fba8b9Smrg#if OPT_WIDE_CHARS
6058d522f475Smrg    /*
6059d4fba8b9Smrg     * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
6060d4fba8b9Smrg     * the WM_NAME property, rather than doing this directly.  That relies on
6061d4fba8b9Smrg     * the application to tell it if the format should be something other than
6062d4fba8b9Smrg     * STRING, i.e., by setting the XtNtitleEncoding resource.
6063d4fba8b9Smrg     *
6064d4fba8b9Smrg     * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
6065d4fba8b9Smrg     * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
6066d4fba8b9Smrg     * added UTF8_STRING (the documentation for that was discarded by an Xorg
6067d4fba8b9Smrg     * developer, although the source-code provides this feature).
6068d4fba8b9Smrg     *
6069d4fba8b9Smrg     * Since X11R5, if the X11 library fails to store a text property as
6070d4fba8b9Smrg     * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
6071d4fba8b9Smrg     * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
6072d4fba8b9Smrg     * case, limit the resulting characters to the printable ISO-8859-1 set.
6073d522f475Smrg     */
6074d4fba8b9Smrg    titleIsUTF8 = isValidUTF8((Char *) value);
6075d4fba8b9Smrg    if (IsSetUtf8Title(xw) && titleIsUTF8) {
6076d4fba8b9Smrg	char *testc = malloc(strlen(value) + 1);
6077d4fba8b9Smrg	Char *nextc = (Char *) value;
6078d4fba8b9Smrg	Boolean ok8bit = True;
6079d522f475Smrg
6080d4fba8b9Smrg	if (testc != NULL) {
6081d4fba8b9Smrg	    /*
6082d4fba8b9Smrg	     * Check if the data fits in STRING.  Along the way, replace
6083d4fba8b9Smrg	     * control characters.
6084d4fba8b9Smrg	     */
6085d4fba8b9Smrg	    Char *lastc = (Char *) testc;
6086d4fba8b9Smrg	    while (*nextc != '\0') {
6087d4fba8b9Smrg		unsigned ch;
6088d4fba8b9Smrg		nextc = convertFromUTF8(nextc, &ch);
6089d4fba8b9Smrg		if (ch > 255) {
6090d4fba8b9Smrg		    ok8bit = False;
6091d4fba8b9Smrg		} else if (!IsLatin1(ch)) {
6092d4fba8b9Smrg		    ch = OnlyLatin1(ch);
6093d4fba8b9Smrg		}
6094d4fba8b9Smrg		*lastc++ = (Char) ch;
6095d4fba8b9Smrg	    }
6096d4fba8b9Smrg	    *lastc = '\0';
6097d4fba8b9Smrg	    if (ok8bit) {
6098d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
6099d4fba8b9Smrg		if (value != old_value)
6100d4fba8b9Smrg		    free(value);
6101d4fba8b9Smrg		value = testc;
6102d4fba8b9Smrg		titleIsUTF8 = False;
6103d4fba8b9Smrg	    } else {
6104d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
6105d4fba8b9Smrg		       "\t%s\n", value));
6106d4fba8b9Smrg		free(testc);
6107d4fba8b9Smrg		nextc = (Char *) value;
6108d4fba8b9Smrg		while (*nextc != '\0') {
6109d4fba8b9Smrg		    unsigned ch;
6110d4fba8b9Smrg		    Char *skip = convertFromUTF8(nextc, &ch);
6111d4fba8b9Smrg		    if (iswcntrl((wint_t) ch)) {
6112d4fba8b9Smrg			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
6113d4fba8b9Smrg		    }
6114d4fba8b9Smrg		    nextc = skip;
6115d4fba8b9Smrg		}
6116cd3331d0Smrg	    }
6117d522f475Smrg	}
6118d4fba8b9Smrg    } else
6119d4fba8b9Smrg#endif
6120d4fba8b9Smrg    {
6121d4fba8b9Smrg	Char *c1 = (Char *) value;
6122d4fba8b9Smrg
6123d4fba8b9Smrg	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
6124d4fba8b9Smrg	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
6125d4fba8b9Smrg	    *c1 = (Char) OnlyLatin1(*c1);
6126d4fba8b9Smrg	}
6127d4fba8b9Smrg    }
6128d4fba8b9Smrg
6129d4fba8b9Smrg    my_attr = x_strdup(attribute);
6130d4fba8b9Smrg
6131d4fba8b9Smrg    ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
6132d522f475Smrg
6133d522f475Smrg#if OPT_WIDE_CHARS
6134d4fba8b9Smrg    /*
6135d4fba8b9Smrg     * If we're running in UTF-8 mode, and have not been told that the
6136d4fba8b9Smrg     * title string is in UTF-8, it is likely that non-ASCII text in the
6137d4fba8b9Smrg     * string will be rejected because it is not printable in the current
6138d4fba8b9Smrg     * locale.  So we convert it to UTF-8, allowing the X library to
6139d4fba8b9Smrg     * convert it back.
6140d4fba8b9Smrg     */
6141d4fba8b9Smrg    TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
6142d4fba8b9Smrg    if (xtermEnvUTF8() && !titleIsUTF8) {
6143d4fba8b9Smrg	size_t limit = strlen(value);
6144d4fba8b9Smrg	Char *c1 = (Char *) value;
6145d4fba8b9Smrg	int n;
6146cd3331d0Smrg
6147d4fba8b9Smrg	for (n = 0; c1[n] != '\0'; ++n) {
6148d4fba8b9Smrg	    if (c1[n] > 127) {
6149d4fba8b9Smrg		Char *converted;
6150d4fba8b9Smrg		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
6151d4fba8b9Smrg		    Char *temp = converted;
6152d4fba8b9Smrg		    while (*c1 != 0) {
6153d4fba8b9Smrg			temp = convertToUTF8(temp, *c1++);
6154d522f475Smrg		    }
6155d4fba8b9Smrg		    *temp = 0;
6156d4fba8b9Smrg		    if (value != old_value)
6157d4fba8b9Smrg			free(value);
6158d4fba8b9Smrg		    value = (char *) converted;
6159d4fba8b9Smrg		    ReportIcons(("...converted{%s}\n", value));
6160d522f475Smrg		}
6161d4fba8b9Smrg		break;
6162d522f475Smrg	    }
6163d522f475Smrg	}
6164d4fba8b9Smrg    }
6165d522f475Smrg#endif
6166d522f475Smrg
6167d522f475Smrg#if OPT_SAME_NAME
6168d4fba8b9Smrg    /* If the attribute isn't going to change, then don't bother... */
6169d4fba8b9Smrg    if (resource.sameName) {
6170d4fba8b9Smrg	char *buf = 0;
6171d4fba8b9Smrg	XtSetArg(args[0], my_attr, &buf);
6172d4fba8b9Smrg	XtGetValues(top, args, 1);
6173980988aeSmrg	TRACE(("...comparing resource{%s} to new value{%s}\n",
6174980988aeSmrg	       NonNull(buf),
6175980988aeSmrg	       NonNull(value)));
6176d4fba8b9Smrg	if (buf != 0 && strcmp(value, buf) == 0)
6177d4fba8b9Smrg	    changed = False;
6178d4fba8b9Smrg    }
6179d522f475Smrg#endif /* OPT_SAME_NAME */
6180d522f475Smrg
6181d4fba8b9Smrg    if (changed) {
6182d4fba8b9Smrg	ReportIcons(("...updating %s\n", my_attr));
6183d4fba8b9Smrg	ReportIcons(("...value is %s\n", value));
6184d4fba8b9Smrg	XtSetArg(args[0], my_attr, value);
6185d4fba8b9Smrg	XtSetValues(top, args, 1);
6186d4fba8b9Smrg    }
6187d522f475Smrg#if OPT_WIDE_CHARS
6188d4fba8b9Smrg    if (xtermEnvUTF8()) {
6189d4fba8b9Smrg	Display *dpy = XtDisplay(xw);
6190d4fba8b9Smrg	const char *propname = (!strcmp(my_attr, XtNtitle)
6191d4fba8b9Smrg				? "_NET_WM_NAME"
6192d4fba8b9Smrg				: "_NET_WM_ICON_NAME");
6193980988aeSmrg	Atom my_atom = CachedInternAtom(dpy, propname);
6194d4fba8b9Smrg
6195d4fba8b9Smrg	if (my_atom != None) {
6196d4fba8b9Smrg	    changed = True;
6197d4fba8b9Smrg
6198d4fba8b9Smrg	    if (IsSetUtf8Title(xw)) {
6199d4fba8b9Smrg#if OPT_SAME_NAME
6200d4fba8b9Smrg		if (resource.sameName) {
6201d4fba8b9Smrg		    Atom actual_type;
6202d4fba8b9Smrg		    Atom requested_type = XA_UTF8_STRING(dpy);
6203d4fba8b9Smrg		    int actual_format = 0;
6204d4fba8b9Smrg		    long long_length = 1024;
6205d4fba8b9Smrg		    unsigned long nitems = 0;
6206d4fba8b9Smrg		    unsigned long bytes_after = 0;
6207d4fba8b9Smrg		    unsigned char *prop = 0;
6208d4fba8b9Smrg
6209d4fba8b9Smrg		    if (xtermGetWinProp(dpy,
6210d4fba8b9Smrg					VShellWindow(xw),
6211d4fba8b9Smrg					my_atom,
6212d4fba8b9Smrg					0L,
6213d4fba8b9Smrg					long_length,
6214d4fba8b9Smrg					requested_type,
6215d4fba8b9Smrg					&actual_type,
6216d4fba8b9Smrg					&actual_format,
6217d4fba8b9Smrg					&nitems,
6218d4fba8b9Smrg					&bytes_after,
621950027b5bSmrg					&prop)) {
622050027b5bSmrg			if (actual_type == requested_type
622150027b5bSmrg			    && actual_format == 8
622250027b5bSmrg			    && prop != 0
622350027b5bSmrg			    && nitems == strlen(value)
622450027b5bSmrg			    && memcmp(value, prop, nitems) == 0) {
622550027b5bSmrg			    changed = False;
622650027b5bSmrg			}
622750027b5bSmrg			XFree(prop);
6228cd3331d0Smrg		    }
6229cd3331d0Smrg		}
6230d4fba8b9Smrg#endif /* OPT_SAME_NAME */
6231d4fba8b9Smrg		if (changed) {
6232d4fba8b9Smrg		    ReportIcons(("...updating %s\n", propname));
6233d4fba8b9Smrg		    ReportIcons(("...value is %s\n", value));
6234d4fba8b9Smrg		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
6235d4fba8b9Smrg				    XA_UTF8_STRING(dpy), 8,
6236d4fba8b9Smrg				    PropModeReplace,
6237d4fba8b9Smrg				    (Char *) value,
6238d4fba8b9Smrg				    (int) strlen(value));
6239d4fba8b9Smrg		}
6240d4fba8b9Smrg	    } else {
6241d4fba8b9Smrg		ReportIcons(("...deleting %s\n", propname));
6242d4fba8b9Smrg		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
6243d522f475Smrg	    }
6244d522f475Smrg	}
6245d522f475Smrg    }
6246d4fba8b9Smrg#endif
6247d4fba8b9Smrg    if (value != old_value) {
62483367019cSmrg	free(value);
62493367019cSmrg    }
62503367019cSmrg    free(my_attr);
62513367019cSmrg
6252cd3331d0Smrg    return;
6253d522f475Smrg}
6254d522f475Smrg
6255d522f475Smrgvoid
6256b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
6257d522f475Smrg{
62583367019cSmrg    if (!showZIconBeep(xw, name))
6259b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
6260d522f475Smrg}
6261d522f475Smrg
6262d522f475Smrgvoid
6263b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
6264d522f475Smrg{
6265b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
6266d522f475Smrg}
6267d522f475Smrg
6268712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
6269d522f475Smrg
6270d522f475Smrgvoid
6271d522f475SmrgChangeXprop(char *buf)
6272d522f475Smrg{
6273d522f475Smrg    Display *dpy = XtDisplay(toplevel);
6274d522f475Smrg    Window w = XtWindow(toplevel);
6275d522f475Smrg    XTextProperty text_prop;
6276d522f475Smrg    Atom aprop;
6277d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
6278d522f475Smrg
6279d522f475Smrg    if (pchEndPropName)
6280d522f475Smrg	*pchEndPropName = '\0';
6281980988aeSmrg    aprop = CachedInternAtom(dpy, buf);
6282d522f475Smrg    if (pchEndPropName == NULL) {
6283d522f475Smrg	/* no "=value" given, so delete the property */
6284d522f475Smrg	XDeleteProperty(dpy, w, aprop);
6285d522f475Smrg    } else {
6286d522f475Smrg	text_prop.value = pchEndPropName + 1;
6287d522f475Smrg	text_prop.encoding = XA_STRING;
6288d522f475Smrg	text_prop.format = 8;
6289d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
6290d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
6291d522f475Smrg    }
6292d522f475Smrg}
6293d522f475Smrg
6294d522f475Smrg/***====================================================================***/
6295d522f475Smrg
6296d522f475Smrg/*
6297d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
6298d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
6299d522f475Smrg */
6300d522f475Smrgvoid
63019a64e1c5SmrgReverseOldColors(XtermWidget xw)
6302d522f475Smrg{
63039a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
6304d522f475Smrg    Pixel tmpPix;
6305d522f475Smrg    char *tmpName;
6306d522f475Smrg
6307d522f475Smrg    if (pOld) {
6308d4fba8b9Smrg	/* change text cursor, if necessary */
6309d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
6310d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
6311d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
63129a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
6313d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
6314d522f475Smrg	    }
6315d522f475Smrg	    if (pOld->names[TEXT_BG]) {
6316d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
6317d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
6318d522f475Smrg		}
6319d522f475Smrg	    }
6320d522f475Smrg	}
6321d522f475Smrg
6322d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
6323d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
6324d522f475Smrg
6325d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
6326d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
6327d522f475Smrg
6328d522f475Smrg#if OPT_TEK4014
6329d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
6330d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
6331d522f475Smrg#endif
6332d4fba8b9Smrg	FreeMarkGCs(xw);
6333d522f475Smrg    }
6334d522f475Smrg    return;
6335d522f475Smrg}
6336d522f475Smrg
6337d522f475SmrgBool
6338d522f475SmrgAllocateTermColor(XtermWidget xw,
6339d522f475Smrg		  ScrnColors * pNew,
6340d522f475Smrg		  int ndx,
6341cd3331d0Smrg		  const char *name,
6342cd3331d0Smrg		  Bool always)
6343d522f475Smrg{
6344cd3331d0Smrg    Bool result = False;
6345d522f475Smrg
6346cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
6347cd3331d0Smrg	XColor def;
6348cd3331d0Smrg	char *newName;
6349cd3331d0Smrg
6350712a7ff4Smrg	result = True;
6351712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
6352712a7ff4Smrg	    def.pixel = xw->old_foreground;
6353712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
6354712a7ff4Smrg	    def.pixel = xw->old_background;
63553367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
6356712a7ff4Smrg	    result = False;
6357712a7ff4Smrg	}
6358712a7ff4Smrg
6359712a7ff4Smrg	if (result
6360cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
6361712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
6362cd3331d0Smrg		free(pNew->names[ndx]);
6363712a7ff4Smrg	    }
6364cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
6365cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
6366712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
6367712a7ff4Smrg		   ndx, newName, def.pixel));
6368cd3331d0Smrg	} else {
6369cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
6370712a7ff4Smrg	    result = False;
6371cd3331d0Smrg	}
6372cd3331d0Smrg    }
6373cd3331d0Smrg    return result;
6374d522f475Smrg}
6375d522f475Smrg/***====================================================================***/
6376d522f475Smrg
6377d522f475Smrg/* ARGSUSED */
6378d522f475Smrgvoid
6379cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
6380d522f475Smrg{
63813367019cSmrg    if_DEBUG({
63823367019cSmrg	xtermWarning(s, a);
63833367019cSmrg    });
6384d522f475Smrg}
6385d522f475Smrg
6386d522f475Smrgconst char *
6387d522f475SmrgSysErrorMsg(int code)
6388d522f475Smrg{
638994644356Smrg    static const char unknown[] = "unknown error";
6390d4fba8b9Smrg    const char *s = strerror(code);
6391d522f475Smrg    return s ? s : unknown;
6392d522f475Smrg}
6393d522f475Smrg
6394d522f475Smrgconst char *
6395d522f475SmrgSysReasonMsg(int code)
6396d522f475Smrg{
6397d522f475Smrg    /* *INDENT-OFF* */
6398d522f475Smrg    static const struct {
6399d522f475Smrg	int code;
6400d522f475Smrg	const char *name;
6401d522f475Smrg    } table[] = {
6402d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
6403d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
6404d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
6405d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
6406d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
6407d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
6408d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
6409d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
6410d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
6411d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
6412d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
6413d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
6414d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
6415d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
6416d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
6417d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
6418d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
6419d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
6420d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
6421d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
6422d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6423d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
6424d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
6425d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6426d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6427d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6428d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
6429d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6430d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6431d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
6432d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6433d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6434d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6435d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
6436d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6437d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6438d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6439d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6440d522f475Smrg    };
6441d522f475Smrg    /* *INDENT-ON* */
6442d522f475Smrg
6443d522f475Smrg    Cardinal n;
6444d522f475Smrg    const char *result = "?";
6445d522f475Smrg
6446d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
6447d522f475Smrg	if (code == table[n].code) {
6448d522f475Smrg	    result = table[n].name;
6449d522f475Smrg	    break;
6450d522f475Smrg	}
6451d522f475Smrg    }
6452d522f475Smrg    return result;
6453d522f475Smrg}
6454d522f475Smrg
6455d522f475Smrgvoid
6456d522f475SmrgSysError(int code)
6457d522f475Smrg{
6458d522f475Smrg    int oerrno = errno;
6459d522f475Smrg
6460c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6461d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6462d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6463d522f475Smrg
6464d522f475Smrg    Cleanup(code);
6465d522f475Smrg}
6466d522f475Smrg
6467d522f475Smrgvoid
64683367019cSmrgNormalExit(void)
6469d522f475Smrg{
6470d522f475Smrg    static Bool cleaning;
6471d522f475Smrg
6472d522f475Smrg    /*
6473d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
6474d522f475Smrg     */
64753367019cSmrg    if (cleaning) {
64763367019cSmrg	hold_screen = 0;
64773367019cSmrg	return;
64783367019cSmrg    }
6479d522f475Smrg
64803367019cSmrg    cleaning = True;
64813367019cSmrg    need_cleanup = False;
6482d522f475Smrg
64833367019cSmrg    if (hold_screen) {
64843367019cSmrg	hold_screen = 2;
64853367019cSmrg	while (hold_screen) {
6486d4fba8b9Smrg	    xtermFlushDbe(term);
6487d4fba8b9Smrg	    xevents(term);
6488d4fba8b9Smrg	    Sleep(EVENT_DELAY);
6489d522f475Smrg	}
64903367019cSmrg    }
6491d522f475Smrg#if OPT_SESSION_MGT
64923367019cSmrg    if (resource.sessionMgt) {
64933367019cSmrg	XtVaSetValues(toplevel,
64943367019cSmrg		      XtNjoinSession, False,
64953367019cSmrg		      (void *) 0);
6496d522f475Smrg    }
64973367019cSmrg#endif
64983367019cSmrg    Cleanup(0);
64993367019cSmrg}
65003367019cSmrg
6501d4fba8b9Smrg#if USE_DOUBLE_BUFFER
6502d4fba8b9Smrgvoid
6503d4fba8b9SmrgxtermFlushDbe(XtermWidget xw)
6504d4fba8b9Smrg{
6505d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
6506d4fba8b9Smrg    if (resource.buffered && screen->needSwap) {
6507d4fba8b9Smrg	XdbeSwapInfo swap;
6508d4fba8b9Smrg	swap.swap_window = VWindow(screen);
6509d4fba8b9Smrg	swap.swap_action = XdbeCopied;
6510d4fba8b9Smrg	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6511d4fba8b9Smrg	XFlush(XtDisplay(xw));
6512d4fba8b9Smrg	screen->needSwap = 0;
6513d4fba8b9Smrg	ScrollBarDrawThumb(xw, 2);
6514d4fba8b9Smrg	X_GETTIMEOFDAY(&screen->buffered_at);
6515d4fba8b9Smrg    }
6516d4fba8b9Smrg}
6517d4fba8b9Smrg
6518d4fba8b9Smrgvoid
6519d4fba8b9SmrgxtermTimedDbe(XtermWidget xw)
6520d4fba8b9Smrg{
6521d4fba8b9Smrg    if (resource.buffered) {
6522d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
6523d4fba8b9Smrg	struct timeval now;
6524d4fba8b9Smrg	long elapsed;
6525d4fba8b9Smrg	long limit = DbeMsecs(xw);
6526d4fba8b9Smrg
6527d4fba8b9Smrg	X_GETTIMEOFDAY(&now);
6528d4fba8b9Smrg	if (screen->buffered_at.tv_sec) {
6529d4fba8b9Smrg	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6530d4fba8b9Smrg		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6531d4fba8b9Smrg	} else {
6532d4fba8b9Smrg	    elapsed = limit;
6533d4fba8b9Smrg	}
6534d4fba8b9Smrg	if (elapsed >= limit) {
6535d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
6536d4fba8b9Smrg	    xtermFlushDbe(xw);
6537d4fba8b9Smrg	}
6538d4fba8b9Smrg    }
6539d4fba8b9Smrg}
6540d4fba8b9Smrg#endif
6541d4fba8b9Smrg
65423367019cSmrg/*
65433367019cSmrg * cleanup by sending SIGHUP to client processes
65443367019cSmrg */
65453367019cSmrgvoid
65463367019cSmrgCleanup(int code)
65473367019cSmrg{
65483367019cSmrg    TScreen *screen = TScreenOf(term);
65493367019cSmrg
65503367019cSmrg    TRACE(("Cleanup %d\n", code));
6551d522f475Smrg
6552d522f475Smrg    if (screen->pid > 1) {
6553d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
6554d522f475Smrg    }
6555d522f475Smrg    Exit(code);
6556d522f475Smrg}
6557d522f475Smrg
6558fa3f02f3Smrg#ifndef S_IXOTH
6559fa3f02f3Smrg#define S_IXOTH 1
6560fa3f02f3Smrg#endif
6561fa3f02f3Smrg
6562fa3f02f3SmrgBoolean
6563fa3f02f3SmrgvalidProgram(const char *pathname)
6564fa3f02f3Smrg{
6565fa3f02f3Smrg    Boolean result = False;
6566fa3f02f3Smrg    struct stat sb;
6567fa3f02f3Smrg
6568fa3f02f3Smrg    if (!IsEmpty(pathname)
6569fa3f02f3Smrg	&& *pathname == '/'
6570fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
6571fa3f02f3Smrg	&& stat(pathname, &sb) == 0
6572fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
6573fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
6574fa3f02f3Smrg	result = True;
6575fa3f02f3Smrg    }
6576fa3f02f3Smrg    return result;
6577fa3f02f3Smrg}
6578fa3f02f3Smrg
6579d522f475Smrg#ifndef VMS
65803367019cSmrg#ifndef PATH_MAX
65813367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
65823367019cSmrg#endif
6583d522f475Smrgchar *
6584d522f475SmrgxtermFindShell(char *leaf, Bool warning)
6585d522f475Smrg{
65863367019cSmrg    char *s0;
6587d522f475Smrg    char *s;
6588d522f475Smrg    char *d;
6589d522f475Smrg    char *tmp;
6590d522f475Smrg    char *result = leaf;
65913367019cSmrg    Bool allocated = False;
6592d522f475Smrg
6593d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
65943367019cSmrg
65953367019cSmrg    if (!strncmp("./", result, (size_t) 2)
65963367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
65973367019cSmrg	size_t need = PATH_MAX;
65983367019cSmrg	size_t used = strlen(result) + 2;
65993367019cSmrg	char *buffer = malloc(used + need);
66003367019cSmrg	if (buffer != 0) {
66013367019cSmrg	    if (getcwd(buffer, need) != 0) {
66023367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
66033367019cSmrg		result = buffer;
66043367019cSmrg		allocated = True;
66053367019cSmrg	    } else {
66063367019cSmrg		free(buffer);
66073367019cSmrg	    }
66083367019cSmrg	}
66093367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
6610d522f475Smrg	/* find it in $PATH */
66113367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
66120d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
6613d522f475Smrg		Bool found = False;
6614d522f475Smrg		while (*s != '\0') {
6615d522f475Smrg		    strcpy(tmp, s);
6616d522f475Smrg		    for (d = tmp;; ++d) {
6617d522f475Smrg			if (*d == ':' || *d == '\0') {
6618d522f475Smrg			    int skip = (*d != '\0');
6619d522f475Smrg			    *d = '/';
6620d522f475Smrg			    strcpy(d + 1, leaf);
6621d522f475Smrg			    if (skip)
6622d522f475Smrg				++d;
6623d522f475Smrg			    s += (d - tmp);
6624fa3f02f3Smrg			    if (validProgram(tmp)) {
6625d522f475Smrg				result = x_strdup(tmp);
6626d522f475Smrg				found = True;
66273367019cSmrg				allocated = True;
6628d522f475Smrg			    }
6629d522f475Smrg			    break;
6630d522f475Smrg			}
6631d522f475Smrg		    }
6632d522f475Smrg		    if (found)
6633d522f475Smrg			break;
6634d522f475Smrg		}
6635d522f475Smrg		free(tmp);
6636d522f475Smrg	    }
66373367019cSmrg	    free(s0);
6638d522f475Smrg	}
6639d522f475Smrg    }
6640d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
6641fa3f02f3Smrg    if (!validProgram(result)) {
6642d522f475Smrg	if (warning)
66433367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
66443367019cSmrg	if (allocated)
66453367019cSmrg	    free(result);
6646d522f475Smrg	result = 0;
6647d522f475Smrg    }
66483367019cSmrg    /* be consistent, so that caller can always free the result */
66493367019cSmrg    if (result != 0 && !allocated)
66503367019cSmrg	result = x_strdup(result);
6651d522f475Smrg    return result;
6652d522f475Smrg}
6653d522f475Smrg#endif /* VMS */
6654d522f475Smrg
66550d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6656d522f475Smrg
66573367019cSmrg/*
66583367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
66593367019cSmrg * This could happen on some older machines due to the uneven standardization
66603367019cSmrg * process for the two functions.
66613367019cSmrg *
66623367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
66633367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
66643367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
66653367019cSmrg * could copy environ.
66663367019cSmrg */
66673367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
66683367019cSmrg#undef HAVE_PUTENV
66693367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
66703367019cSmrg#undef HAVE_UNSETENV
66713367019cSmrg#endif
66723367019cSmrg
6673d522f475Smrg/*
6674d522f475Smrg * copy the environment before Setenv'ing.
6675d522f475Smrg */
6676d522f475Smrgvoid
6677d522f475SmrgxtermCopyEnv(char **oldenv)
6678d522f475Smrg{
66793367019cSmrg#ifdef HAVE_PUTENV
66803367019cSmrg    (void) oldenv;
66813367019cSmrg#else
6682d522f475Smrg    unsigned size;
6683d522f475Smrg    char **newenv;
6684d522f475Smrg
6685d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
6686d522f475Smrg	;
6687d522f475Smrg    }
6688d522f475Smrg
6689d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
6690d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
6691d522f475Smrg    environ = newenv;
66923367019cSmrg#endif
66933367019cSmrg}
66943367019cSmrg
66953367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
66963367019cSmrgstatic int
66973367019cSmrgfindEnv(const char *var, int *lengthp)
66983367019cSmrg{
66993367019cSmrg    char *test;
67003367019cSmrg    int envindex = 0;
67013367019cSmrg    size_t len = strlen(var);
67023367019cSmrg    int found = -1;
67033367019cSmrg
67043367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
67053367019cSmrg
67063367019cSmrg    while ((test = environ[envindex]) != NULL) {
67073367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
67083367019cSmrg	    found = envindex;
67093367019cSmrg	    break;
67103367019cSmrg	}
67113367019cSmrg	envindex++;
67123367019cSmrg    }
67133367019cSmrg    *lengthp = envindex;
67143367019cSmrg    return found;
6715d522f475Smrg}
67163367019cSmrg#endif
6717d522f475Smrg
6718d522f475Smrg/*
6719d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6720d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
6721d522f475Smrg * This procedure assumes the memory for the first level of environ
6722d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
6723d522f475Smrg * to have to do a realloc().
6724d522f475Smrg */
6725d522f475Smrgvoid
6726cd3331d0SmrgxtermSetenv(const char *var, const char *value)
6727d522f475Smrg{
6728d522f475Smrg    if (value != 0) {
67293367019cSmrg#ifdef HAVE_PUTENV
67303367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
67313367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
67323367019cSmrg	if (both) {
67333367019cSmrg	    sprintf(both, "%s=%s", var, value);
67343367019cSmrg	    putenv(both);
67353367019cSmrg	}
67363367019cSmrg#else
6737d522f475Smrg	size_t len = strlen(var);
67383367019cSmrg	int envindex;
67393367019cSmrg	int found = findEnv(var, &envindex);
6740d522f475Smrg
6741d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6742d522f475Smrg
6743d522f475Smrg	if (found < 0) {
6744d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
6745d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
6746d522f475Smrg
6747d522f475Smrg	    if (need > have) {
6748d522f475Smrg		char **newenv;
6749d522f475Smrg		newenv = TypeMallocN(char *, need);
6750d522f475Smrg		if (newenv == 0) {
67513367019cSmrg		    xtermWarning("Cannot increase environment\n");
6752d522f475Smrg		    return;
6753d522f475Smrg		}
6754d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
6755d522f475Smrg		free(environ);
6756d522f475Smrg		environ = newenv;
6757d522f475Smrg	    }
6758d522f475Smrg
6759d522f475Smrg	    found = envindex;
6760d522f475Smrg	    environ[found + 1] = NULL;
6761d522f475Smrg	}
6762d522f475Smrg
6763d4fba8b9Smrg	environ[found] = malloc(2 + len + strlen(value));
6764d522f475Smrg	if (environ[found] == 0) {
67653367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
6766d522f475Smrg	    return;
6767d522f475Smrg	}
6768d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
67693367019cSmrg#endif
6770d522f475Smrg    }
6771d522f475Smrg}
6772d522f475Smrg
67733367019cSmrgvoid
67743367019cSmrgxtermUnsetenv(const char *var)
67753367019cSmrg{
67763367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
67773367019cSmrg#ifdef HAVE_UNSETENV
67783367019cSmrg    unsetenv(var);
67793367019cSmrg#else
67803367019cSmrg    {
67813367019cSmrg	int ignore;
67823367019cSmrg	int item = findEnv(var, &ignore);
67833367019cSmrg	if (item >= 0) {
67843367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
67853367019cSmrg		++item;
67863367019cSmrg	    }
67873367019cSmrg	}
67883367019cSmrg    }
67893367019cSmrg#endif
67903367019cSmrg}
67913367019cSmrg
6792d522f475Smrg/*ARGSUSED*/
6793d522f475Smrgint
67949a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
6795d522f475Smrg{
67963367019cSmrg    xtermWarning("warning, error event received:\n");
6797d4fba8b9Smrg    TRACE_X_ERR(d, ev);
6798d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6799d522f475Smrg    Exit(ERROR_XERROR);
6800d522f475Smrg    return 0;			/* appease the compiler */
6801d522f475Smrg}
6802d522f475Smrg
6803712a7ff4Smrgvoid
6804712a7ff4Smrgice_error(IceConn iceConn)
6805712a7ff4Smrg{
6806712a7ff4Smrg    (void) iceConn;
6807712a7ff4Smrg
68083367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
68093367019cSmrg		 (long) getpid(), errno);
6810712a7ff4Smrg
6811712a7ff4Smrg    Exit(ERROR_ICEERROR);
6812712a7ff4Smrg}
6813712a7ff4Smrg
6814d522f475Smrg/*ARGSUSED*/
6815d522f475Smrgint
6816fa3f02f3Smrgxioerror(Display *dpy)
6817d522f475Smrg{
6818d522f475Smrg    int the_error = errno;
6819d522f475Smrg
68203367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
68213367019cSmrg		 the_error, SysErrorMsg(the_error),
68223367019cSmrg		 DisplayString(dpy));
6823d522f475Smrg
6824d522f475Smrg    Exit(ERROR_XIOERROR);
6825d522f475Smrg    return 0;			/* appease the compiler */
6826d522f475Smrg}
6827d522f475Smrg
6828728ff447Schristosvoid
6829d522f475Smrgxt_error(String message)
6830d522f475Smrg{
68313367019cSmrg    xtermWarning("Xt error: %s\n", message);
6832d522f475Smrg
6833d522f475Smrg    /*
6834d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
6835d522f475Smrg     */
6836d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
68373367019cSmrg	xtermWarning("DISPLAY is not set\n");
6838d522f475Smrg    }
6839980988aeSmrg    exit(ERROR_MISC);
6840d522f475Smrg}
6841d522f475Smrg
6842d522f475Smrgint
6843d522f475SmrgXStrCmp(char *s1, char *s2)
6844d522f475Smrg{
6845d522f475Smrg    if (s1 && s2)
6846d522f475Smrg	return (strcmp(s1, s2));
6847d522f475Smrg    if (s1 && *s1)
6848d522f475Smrg	return (1);
6849d522f475Smrg    if (s2 && *s2)
6850d522f475Smrg	return (-1);
6851d522f475Smrg    return (0);
6852d522f475Smrg}
6853d522f475Smrg
6854d522f475Smrg#if OPT_TEK4014
6855d522f475Smrgstatic void
6856fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
6857d522f475Smrg{
6858d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
6859d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
6860d522f475Smrg    XWithdrawWindow(dpy, w, scr);
6861d522f475Smrg    return;
6862d522f475Smrg}
6863d522f475Smrg#endif
6864d522f475Smrg
6865d522f475Smrgvoid
6866d522f475Smrgset_vt_visibility(Bool on)
6867d522f475Smrg{
6868c219fbebSmrg    XtermWidget xw = term;
6869c219fbebSmrg    TScreen *screen = TScreenOf(xw);
6870d522f475Smrg
6871d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
6872d522f475Smrg    if (on) {
6873c219fbebSmrg	if (!screen->Vshow && xw) {
6874c219fbebSmrg	    VTInit(xw);
6875c219fbebSmrg	    XtMapWidget(XtParent(xw));
6876d522f475Smrg#if OPT_TOOLBAR
6877d522f475Smrg	    /* we need both of these during initialization */
6878c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
6879d522f475Smrg	    ShowToolbar(resource.toolBar);
6880d522f475Smrg#endif
6881d522f475Smrg	    screen->Vshow = True;
6882d522f475Smrg	}
6883d522f475Smrg    }
6884d522f475Smrg#if OPT_TEK4014
6885d522f475Smrg    else {
6886c219fbebSmrg	if (screen->Vshow && xw) {
6887c219fbebSmrg	    withdraw_window(XtDisplay(xw),
6888c219fbebSmrg			    VShellWindow(xw),
6889c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
6890d522f475Smrg	    screen->Vshow = False;
6891d522f475Smrg	}
6892d522f475Smrg    }
6893d522f475Smrg    set_vthide_sensitivity();
6894d522f475Smrg    set_tekhide_sensitivity();
6895d522f475Smrg    update_vttekmode();
6896d522f475Smrg    update_tekshow();
6897d522f475Smrg    update_vtshow();
6898d522f475Smrg#endif
6899d522f475Smrg    return;
6900d522f475Smrg}
6901d522f475Smrg
6902d522f475Smrg#if OPT_TEK4014
6903d522f475Smrgvoid
6904d522f475Smrgset_tek_visibility(Bool on)
6905d522f475Smrg{
6906d4fba8b9Smrg    XtermWidget xw = term;
6907d4fba8b9Smrg
6908d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
6909d522f475Smrg
6910d522f475Smrg    if (on) {
6911d4fba8b9Smrg	if (!TEK4014_SHOWN(xw)) {
6912cd3331d0Smrg	    if (tekWidget == 0) {
6913cd3331d0Smrg		TekInit();	/* will exit on failure */
6914cd3331d0Smrg	    }
6915cd3331d0Smrg	    if (tekWidget != 0) {
6916cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
6917cd3331d0Smrg		XtRealizeWidget(tekParent);
6918cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
6919d522f475Smrg#if OPT_TOOLBAR
6920cd3331d0Smrg		/* we need both of these during initialization */
6921cd3331d0Smrg		XtMapWidget(tekParent);
6922cd3331d0Smrg		XtMapWidget(tekWidget);
6923d522f475Smrg#endif
6924cd3331d0Smrg		XtOverrideTranslations(tekParent,
6925cd3331d0Smrg				       XtParseTranslationTable
6926cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6927cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
6928cd3331d0Smrg				       XtWindow(tekParent),
6929cd3331d0Smrg				       &wm_delete_window, 1);
6930d4fba8b9Smrg		TEK4014_SHOWN(xw) = True;
6931cd3331d0Smrg	    }
6932d522f475Smrg	}
6933d522f475Smrg    } else {
6934d4fba8b9Smrg	if (TEK4014_SHOWN(xw) && tekWidget) {
6935d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
6936d522f475Smrg			    TShellWindow,
6937d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6938d4fba8b9Smrg	    TEK4014_SHOWN(xw) = False;
6939d522f475Smrg	}
6940d522f475Smrg    }
6941d522f475Smrg    set_tekhide_sensitivity();
6942d522f475Smrg    set_vthide_sensitivity();
6943d522f475Smrg    update_vtshow();
6944d522f475Smrg    update_tekshow();
6945d522f475Smrg    update_vttekmode();
6946d522f475Smrg    return;
6947d522f475Smrg}
6948d522f475Smrg
6949d522f475Smrgvoid
6950d522f475Smrgend_tek_mode(void)
6951d522f475Smrg{
6952cd3331d0Smrg    XtermWidget xw = term;
6953cd3331d0Smrg
6954cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
6955cd3331d0Smrg	FlushLog(xw);
6956dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
6957dfb07bc7Smrg	xtermSetWinSize(xw);
6958d522f475Smrg	longjmp(Tekend, 1);
6959d522f475Smrg    }
6960d522f475Smrg    return;
6961d522f475Smrg}
6962d522f475Smrg
6963d522f475Smrgvoid
6964d522f475Smrgend_vt_mode(void)
6965d522f475Smrg{
6966cd3331d0Smrg    XtermWidget xw = term;
6967cd3331d0Smrg
6968cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
6969cd3331d0Smrg	FlushLog(xw);
6970d4fba8b9Smrg	set_tek_visibility(True);
6971cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
6972dfb07bc7Smrg	TekSetWinSize(tekWidget);
6973d522f475Smrg	longjmp(VTend, 1);
6974d522f475Smrg    }
6975d522f475Smrg    return;
6976d522f475Smrg}
6977d522f475Smrg
6978d522f475Smrgvoid
6979d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
6980d522f475Smrg{
6981d522f475Smrg    if (tovt) {
6982d522f475Smrg	if (tekRefreshList)
6983d522f475Smrg	    TekRefresh(tekWidget);
6984d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
6985d522f475Smrg    } else {
6986d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
6987d522f475Smrg    }
6988d522f475Smrg}
6989d522f475Smrg
6990d522f475Smrgvoid
6991d522f475Smrghide_vt_window(void)
6992d522f475Smrg{
6993d522f475Smrg    set_vt_visibility(False);
6994d522f475Smrg    if (!TEK4014_ACTIVE(term))
6995d522f475Smrg	switch_modes(False);	/* switch to tek mode */
6996d522f475Smrg}
6997d522f475Smrg
6998d522f475Smrgvoid
6999d522f475Smrghide_tek_window(void)
7000d522f475Smrg{
7001d522f475Smrg    set_tek_visibility(False);
7002d522f475Smrg    tekRefreshList = (TekLink *) 0;
7003d522f475Smrg    if (TEK4014_ACTIVE(term))
7004d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
7005d522f475Smrg}
7006d522f475Smrg#endif /* OPT_TEK4014 */
7007d522f475Smrg
7008d522f475Smrgstatic const char *
7009d522f475Smrgskip_punct(const char *s)
7010d522f475Smrg{
7011d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
7012d522f475Smrg	++s;
7013d522f475Smrg    }
7014d522f475Smrg    return s;
7015d522f475Smrg}
7016d522f475Smrg
7017d522f475Smrgstatic int
7018d522f475Smrgcmp_options(const void *a, const void *b)
7019d522f475Smrg{
7020d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
7021d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
7022d522f475Smrg    return strcmp(s1, s2);
7023d522f475Smrg}
7024d522f475Smrg
7025d522f475Smrgstatic int
7026d522f475Smrgcmp_resources(const void *a, const void *b)
7027d522f475Smrg{
7028d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
7029d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
7030d522f475Smrg}
7031d522f475Smrg
7032d522f475SmrgXrmOptionDescRec *
7033d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
7034d522f475Smrg{
7035d522f475Smrg    static XrmOptionDescRec *res_array = 0;
7036d522f475Smrg
7037d522f475Smrg#ifdef NO_LEAKS
7038cd3331d0Smrg    if (descs == 0) {
7039d4fba8b9Smrg	FreeAndNull(res_array);
7040d522f475Smrg    } else
7041d522f475Smrg#endif
7042d522f475Smrg    if (res_array == 0) {
7043d522f475Smrg	Cardinal j;
7044d522f475Smrg
7045d522f475Smrg	/* make a sorted index to 'resources' */
7046d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
7047cd3331d0Smrg	if (res_array != 0) {
7048cd3331d0Smrg	    for (j = 0; j < res_count; j++)
7049cd3331d0Smrg		res_array[j] = descs[j];
7050cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
7051cd3331d0Smrg	}
7052d522f475Smrg    }
7053d522f475Smrg    return res_array;
7054d522f475Smrg}
7055d522f475Smrg
7056d522f475Smrg/*
7057d522f475Smrg * The first time this is called, construct sorted index to the main program's
7058d522f475Smrg * list of options, taking into account the on/off options which will be
7059d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
7060d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
7061d522f475Smrg */
7062d522f475SmrgOptionHelp *
7063d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
7064d522f475Smrg{
7065d522f475Smrg    static OptionHelp *opt_array = 0;
7066d522f475Smrg
7067d522f475Smrg#ifdef NO_LEAKS
7068d522f475Smrg    if (descs == 0 && opt_array != 0) {
7069d522f475Smrg	sortedOptDescs(descs, numDescs);
7070d4fba8b9Smrg	FreeAndNull(opt_array);
7071d522f475Smrg	return 0;
7072d522f475Smrg    } else if (options == 0 || descs == 0) {
7073d522f475Smrg	return 0;
7074d522f475Smrg    }
7075d522f475Smrg#endif
7076d522f475Smrg
7077d522f475Smrg    if (opt_array == 0) {
7078cd3331d0Smrg	size_t opt_count, j;
7079d522f475Smrg#if OPT_TRACE
7080d522f475Smrg	Cardinal k;
7081d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
7082d522f475Smrg	int code;
7083cd3331d0Smrg	const char *mesg;
7084d522f475Smrg#else
7085d522f475Smrg	(void) descs;
7086d522f475Smrg	(void) numDescs;
7087d522f475Smrg#endif
7088d522f475Smrg
7089d522f475Smrg	/* count 'options' and make a sorted index to it */
7090d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
7091d522f475Smrg	    ;
7092d522f475Smrg	}
7093d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
7094d522f475Smrg	for (j = 0; j < opt_count; j++)
7095d522f475Smrg	    opt_array[j] = options[j];
7096d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
7097d522f475Smrg
7098d522f475Smrg	/* supply the "turn on/off" strings if needed */
7099d522f475Smrg#if OPT_TRACE
7100d522f475Smrg	for (j = 0; j < opt_count; j++) {
7101712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
7102c219fbebSmrg		char temp[80];
7103cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
7104d522f475Smrg		for (k = 0; k < numDescs; ++k) {
7105cd3331d0Smrg		    const char *value = res_array[k].value;
7106d522f475Smrg		    if (res_array[k].option[0] == '-') {
7107d522f475Smrg			code = -1;
7108d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
7109d522f475Smrg			code = 1;
7110d522f475Smrg		    } else {
7111d522f475Smrg			code = 0;
7112d522f475Smrg		    }
71133367019cSmrg		    sprintf(temp, "%.*s",
71143367019cSmrg			    (int) sizeof(temp) - 2,
71153367019cSmrg			    opt_array[j].desc);
7116c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
7117d522f475Smrg			code = -code;
7118d522f475Smrg		    if (code != 0
7119d522f475Smrg			&& res_array[k].value != 0
7120d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
7121d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
7122d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
7123d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
7124d522f475Smrg			    mesg = "turn on/off";
7125d522f475Smrg			} else {
7126d522f475Smrg			    mesg = "turn off/on";
7127d522f475Smrg			}
7128d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
7129d522f475Smrg			       mesg,
7130d522f475Smrg			       res_array[k].option,
7131d522f475Smrg			       res_array[k].value,
7132d522f475Smrg			       opt_array[j].opt,
7133d522f475Smrg			       opt_array[j].desc));
7134d522f475Smrg			break;
7135d522f475Smrg		    }
7136d522f475Smrg		}
7137d522f475Smrg	    }
7138d522f475Smrg	}
7139d522f475Smrg#endif
7140d522f475Smrg    }
7141d522f475Smrg    return opt_array;
7142d522f475Smrg}
7143d522f475Smrg
7144d522f475Smrg/*
7145d522f475Smrg * Report the character-type locale that xterm was started in.
7146d522f475Smrg */
71473367019cSmrgString
7148d522f475SmrgxtermEnvLocale(void)
7149d522f475Smrg{
71503367019cSmrg    static String result;
7151d522f475Smrg
7152d522f475Smrg    if (result == 0) {
7153d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
7154cd3331d0Smrg	    result = x_strdup("C");
7155cd3331d0Smrg	} else {
7156cd3331d0Smrg	    result = x_strdup(result);
7157d522f475Smrg	}
7158d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
7159d522f475Smrg    }
7160d522f475Smrg    return result;
7161d522f475Smrg}
7162d522f475Smrg
7163d522f475Smrgchar *
7164d522f475SmrgxtermEnvEncoding(void)
7165d522f475Smrg{
7166d522f475Smrg    static char *result;
7167d522f475Smrg
7168d522f475Smrg    if (result == 0) {
7169d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
7170d522f475Smrg	result = nl_langinfo(CODESET);
7171d522f475Smrg#else
7172d4fba8b9Smrg	const char *locale = xtermEnvLocale();
7173d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
7174d4fba8b9Smrg	    result = x_strdup("ASCII");
7175d522f475Smrg	} else {
7176d4fba8b9Smrg	    result = x_strdup("ISO-8859-1");
7177d522f475Smrg	}
7178d522f475Smrg#endif
7179d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
7180d522f475Smrg    }
7181d522f475Smrg    return result;
7182d522f475Smrg}
7183d522f475Smrg
7184d522f475Smrg#if OPT_WIDE_CHARS
7185d522f475Smrg/*
7186d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
7187d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
7188d522f475Smrg * various library calls.
7189d522f475Smrg */
7190d522f475SmrgBool
7191d522f475SmrgxtermEnvUTF8(void)
7192d522f475Smrg{
7193d522f475Smrg    static Bool init = False;
7194d522f475Smrg    static Bool result = False;
7195d522f475Smrg
7196d522f475Smrg    if (!init) {
7197d522f475Smrg	init = True;
7198d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
7199d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
7200d522f475Smrg#else
7201fa3f02f3Smrg	{
7202fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
7203fa3f02f3Smrg	    int n;
7204fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
7205fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
7206fa3f02f3Smrg	    }
7207fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
7208fa3f02f3Smrg		result = True;
7209fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
7210fa3f02f3Smrg		result = True;
7211fa3f02f3Smrg	    free(locale);
7212fa3f02f3Smrg	}
7213d522f475Smrg#endif
7214d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
7215d522f475Smrg    }
7216d522f475Smrg    return result;
7217d522f475Smrg}
7218d522f475Smrg#endif /* OPT_WIDE_CHARS */
7219d522f475Smrg
7220b7c89284Ssnj/*
7221b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
7222b7c89284Ssnj */
7223b7c89284SsnjXtermWidget
7224b7c89284SsnjgetXtermWidget(Widget w)
7225b7c89284Ssnj{
7226b7c89284Ssnj    XtermWidget xw;
7227b7c89284Ssnj
7228b7c89284Ssnj    if (w == 0) {
7229b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
7230b7c89284Ssnj	if (!IsXtermWidget(xw)) {
7231b7c89284Ssnj	    xw = 0;
7232b7c89284Ssnj	}
7233b7c89284Ssnj    } else if (IsXtermWidget(w)) {
7234b7c89284Ssnj	xw = (XtermWidget) w;
7235b7c89284Ssnj    } else {
7236b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
7237b7c89284Ssnj    }
7238b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
7239b7c89284Ssnj    return xw;
7240b7c89284Ssnj}
72413367019cSmrg
72423367019cSmrg#if OPT_SESSION_MGT
7243636d5e9fSmrg
7244636d5e9fSmrg#if OPT_TRACE
7245636d5e9fSmrgstatic void
7246636d5e9fSmrgtrace_1_SM(const char *tag, String name)
7247636d5e9fSmrg{
7248636d5e9fSmrg    Arg args[1];
7249636d5e9fSmrg    char *buf = 0;
7250636d5e9fSmrg
7251636d5e9fSmrg    XtSetArg(args[0], name, &buf);
7252636d5e9fSmrg    XtGetValues(toplevel, args, 1);
7253636d5e9fSmrg
7254636d5e9fSmrg    if (strstr(name, "Path") || strstr(name, "Directory")) {
7255636d5e9fSmrg	TRACE(("%s %s: %s\n", tag, name, NonNull(buf)));
7256636d5e9fSmrg    } else if (strstr(name, "Command")) {
7257636d5e9fSmrg	if (buf != NULL) {
7258636d5e9fSmrg	    char **vec = (char **) (void *) buf;
7259636d5e9fSmrg	    int n;
7260636d5e9fSmrg	    TRACE(("%s %s:\n", tag, name));
7261636d5e9fSmrg	    for (n = 0; vec[n] != NULL; ++n) {
7262636d5e9fSmrg		TRACE((" arg[%d] = %s\n", n, vec[n]));
7263636d5e9fSmrg	    }
7264636d5e9fSmrg	} else {
7265636d5e9fSmrg	    TRACE(("%s %s: %p\n", tag, name, buf));
7266636d5e9fSmrg	}
7267636d5e9fSmrg    } else {
7268636d5e9fSmrg	TRACE(("%s %s: %p\n", tag, name, buf));
7269636d5e9fSmrg    }
7270636d5e9fSmrg}
7271636d5e9fSmrg
7272636d5e9fSmrgstatic void
7273636d5e9fSmrgtrace_SM_props(void)
7274636d5e9fSmrg{
7275636d5e9fSmrg    /* *INDENT-OFF* */
7276636d5e9fSmrg    static struct { String app, cls; } table[] = {
7277636d5e9fSmrg	{ XtNcurrentDirectory,	XtCCurrentDirectory },
7278636d5e9fSmrg	{ XtNdieCallback,	XtNdiscardCommand },
7279636d5e9fSmrg	{ XtCDiscardCommand,	XtNenvironment },
7280636d5e9fSmrg	{ XtCEnvironment,	XtNinteractCallback },
7281636d5e9fSmrg	{ XtNjoinSession,	XtCJoinSession },
7282636d5e9fSmrg	{ XtNprogramPath,	XtCProgramPath },
7283636d5e9fSmrg	{ XtNresignCommand,	XtCResignCommand },
7284636d5e9fSmrg	{ XtNrestartCommand,	XtCRestartCommand },
7285636d5e9fSmrg	{ XtNrestartStyle,	XtCRestartStyle },
7286636d5e9fSmrg	{ XtNsaveCallback,	XtNsaveCompleteCallback },
7287636d5e9fSmrg	{ XtNsessionID,		XtCSessionID },
7288636d5e9fSmrg	{ XtNshutdownCommand,	XtCShutdownCommand },
7289636d5e9fSmrg    };
7290636d5e9fSmrg    /* *INDENT-ON* */
7291636d5e9fSmrg    Cardinal n;
7292636d5e9fSmrg    TRACE(("Session properties:\n"));
7293636d5e9fSmrg    for (n = 0; n < XtNumber(table); ++n) {
7294636d5e9fSmrg	trace_1_SM("app", table[n].app);
7295636d5e9fSmrg	trace_1_SM("cls", table[n].cls);
7296636d5e9fSmrg    }
7297636d5e9fSmrg}
7298636d5e9fSmrg#define TRACE_SM_PROPS()	trace_SM_props()
7299636d5e9fSmrg#else
7300636d5e9fSmrg#define TRACE_SM_PROPS()	/* nothing */
7301636d5e9fSmrg#endif
7302636d5e9fSmrg
73033367019cSmrgstatic void
73043367019cSmrgdie_callback(Widget w GCC_UNUSED,
73053367019cSmrg	     XtPointer client_data GCC_UNUSED,
73063367019cSmrg	     XtPointer call_data GCC_UNUSED)
73073367019cSmrg{
7308c48a5815Smrg    TRACE(("die_callback client=%p, call=%p\n",
7309c48a5815Smrg	   (void *) client_data,
7310c48a5815Smrg	   (void *) call_data));
7311636d5e9fSmrg    TRACE_SM_PROPS();
73123367019cSmrg    NormalExit();
73133367019cSmrg}
73143367019cSmrg
73153367019cSmrgstatic void
73163367019cSmrgsave_callback(Widget w GCC_UNUSED,
73173367019cSmrg	      XtPointer client_data GCC_UNUSED,
73183367019cSmrg	      XtPointer call_data)
73193367019cSmrg{
73203367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
7321636d5e9fSmrg    TRACE(("save_callback:\n"));
7322636d5e9fSmrg    TRACE(("... save_type            <-%d\n", token->save_type));
7323636d5e9fSmrg    TRACE(("... interact_style       <-%d\n", token->interact_style));
7324636d5e9fSmrg    TRACE(("... shutdown             <-%s\n", BtoS(token->shutdown)));
7325636d5e9fSmrg    TRACE(("... fast                 <-%s\n", BtoS(token->fast)));
7326636d5e9fSmrg    TRACE(("... cancel_shutdown      <-%s\n", BtoS(token->cancel_shutdown)));
7327636d5e9fSmrg    TRACE(("... phase                <-%d\n", token->phase));
7328636d5e9fSmrg    TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type));
7329636d5e9fSmrg    TRACE(("... request_cancel       ->%s\n", BtoS(token->request_cancel)));
7330636d5e9fSmrg    TRACE(("... request_next_phase   ->%s\n", BtoS(token->request_next_phase)));
7331636d5e9fSmrg    TRACE(("... save_success         ->%s\n", BtoS(token->save_success)));
7332636d5e9fSmrg    xtermUpdateRestartCommand(term);
7333636d5e9fSmrg    /* we have nothing more to save */
73343367019cSmrg    token->save_success = True;
73353367019cSmrg}
73363367019cSmrg
73373367019cSmrgstatic void
73383367019cSmrgicewatch(IceConn iceConn,
73393367019cSmrg	 IcePointer clientData GCC_UNUSED,
73403367019cSmrg	 Bool opening,
73413367019cSmrg	 IcePointer * watchData GCC_UNUSED)
73423367019cSmrg{
73433367019cSmrg    if (opening) {
73443367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
73453367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
73463367019cSmrg    } else {
73473367019cSmrg	ice_fd = -1;
73483367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
73493367019cSmrg    }
73503367019cSmrg}
73513367019cSmrg
73523367019cSmrgvoid
73533367019cSmrgxtermOpenSession(void)
73543367019cSmrg{
73553367019cSmrg    if (resource.sessionMgt) {
73563367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
73573367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
73583367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
7359636d5e9fSmrg
7360636d5e9fSmrg	TRACE_SM_PROPS();
73613367019cSmrg    }
73623367019cSmrg}
73633367019cSmrg
73643367019cSmrgvoid
73653367019cSmrgxtermCloseSession(void)
73663367019cSmrg{
73673367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
73683367019cSmrg}
7369636d5e9fSmrg
7370636d5e9fSmrgtypedef enum {
7371636d5e9fSmrg    B_ARG = 0,
7372636d5e9fSmrg    I_ARG,
7373636d5e9fSmrg    D_ARG,
7374636d5e9fSmrg    S_ARG
7375636d5e9fSmrg} ParamType;
7376636d5e9fSmrg
7377636d5e9fSmrg#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) }
7378636d5e9fSmrg#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) }
7379636d5e9fSmrg#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) }
7380636d5e9fSmrg#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) }
7381636d5e9fSmrg
7382636d5e9fSmrgtypedef struct {
7383636d5e9fSmrg    const char name[30];
7384636d5e9fSmrg    ParamType type;
7385636d5e9fSmrg    Cardinal offset;
7386636d5e9fSmrg} FontParams;
7387636d5e9fSmrg
7388636d5e9fSmrg/* *INDENT-OFF* */
7389636d5e9fSmrgstatic const FontParams fontParams[] = {
7390636d5e9fSmrg    Iarg(XtNinitialFont,     screen.menu_font_number),	/* "-fc" */
7391636d5e9fSmrg    Barg(XtNallowBoldFonts,  screen.allowBoldFonts),	/* menu */
7392636d5e9fSmrg#if OPT_BOX_CHARS
7393636d5e9fSmrg    Barg(XtNforceBoxChars,   screen.force_box_chars),	/* "-fbx" */
7394636d5e9fSmrg    Barg(XtNforcePackedFont, screen.force_packed),	/* menu */
7395636d5e9fSmrg#endif
7396636d5e9fSmrg#if OPT_DEC_CHRSET
7397636d5e9fSmrg    Barg(XtNfontDoublesize,  screen.font_doublesize),	/* menu */
7398636d5e9fSmrg#endif
7399636d5e9fSmrg#if OPT_WIDE_CHARS
7400636d5e9fSmrg    Barg(XtNutf8Fonts,       screen.utf8_fonts),	/* menu */
7401636d5e9fSmrg#endif
7402636d5e9fSmrg#if OPT_RENDERFONT
7403636d5e9fSmrg    Darg(XtNfaceSize,        misc.face_size[0]),	/* "-fs" */
7404636d5e9fSmrg    Sarg(XtNfaceName,        misc.default_xft.f_n),	/* "-fa" */
7405636d5e9fSmrg    Sarg(XtNrenderFont,      misc.render_font_s),	/* (resource) */
7406636d5e9fSmrg#endif
7407636d5e9fSmrg};
7408636d5e9fSmrg/* *INDENT-ON* */
7409636d5e9fSmrg
7410636d5e9fSmrg#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2)
7411636d5e9fSmrg#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset)
7412636d5e9fSmrg
7413636d5e9fSmrg/*
7414636d5e9fSmrg * If no widget is given, no value is used.
7415636d5e9fSmrg */
7416636d5e9fSmrgstatic char *
7417636d5e9fSmrgformatFontParam(char *result, XtermWidget xw, const FontParams * parameter)
7418636d5e9fSmrg{
7419636d5e9fSmrg    sprintf(result, "%s*%s:", ProgramName, parameter->name);
7420636d5e9fSmrg    if (xw != None) {
7421636d5e9fSmrg	char *next = result + strlen(result);
7422636d5e9fSmrg	switch (parameter->type) {
7423636d5e9fSmrg	case B_ARG:
7424636d5e9fSmrg	    sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset)
7425636d5e9fSmrg		    ? "true"
7426636d5e9fSmrg		    : "false");
7427636d5e9fSmrg	    break;
7428636d5e9fSmrg	case I_ARG:
7429636d5e9fSmrg	    sprintf(next, "%d", TypedPtr(int));
7430636d5e9fSmrg	    break;
7431636d5e9fSmrg	case D_ARG:
7432636d5e9fSmrg	    sprintf(next, "%.1f", TypedPtr(float));
7433636d5e9fSmrg	    break;
7434636d5e9fSmrg	case S_ARG:
7435636d5e9fSmrg	    strcpy(next, TypedPtr(char *));
7436636d5e9fSmrg#if OPT_RENDERFONT
7437636d5e9fSmrg	    if (!strcmp(parameter->name, XtNfaceName)) {
7438636d5e9fSmrg		if (IsEmpty(next)
7439636d5e9fSmrg		    && xw->work.render_font) {
7440636d5e9fSmrg		    strcpy(next, DEFFACENAME_AUTO);
7441636d5e9fSmrg		}
7442636d5e9fSmrg	    } else if (!strcmp(parameter->name, XtNrenderFont)) {
7443636d5e9fSmrg		if (xw->work.render_font == erDefault
7444636d5e9fSmrg		    && IsEmpty(xw->misc.default_xft.f_n)) {
7445636d5e9fSmrg		    strcpy(next, "DefaultOff");
7446636d5e9fSmrg		}
7447636d5e9fSmrg	    }
7448636d5e9fSmrg#endif
7449636d5e9fSmrg	    break;
7450636d5e9fSmrg	}
7451636d5e9fSmrg    }
7452636d5e9fSmrg    return result;
7453636d5e9fSmrg}
7454636d5e9fSmrg
7455636d5e9fSmrg#if OPT_TRACE
7456636d5e9fSmrgstatic void
7457636d5e9fSmrgdumpFontParams(XtermWidget xw)
7458636d5e9fSmrg{
7459636d5e9fSmrg    char buffer[1024];
7460636d5e9fSmrg    Cardinal n;
7461636d5e9fSmrg
7462636d5e9fSmrg    TRACE(("FontParams:\n"));
7463636d5e9fSmrg    for (n = 0; n < XtNumber(fontParams); ++n) {
7464636d5e9fSmrg	TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n)));
7465636d5e9fSmrg    }
7466636d5e9fSmrg}
7467636d5e9fSmrg#else
7468636d5e9fSmrg#define dumpFontParams(xw)	/* nothing */
7469636d5e9fSmrg#endif
7470636d5e9fSmrg
7471636d5e9fSmrgstatic Boolean
7472636d5e9fSmrgfindFontParams(int argc, char **argv)
7473636d5e9fSmrg{
7474636d5e9fSmrg    Boolean result = False;
7475636d5e9fSmrg
7476636d5e9fSmrg    if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) {
7477636d5e9fSmrg	int n;
7478636d5e9fSmrg
7479636d5e9fSmrg	for (n = 0; n < RESTART_PARAMS; ++n) {
7480636d5e9fSmrg	    int my_index = argc - restart_params - n - 1;
7481636d5e9fSmrg	    int my_param = (RESTART_PARAMS - n - 1) / 2;
7482636d5e9fSmrg	    char *actual = argv[my_index];
7483636d5e9fSmrg	    char expect[1024];
7484636d5e9fSmrg	    Boolean value = (Boolean) ((n % 2) == 0);
7485636d5e9fSmrg
7486636d5e9fSmrg	    result = False;
7487636d5e9fSmrg	    TRACE(("...index: %d\n", my_index));
7488636d5e9fSmrg	    TRACE(("...param: %d\n", my_param));
7489636d5e9fSmrg	    TRACE(("...actual %s\n", actual));
7490636d5e9fSmrg	    if (IsEmpty(actual))
7491636d5e9fSmrg		break;
7492636d5e9fSmrg
7493636d5e9fSmrg	    if (value) {
7494636d5e9fSmrg		formatFontParam(expect, None, fontParams + my_param);
7495636d5e9fSmrg	    } else {
7496636d5e9fSmrg		strcpy(expect, "-xrm");
7497636d5e9fSmrg	    }
7498636d5e9fSmrg
7499636d5e9fSmrg	    TRACE(("...expect %s\n", expect));
7500636d5e9fSmrg
7501636d5e9fSmrg	    if (value) {
7502636d5e9fSmrg		if (strlen(expect) >= strlen(actual))
7503636d5e9fSmrg		    break;
7504636d5e9fSmrg		if (strncmp(expect, actual, strlen(expect)))
7505636d5e9fSmrg		    break;
7506636d5e9fSmrg	    } else {
7507636d5e9fSmrg		if (strcmp(actual, expect))
7508636d5e9fSmrg		    break;
7509636d5e9fSmrg	    }
7510636d5e9fSmrg	    TRACE(("fixme/ok:%d\n", n));
7511636d5e9fSmrg	    result = True;
7512636d5e9fSmrg	}
7513636d5e9fSmrg	TRACE(("findFontParams: %s (tested %d of %d parameters)\n",
7514636d5e9fSmrg	       BtoS(result), n + 1, RESTART_PARAMS));
7515636d5e9fSmrg    }
7516636d5e9fSmrg    return result;
7517636d5e9fSmrg}
7518636d5e9fSmrg
7519636d5e9fSmrgstatic int
7520c48a5815SmrginsertFontParams(XtermWidget xw, int *targetp, Bool first)
7521636d5e9fSmrg{
7522636d5e9fSmrg    int changed = 0;
7523636d5e9fSmrg    int n;
7524636d5e9fSmrg    int target = *targetp;
7525636d5e9fSmrg    char buffer[1024];
7526636d5e9fSmrg    const char *option = "-xrm";
7527636d5e9fSmrg
7528636d5e9fSmrg    for (n = 0; n < (int) XtNumber(fontParams); ++n) {
7529636d5e9fSmrg	formatFontParam(buffer, xw, fontParams + n);
7530636d5e9fSmrg	TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer));
7531636d5e9fSmrg	if (restart_command[target] == NULL)
7532636d5e9fSmrg	    restart_command[target] = x_strdup(option);
7533636d5e9fSmrg	++target;
7534636d5e9fSmrg	if (first) {
7535636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7536636d5e9fSmrg	    ++changed;
7537636d5e9fSmrg	} else if (restart_command[target] == NULL
7538636d5e9fSmrg		   || strcmp(restart_command[target], buffer)) {
7539636d5e9fSmrg	    free(restart_command[target]);
7540636d5e9fSmrg	    restart_command[target] = x_strdup(buffer);
7541636d5e9fSmrg	    ++changed;
7542636d5e9fSmrg	}
7543636d5e9fSmrg	++target;
7544636d5e9fSmrg    }
7545636d5e9fSmrg    *targetp = target;
7546636d5e9fSmrg    return changed;
7547636d5e9fSmrg}
7548636d5e9fSmrg
7549636d5e9fSmrgvoid
7550636d5e9fSmrgxtermUpdateRestartCommand(XtermWidget xw)
7551636d5e9fSmrg{
7552636d5e9fSmrg    if (resource.sessionMgt) {
7553636d5e9fSmrg	Arg args[1];
7554636d5e9fSmrg	char **argv = 0;
7555636d5e9fSmrg
7556636d5e9fSmrg	XtSetArg(args[0], XtNrestartCommand, &argv);
7557636d5e9fSmrg	XtGetValues(toplevel, args, 1);
7558636d5e9fSmrg	if (argv != NULL) {
7559636d5e9fSmrg	    static int my_params = 0;
7560636d5e9fSmrg
7561636d5e9fSmrg	    int changes = 0;
7562636d5e9fSmrg	    Boolean first = False;
7563636d5e9fSmrg	    int argc;
7564636d5e9fSmrg	    int want;
7565636d5e9fSmrg	    int source, target;
7566636d5e9fSmrg
7567636d5e9fSmrg	    TRACE(("xtermUpdateRestartCommand\n"));
7568636d5e9fSmrg	    dumpFontParams(xw);
7569636d5e9fSmrg	    for (argc = 0; argv[argc] != NULL; ++argc) {
7570636d5e9fSmrg		TRACE((" arg[%d] = %s\n", argc, argv[argc]));
7571636d5e9fSmrg		;
7572636d5e9fSmrg	    }
7573636d5e9fSmrg	    want = argc - (restart_params + RESTART_PARAMS);
7574636d5e9fSmrg
7575636d5e9fSmrg	    TRACE((" argc:           %d\n", argc));
7576636d5e9fSmrg	    TRACE((" restart_params: %d\n", restart_params));
7577636d5e9fSmrg	    TRACE((" want to insert: %d\n", want));
7578636d5e9fSmrg
7579636d5e9fSmrg	    /*
7580636d5e9fSmrg	     * If we already have the font-choice option, do not add it again.
7581636d5e9fSmrg	     */
7582636d5e9fSmrg	    if (findFontParams(argc, argv)) {
7583636d5e9fSmrg		my_params = (want);
7584636d5e9fSmrg	    } else {
7585636d5e9fSmrg		first = True;
7586636d5e9fSmrg		my_params = (argc - restart_params);
7587636d5e9fSmrg	    }
7588636d5e9fSmrg	    TRACE((" my_params:      %d\n", my_params));
7589636d5e9fSmrg
7590636d5e9fSmrg	    if (my_params > argc) {
7591636d5e9fSmrg		TRACE((" re-allocate restartCommand\n"));
7592636d5e9fSmrg		FreeAndNull(restart_command);
7593636d5e9fSmrg	    }
7594636d5e9fSmrg
7595636d5e9fSmrg	    if (restart_command == NULL) {
7596636d5e9fSmrg		int need = argc + RESTART_PARAMS + 1;
7597636d5e9fSmrg
7598636d5e9fSmrg		restart_command = TypeCallocN(char *, need);
7599636d5e9fSmrg
7600636d5e9fSmrg		TRACE(("..inserting font-parameters\n"));
7601636d5e9fSmrg		for (source = target = 0; source < argc; ++source) {
7602636d5e9fSmrg		    if (source == my_params) {
7603636d5e9fSmrg			changes += insertFontParams(xw, &target, first);
7604636d5e9fSmrg			if (!first) {
7605636d5e9fSmrg			    source += (RESTART_PARAMS - 1);
7606636d5e9fSmrg			    continue;
7607636d5e9fSmrg			}
7608636d5e9fSmrg		    }
7609636d5e9fSmrg		    if (argv[source] == NULL)
7610636d5e9fSmrg			break;
7611636d5e9fSmrg		    restart_command[target++] = x_strdup(argv[source]);
7612636d5e9fSmrg		}
7613636d5e9fSmrg		restart_command[target] = NULL;
7614636d5e9fSmrg	    } else {
7615636d5e9fSmrg		TRACE(("..replacing font-parameters\n"));
7616636d5e9fSmrg		target = my_params;
7617636d5e9fSmrg		changes += insertFontParams(xw, &target, first);
7618636d5e9fSmrg	    }
7619636d5e9fSmrg	    if (changes) {
7620636d5e9fSmrg		TRACE(("..%d parameters changed\n", changes));
7621636d5e9fSmrg		XtSetArg(args[0], XtNrestartCommand, restart_command);
7622636d5e9fSmrg		XtSetValues(toplevel, args, 1);
7623636d5e9fSmrg	    } else {
7624636d5e9fSmrg		TRACE(("..NO parameters changed\n"));
7625636d5e9fSmrg	    }
7626636d5e9fSmrg	}
7627636d5e9fSmrg	TRACE_SM_PROPS();
7628636d5e9fSmrg    }
7629636d5e9fSmrg}
76303367019cSmrg#endif /* OPT_SESSION_MGT */
76313367019cSmrg
76323367019cSmrgWidget
76333367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
76343367019cSmrg		     String my_class,
76353367019cSmrg		     XrmOptionDescRec * options,
76363367019cSmrg		     Cardinal num_options,
76373367019cSmrg		     int *argc_in_out,
7638d4fba8b9Smrg		     char **argv_in_out,
7639fa3f02f3Smrg		     String *fallback_resources,
76403367019cSmrg		     WidgetClass widget_class,
76413367019cSmrg		     ArgList args,
76423367019cSmrg		     Cardinal num_args)
76433367019cSmrg{
76443367019cSmrg    Widget result;
76453367019cSmrg
76463367019cSmrg    XtSetErrorHandler(xt_error);
76473367019cSmrg#if OPT_SESSION_MGT
76483367019cSmrg    result = XtOpenApplication(app_context_return,
76493367019cSmrg			       my_class,
76503367019cSmrg			       options,
76513367019cSmrg			       num_options,
76523367019cSmrg			       argc_in_out,
76533367019cSmrg			       argv_in_out,
76543367019cSmrg			       fallback_resources,
76553367019cSmrg			       widget_class,
76563367019cSmrg			       args,
76573367019cSmrg			       num_args);
76583367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
76593367019cSmrg#else
76609a64e1c5Smrg    (void) widget_class;
76619a64e1c5Smrg    (void) args;
76629a64e1c5Smrg    (void) num_args;
76633367019cSmrg    result = XtAppInitialize(app_context_return,
76643367019cSmrg			     my_class,
76653367019cSmrg			     options,
76663367019cSmrg			     num_options,
76673367019cSmrg			     argc_in_out,
76683367019cSmrg			     argv_in_out,
76693367019cSmrg			     fallback_resources,
76703367019cSmrg			     NULL, 0);
76713367019cSmrg#endif /* OPT_SESSION_MGT */
7672e8264990Smrg    XtSetErrorHandler(NULL);
76733367019cSmrg
76743367019cSmrg    return result;
76753367019cSmrg}
76763367019cSmrg
7677d4fba8b9Smrg/*
7678d4fba8b9Smrg * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
7679d4fba8b9Smrg * our own error-handler.
7680d4fba8b9Smrg */
7681d4fba8b9Smrg/* ARGSUSED */
7682d4fba8b9Smrgint
7683d4fba8b9Smrgignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
7684d4fba8b9Smrg{
7685d4fba8b9Smrg    return 1;
7686d4fba8b9Smrg}
7687d4fba8b9Smrg
76883367019cSmrgstatic int x11_errors;
76893367019cSmrg
76903367019cSmrgstatic int
76919a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
76923367019cSmrg{
76933367019cSmrg    (void) display;
76943367019cSmrg    (void) error_event;
76953367019cSmrg    ++x11_errors;
76963367019cSmrg    return 0;
76973367019cSmrg}
76983367019cSmrg
76993367019cSmrgBoolean
7700fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
77013367019cSmrg{
77023367019cSmrg    Boolean result = False;
77033367019cSmrg    Status code;
77043367019cSmrg
77053367019cSmrg    memset(attrs, 0, sizeof(*attrs));
77063367019cSmrg    if (win != None) {
77073367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
77083367019cSmrg	x11_errors = 0;
77093367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
77103367019cSmrg	XSetErrorHandler(save);
77113367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
77123367019cSmrg	if (result) {
77133367019cSmrg	    TRACE_WIN_ATTRS(attrs);
77143367019cSmrg	} else {
77153367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
77163367019cSmrg	}
77173367019cSmrg    }
77183367019cSmrg    return result;
77193367019cSmrg}
77203367019cSmrg
77213367019cSmrgBoolean
7722fa3f02f3SmrgxtermGetWinProp(Display *display,
77233367019cSmrg		Window win,
77243367019cSmrg		Atom property,
77253367019cSmrg		long long_offset,
77263367019cSmrg		long long_length,
77273367019cSmrg		Atom req_type,
77289a64e1c5Smrg		Atom *actual_type_return,
77293367019cSmrg		int *actual_format_return,
77303367019cSmrg		unsigned long *nitems_return,
77313367019cSmrg		unsigned long *bytes_after_return,
77323367019cSmrg		unsigned char **prop_return)
77333367019cSmrg{
7734d4fba8b9Smrg    Boolean result = False;
77353367019cSmrg
77363367019cSmrg    if (win != None) {
77373367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
77383367019cSmrg	x11_errors = 0;
77393367019cSmrg	if (XGetWindowProperty(display,
77403367019cSmrg			       win,
77413367019cSmrg			       property,
77423367019cSmrg			       long_offset,
77433367019cSmrg			       long_length,
77443367019cSmrg			       False,
77453367019cSmrg			       req_type,
77463367019cSmrg			       actual_type_return,
77473367019cSmrg			       actual_format_return,
77483367019cSmrg			       nitems_return,
77493367019cSmrg			       bytes_after_return,
77503367019cSmrg			       prop_return) == Success
77513367019cSmrg	    && x11_errors == 0) {
77523367019cSmrg	    result = True;
77533367019cSmrg	}
77543367019cSmrg	XSetErrorHandler(save);
77553367019cSmrg    }
77563367019cSmrg    return result;
77573367019cSmrg}
77583367019cSmrg
77593367019cSmrgvoid
77603367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
77613367019cSmrg{
77623367019cSmrg    Display *dpy = XtDisplay(toplevel);
77633367019cSmrg    XWindowAttributes attrs;
77643367019cSmrg
77653367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
77663367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
77673367019cSmrg	XtermWidget xw = term;
77683367019cSmrg	TScreen *screen = TScreenOf(xw);
77693367019cSmrg
77703367019cSmrg	XtRealizeWidget(toplevel);
77713367019cSmrg
77723367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
77733367019cSmrg	       XtWindow(toplevel),
77743367019cSmrg	       winToEmbedInto));
77753367019cSmrg	XReparentWindow(dpy,
77763367019cSmrg			XtWindow(toplevel),
77773367019cSmrg			winToEmbedInto, 0, 0);
77783367019cSmrg
77793367019cSmrg	screen->embed_high = (Dimension) attrs.height;
77803367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
77813367019cSmrg    }
77823367019cSmrg}
778394644356Smrg
778494644356Smrgvoid
778594644356Smrgfree_string(String value)
778694644356Smrg{
778794644356Smrg    free((void *) value);
778894644356Smrg}
7789dfb07bc7Smrg
7790dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
7791d4fba8b9Smrgint
779250027b5bSmrgupdate_winsize(TScreen *screen, int rows, int cols, int height, int width)
7793dfb07bc7Smrg{
7794d4fba8b9Smrg    int code = -1;
7795dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
7796d4fba8b9Smrg    static int last_rows = -1;
7797d4fba8b9Smrg    static int last_cols = -1;
7798636d5e9fSmrg    static int last_high = -1;
7799636d5e9fSmrg    static int last_wide = -1;
7800636d5e9fSmrg
7801636d5e9fSmrg    TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n",
7802636d5e9fSmrg	   last_rows, last_cols, last_high, last_wide,
7803636d5e9fSmrg	   rows, cols, height, width));
7804dfb07bc7Smrg
7805636d5e9fSmrg    if (rows != last_rows
7806636d5e9fSmrg	|| cols != last_cols
7807636d5e9fSmrg	|| last_high != height
7808636d5e9fSmrg	|| last_wide != width) {
7809d4fba8b9Smrg	TTYSIZE_STRUCT ts;
7810d4fba8b9Smrg
7811d4fba8b9Smrg	last_rows = rows;
7812d4fba8b9Smrg	last_cols = cols;
7813636d5e9fSmrg	last_high = height;
7814636d5e9fSmrg	last_wide = width;
7815d4fba8b9Smrg	setup_winsize(ts, rows, cols, height, width);
781650027b5bSmrg	TRACE_RC(code, SET_TTYSIZE(screen->respond, ts));
7817d4fba8b9Smrg	trace_winsize(ts, "from SET_TTYSIZE");
7818d4fba8b9Smrg    }
7819dfb07bc7Smrg#endif
7820dfb07bc7Smrg
7821dfb07bc7Smrg    (void) rows;
7822dfb07bc7Smrg    (void) cols;
7823dfb07bc7Smrg    (void) height;
7824dfb07bc7Smrg    (void) width;
7825d4fba8b9Smrg
7826d4fba8b9Smrg    return code;
7827dfb07bc7Smrg}
7828dfb07bc7Smrg
7829dfb07bc7Smrg/*
7830dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
7831dfb07bc7Smrg * manipulation 18 and 19.
7832dfb07bc7Smrg */
7833dfb07bc7Smrgvoid
7834dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
7835dfb07bc7Smrg{
7836dfb07bc7Smrg#if OPT_TEK4014
7837dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
7838dfb07bc7Smrg#endif
7839dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
7840dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
7841dfb07bc7Smrg
7842dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
784350027b5bSmrg	    update_winsize(screen,
7844dfb07bc7Smrg			   MaxRows(screen),
7845dfb07bc7Smrg			   MaxCols(screen),
7846dfb07bc7Smrg			   Height(screen),
7847dfb07bc7Smrg			   Width(screen));
7848dfb07bc7Smrg	}
7849dfb07bc7Smrg}
7850d4fba8b9Smrg
7851980988aeSmrgstatic void
7852980988aeSmrgxtermInitTitle(TScreen *screen, int which)
7853980988aeSmrg{
7854980988aeSmrg    TRACE(("xtermInitTitle #%d\n", which));
7855980988aeSmrg    screen->saved_titles.data[which].iconName = NULL;
7856980988aeSmrg    screen->saved_titles.data[which].windowName = NULL;
7857980988aeSmrg}
7858980988aeSmrg
7859980988aeSmrg/*
7860980988aeSmrg * Store/update an item on the title stack.
7861980988aeSmrg */
7862980988aeSmrgvoid
7863980988aeSmrgxtermPushTitle(TScreen *screen, int which, SaveTitle * item)
7864980988aeSmrg{
7865980988aeSmrg    if (which-- <= 0) {
7866980988aeSmrg	which = screen->saved_titles.used++;
7867980988aeSmrg	screen->saved_titles.used %= MAX_SAVED_TITLES;
7868980988aeSmrg    }
7869980988aeSmrg    which %= MAX_SAVED_TITLES;
7870980988aeSmrg    xtermFreeTitle(&screen->saved_titles.data[which]);
7871980988aeSmrg    screen->saved_titles.data[which] = *item;
7872980988aeSmrg    TRACE(("xtermPushTitle #%d: icon='%s', window='%s'\n", which,
7873980988aeSmrg	   NonNull(item->iconName),
7874980988aeSmrg	   NonNull(item->windowName)));
7875980988aeSmrg}
7876980988aeSmrg
7877980988aeSmrg/*
7878980988aeSmrg * Pop/retrieve an item from the title stack.
7879980988aeSmrg */
7880980988aeSmrgBoolean
7881980988aeSmrgxtermPopTitle(TScreen *screen, int which, SaveTitle * item)
7882980988aeSmrg{
7883980988aeSmrg    Boolean result = True;
7884980988aeSmrg    Boolean popped = False;
7885980988aeSmrg
7886980988aeSmrg    if (which-- > 0) {
7887980988aeSmrg	which %= MAX_SAVED_TITLES;
7888980988aeSmrg    } else if (screen->saved_titles.used > 0) {
7889980988aeSmrg	which = ((--(screen->saved_titles.used) + MAX_SAVED_TITLES) % MAX_SAVED_TITLES);
7890980988aeSmrg	popped = True;
7891980988aeSmrg    } else {
7892980988aeSmrg	result = False;
7893980988aeSmrg    }
7894980988aeSmrg    if (result) {
7895980988aeSmrg	*item = screen->saved_titles.data[which];
7896980988aeSmrg	TRACE(("xtermPopTitle #%d: icon='%s', window='%s'\n", which,
7897980988aeSmrg	       NonNull(item->iconName),
7898980988aeSmrg	       NonNull(item->windowName)));
7899980988aeSmrg
7900980988aeSmrg	/* if the data is incomplete, try to get it from the next levels */
7901980988aeSmrg#define TryHigher(name) \
7902980988aeSmrg	if (item->name == NULL) { \
7903980988aeSmrg	    int n; \
7904980988aeSmrg	    for (n = 1; n < MAX_SAVED_TITLES; ++n) { \
7905980988aeSmrg		int nw = ((which - n) + MAX_SAVED_TITLES) % MAX_SAVED_TITLES; \
7906980988aeSmrg		if ((item->name = screen->saved_titles.data[nw].name) != NULL) { \
7907980988aeSmrg		    item->name = x_strdup(item->name); \
7908980988aeSmrg		    break; \
7909980988aeSmrg		} \
7910980988aeSmrg	    } \
7911980988aeSmrg	}
7912980988aeSmrg	TryHigher(iconName);
7913980988aeSmrg	TryHigher(windowName);
7914980988aeSmrg
7915980988aeSmrg	if (popped) {
7916980988aeSmrg	    xtermInitTitle(screen, which);
7917980988aeSmrg	}
7918980988aeSmrg    }
7919980988aeSmrg    return result;
7920980988aeSmrg}
7921980988aeSmrg
7922980988aeSmrg/*
7923980988aeSmrg * Discard data used for pushing or popping title.
7924980988aeSmrg */
7925980988aeSmrgvoid
7926980988aeSmrgxtermFreeTitle(SaveTitle * item)
7927980988aeSmrg{
7928980988aeSmrg    TRACE(("xtermFreeTitle icon='%s', window='%s'\n",
7929980988aeSmrg	   NonNull(item->iconName),
7930980988aeSmrg	   NonNull(item->windowName)));
7931980988aeSmrg    FreeAndNull(item->iconName);
7932980988aeSmrg    FreeAndNull(item->windowName);
7933980988aeSmrg}
7934980988aeSmrg
7935d4fba8b9Smrg#if OPT_XTERM_SGR
7936d4fba8b9Smrg
7937d4fba8b9Smrg#if OPT_TRACE
7938d4fba8b9Smrgstatic char *
7939d4fba8b9SmrgtraceIFlags(IFlags flags)
7940d4fba8b9Smrg{
7941d4fba8b9Smrg    static char result[1000];
7942d4fba8b9Smrg    result[0] = '\0';
7943d4fba8b9Smrg#define DATA(name) if (flags & name) { strcat(result, " " #name); }
7944d4fba8b9Smrg    DATA(INVERSE);
7945d4fba8b9Smrg    DATA(UNDERLINE);
7946d4fba8b9Smrg    DATA(BOLD);
7947d4fba8b9Smrg    DATA(BLINK);
7948d4fba8b9Smrg    DATA(INVISIBLE);
7949d4fba8b9Smrg    DATA(BG_COLOR);
7950d4fba8b9Smrg    DATA(FG_COLOR);
7951d4fba8b9Smrg
7952d4fba8b9Smrg#if OPT_WIDE_ATTRS
7953d4fba8b9Smrg    DATA(ATR_FAINT);
7954d4fba8b9Smrg    DATA(ATR_ITALIC);
7955d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7956d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7957d4fba8b9Smrg    DATA(ATR_DIRECT_FG);
7958d4fba8b9Smrg    DATA(ATR_DIRECT_BG);
7959d4fba8b9Smrg#endif
7960d4fba8b9Smrg#undef DATA
7961d4fba8b9Smrg    return result;
7962d4fba8b9Smrg}
7963d4fba8b9Smrg
7964d4fba8b9Smrgstatic char *
7965d4fba8b9SmrgtraceIStack(unsigned flags)
7966d4fba8b9Smrg{
7967d4fba8b9Smrg    static char result[1000];
7968d4fba8b9Smrg    result[0] = '\0';
7969d4fba8b9Smrg#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7970d4fba8b9Smrg    DATA(INVERSE);
7971d4fba8b9Smrg    DATA(UNDERLINE);
7972d4fba8b9Smrg    DATA(BOLD);
7973d4fba8b9Smrg    DATA(BLINK);
7974d4fba8b9Smrg    DATA(INVISIBLE);
7975d4fba8b9Smrg#if OPT_ISO_COLORS
7976d4fba8b9Smrg    DATA(BG_COLOR);
7977d4fba8b9Smrg    DATA(FG_COLOR);
7978d4fba8b9Smrg#endif
7979d4fba8b9Smrg
7980d4fba8b9Smrg#if OPT_WIDE_ATTRS
7981d4fba8b9Smrg    DATA(ATR_FAINT);
7982d4fba8b9Smrg    DATA(ATR_ITALIC);
7983d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7984d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7985d4fba8b9Smrg    /* direct-colors are a special case of ISO-colors (see above) */
7986d4fba8b9Smrg#endif
7987d4fba8b9Smrg#undef DATA
7988d4fba8b9Smrg    return result;
7989d4fba8b9Smrg}
7990d4fba8b9Smrg#endif
7991d4fba8b9Smrg
7992d4fba8b9Smrgvoid
7993d4fba8b9SmrgxtermPushSGR(XtermWidget xw, int value)
7994d4fba8b9Smrg{
7995d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7996d4fba8b9Smrg
7997d4fba8b9Smrg    TRACE(("xtermPushSGR %d mask %#x %s\n",
7998d4fba8b9Smrg	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
7999d4fba8b9Smrg
8000d4fba8b9Smrg    if (s->used < MAX_SAVED_SGR) {
8001d4fba8b9Smrg	s->stack[s->used].mask = (IFlags) value;
8002d4fba8b9Smrg#define PUSH_FLAG(name) \
8003d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
8004d4fba8b9Smrg	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
8005d4fba8b9Smrg#define PUSH_DATA(name) \
8006d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
8007d4fba8b9Smrg	    TRACE(("...may pop %s %d\n", #name, xw->name))
8008d4fba8b9Smrg	PUSH_FLAG(flags);
8009d4fba8b9Smrg#if OPT_ISO_COLORS
8010d4fba8b9Smrg	PUSH_DATA(sgr_foreground);
8011d4fba8b9Smrg	PUSH_DATA(sgr_background);
8012d4fba8b9Smrg	PUSH_DATA(sgr_38_xcolors);
8013d4fba8b9Smrg#endif
8014d4fba8b9Smrg    }
8015d4fba8b9Smrg    s->used++;
8016d4fba8b9Smrg}
8017d4fba8b9Smrg
8018d4fba8b9Smrg#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
8019d4fba8b9Smrg
8020d4fba8b9Smrgvoid
8021d4fba8b9SmrgxtermReportSGR(XtermWidget xw, XTermRect *value)
8022d4fba8b9Smrg{
8023d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
8024d4fba8b9Smrg    char reply[BUFSIZ];
8025d4fba8b9Smrg    CellData working;
8026d4fba8b9Smrg    int row, col;
8027d4fba8b9Smrg    Boolean first = True;
8028d4fba8b9Smrg
8029d4fba8b9Smrg    TRACE(("xtermReportSGR %d,%d - %d,%d\n",
8030d4fba8b9Smrg	   value->top, value->left,
8031d4fba8b9Smrg	   value->bottom, value->right));
8032d4fba8b9Smrg
8033d4fba8b9Smrg    memset(&working, 0, sizeof(working));
8034d4fba8b9Smrg    for (row = value->top - 1; row < value->bottom; ++row) {
8035d4fba8b9Smrg	LineData *ld = getLineData(screen, row);
8036d4fba8b9Smrg	if (ld == 0)
8037d4fba8b9Smrg	    continue;
8038d4fba8b9Smrg	for (col = value->left - 1; col < value->right; ++col) {
8039d4fba8b9Smrg	    if (first) {
8040d4fba8b9Smrg		first = False;
8041d4fba8b9Smrg		saveCellData(screen, &working, 0, ld, NULL, col);
8042d4fba8b9Smrg	    }
8043d4fba8b9Smrg	    working.attribs &= ld->attribs[col];
8044d4fba8b9Smrg#if OPT_ISO_COLORS
8045d4fba8b9Smrg	    if (working.attribs & FG_COLOR
8046d4fba8b9Smrg		&& GetCellColorFG(working.color)
8047d4fba8b9Smrg		!= GetCellColorFG(ld->color[col])) {
8048d4fba8b9Smrg		IAttrClr(working.attribs, FG_COLOR);
8049d4fba8b9Smrg	    }
8050d4fba8b9Smrg	    if (working.attribs & BG_COLOR
8051d4fba8b9Smrg		&& GetCellColorBG(working.color)
8052d4fba8b9Smrg		!= GetCellColorBG(ld->color[col])) {
8053d4fba8b9Smrg		IAttrClr(working.attribs, BG_COLOR);
8054d4fba8b9Smrg	    }
8055d4fba8b9Smrg#endif
8056d4fba8b9Smrg	}
8057d4fba8b9Smrg    }
8058d4fba8b9Smrg    xtermFormatSGR(xw, reply,
8059d4fba8b9Smrg		   working.attribs,
8060d4fba8b9Smrg		   GetCellColorFG(working.color),
8061d4fba8b9Smrg		   GetCellColorBG(working.color));
8062d4fba8b9Smrg    unparseputc1(xw, ANSI_CSI);
8063d4fba8b9Smrg    unparseputs(xw, reply);
8064d4fba8b9Smrg    unparseputc(xw, 'm');
8065d4fba8b9Smrg    unparse_end(xw);
8066d4fba8b9Smrg}
8067d4fba8b9Smrg
8068d4fba8b9Smrgvoid
8069d4fba8b9SmrgxtermPopSGR(XtermWidget xw)
8070d4fba8b9Smrg{
8071d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
8072d4fba8b9Smrg
8073d4fba8b9Smrg    TRACE(("xtermPopSGR %d\n", s->used));
8074d4fba8b9Smrg
8075d4fba8b9Smrg    if (s->used > 0) {
8076d4fba8b9Smrg	if (s->used-- <= MAX_SAVED_SGR) {
8077d4fba8b9Smrg	    IFlags mask = s->stack[s->used].mask;
8078d4fba8b9Smrg	    Boolean changed = False;
8079d4fba8b9Smrg
8080d4fba8b9Smrg	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
8081d4fba8b9Smrg	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
8082d4fba8b9Smrg	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
8083d4fba8b9Smrg#define POP_FLAG(name) \
8084d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
8085d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
8086d4fba8b9Smrg		    changed = True; \
8087d4fba8b9Smrg		    UIntClr(xw->flags, name); \
8088d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
8089d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
8090d4fba8b9Smrg		} \
8091d4fba8b9Smrg	    }
8092d4fba8b9Smrg#define POP_FLAG2(name,part) \
8093d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
8094d4fba8b9Smrg	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
8095d4fba8b9Smrg		    changed = True; \
8096d4fba8b9Smrg		    UIntClr(xw->flags, part); \
8097d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
8098d4fba8b9Smrg		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
8099d4fba8b9Smrg		} \
8100d4fba8b9Smrg	    }
8101d4fba8b9Smrg#define POP_DATA(name,value) \
8102d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
8103d4fba8b9Smrg	        Bool always = False; \
8104d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
8105d4fba8b9Smrg		    always = changed = True; \
8106d4fba8b9Smrg		    UIntClr(xw->flags, name); \
8107d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
8108d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
8109d4fba8b9Smrg		} \
8110d4fba8b9Smrg		if (always || (xw->value != s->stack[s->used].value)) { \
8111d4fba8b9Smrg		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
8112d4fba8b9Smrg		    xw->value = s->stack[s->used].value; \
8113d4fba8b9Smrg		    changed = True; \
8114d4fba8b9Smrg		} \
8115d4fba8b9Smrg	    }
8116d4fba8b9Smrg	    POP_FLAG(BOLD);
8117d4fba8b9Smrg	    POP_FLAG(UNDERLINE);
8118d4fba8b9Smrg	    POP_FLAG(BLINK);
8119d4fba8b9Smrg	    POP_FLAG(INVERSE);
8120d4fba8b9Smrg	    POP_FLAG(INVISIBLE);
8121d4fba8b9Smrg#if OPT_WIDE_ATTRS
8122d4fba8b9Smrg	    if (xBIT(psATR_ITALIC - 1) & mask) {
8123d4fba8b9Smrg		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
8124d4fba8b9Smrg	    }
8125d4fba8b9Smrg	    POP_FLAG(ATR_ITALIC);
8126d4fba8b9Smrg	    POP_FLAG(ATR_FAINT);
8127d4fba8b9Smrg	    POP_FLAG(ATR_STRIKEOUT);
8128d4fba8b9Smrg	    POP_FLAG(ATR_DBL_UNDER);
8129d4fba8b9Smrg#endif
8130d4fba8b9Smrg#if OPT_ISO_COLORS
8131d4fba8b9Smrg	    POP_DATA(FG_COLOR, sgr_foreground);
8132d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_background);
8133d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_38_xcolors);
8134d4fba8b9Smrg#if OPT_DIRECT_COLOR
8135d4fba8b9Smrg	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
8136d4fba8b9Smrg	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
8137d4fba8b9Smrg#endif
8138d4fba8b9Smrg	    if (changed) {
8139d4fba8b9Smrg		setExtendedColors(xw);
8140d4fba8b9Smrg	    }
8141d4fba8b9Smrg#else
8142d4fba8b9Smrg	    (void) changed;
8143d4fba8b9Smrg#endif
8144d4fba8b9Smrg	}
8145d4fba8b9Smrg#if OPT_ISO_COLORS
8146d4fba8b9Smrg	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
8147d4fba8b9Smrg	       traceIFlags(xw->flags),
8148d4fba8b9Smrg	       xw->sgr_foreground,
8149d4fba8b9Smrg	       xw->sgr_background,
8150d4fba8b9Smrg	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
8151d4fba8b9Smrg#else
8152d4fba8b9Smrg	TRACE(("xtermP -> flags%s\n",
8153d4fba8b9Smrg	       traceIFlags(xw->flags)));
8154d4fba8b9Smrg#endif
8155d4fba8b9Smrg    }
8156d4fba8b9Smrg}
8157d4fba8b9Smrg
8158d4fba8b9Smrg#if OPT_ISO_COLORS
8159d4fba8b9Smrgstatic ColorSlot *
8160d4fba8b9SmrgallocColorSlot(XtermWidget xw, int slot)
8161d4fba8b9Smrg{
8162d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8163d4fba8b9Smrg    ColorSlot *result = NULL;
8164d4fba8b9Smrg
8165d4fba8b9Smrg    if (slot >= 0 && slot < MAX_SAVED_SGR) {
8166d4fba8b9Smrg	if (s->palettes[slot] == NULL) {
8167d4fba8b9Smrg	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
8168d4fba8b9Smrg						     sizeof(ColorSlot)
8169d4fba8b9Smrg						     + (sizeof(ColorRes)
8170d4fba8b9Smrg							* MAXCOLORS));
8171d4fba8b9Smrg	}
8172d4fba8b9Smrg	result = s->palettes[slot];
8173d4fba8b9Smrg    }
8174d4fba8b9Smrg    return result;
8175d4fba8b9Smrg}
8176d4fba8b9Smrg
8177d4fba8b9Smrgstatic void
8178d4fba8b9SmrgpopOldColors(XtermWidget xw, ScrnColors * source)
8179d4fba8b9Smrg{
8180d4fba8b9Smrg    Boolean changed = False;
8181d4fba8b9Smrg    ScrnColors *target = xw->work.oldColors;
8182d4fba8b9Smrg
8183d4fba8b9Smrg    if (source->which != target->which) {
8184d4fba8b9Smrg	changed = True;
8185d4fba8b9Smrg    } else {
8186d4fba8b9Smrg	int n;
8187d4fba8b9Smrg	for (n = 0; n < NCOLORS; ++n) {
8188d4fba8b9Smrg	    if (COLOR_DEFINED(source, n)) {
8189d4fba8b9Smrg		if (COLOR_DEFINED(target, n)) {
8190d4fba8b9Smrg		    if (source->colors[n] != target->colors[n]) {
8191d4fba8b9Smrg			changed = True;
8192d4fba8b9Smrg			break;
8193d4fba8b9Smrg		    }
8194d4fba8b9Smrg		} else {
8195d4fba8b9Smrg		    changed = True;
8196d4fba8b9Smrg		    break;
8197d4fba8b9Smrg		}
8198d4fba8b9Smrg	    } else if (COLOR_DEFINED(target, n)) {
8199d4fba8b9Smrg		changed = True;
8200d4fba8b9Smrg		break;
8201d4fba8b9Smrg	    }
8202d4fba8b9Smrg	}
8203d4fba8b9Smrg    }
8204d4fba8b9Smrg    if (changed) {
8205d4fba8b9Smrg	ChangeColors(xw, source);
8206d4fba8b9Smrg	UpdateOldColors(xw, source);
8207d4fba8b9Smrg    }
8208d4fba8b9Smrg}
8209d4fba8b9Smrg#endif /* OPT_ISO_COLORS */
8210d4fba8b9Smrg
8211d4fba8b9Smrg#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
8212d4fba8b9Smrg#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
8213d4fba8b9Smrg
8214d4fba8b9Smrg/*
8215d4fba8b9Smrg * By default, a "push" increments the stack after copying to the current
8216d4fba8b9Smrg * slot.  But a specific target allows one to copy into a specific slot.
8217d4fba8b9Smrg */
8218d4fba8b9Smrgvoid
8219d4fba8b9SmrgxtermPushColors(XtermWidget xw, int value)
8220d4fba8b9Smrg{
8221d4fba8b9Smrg#if OPT_ISO_COLORS
8222d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8223d4fba8b9Smrg    int pushed = s->used;
8224d4fba8b9Smrg    int actual = (value <= 0) ? pushed : (value - 1);
8225d4fba8b9Smrg
8226d4fba8b9Smrg    TRACE(("xtermPushColors %d:%d\n", actual, pushed));
8227d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
8228d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
8229d4fba8b9Smrg	ColorSlot *palette;
8230d4fba8b9Smrg
8231d4fba8b9Smrg	if ((palette = allocColorSlot(xw, actual)) != NULL) {
8232d4fba8b9Smrg	    GetColors(xw, &(palette->base));
8233d4fba8b9Smrg	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
8234d4fba8b9Smrg	    if (value < 0) {
8235d4fba8b9Smrg		s->used++;
8236d4fba8b9Smrg		if (s->last < s->used)
8237d4fba8b9Smrg		    s->last = s->used;
8238d4fba8b9Smrg	    } else {
8239d4fba8b9Smrg		s->used = value;
8240d4fba8b9Smrg	    }
8241d4fba8b9Smrg	}
8242d4fba8b9Smrg    }
8243d4fba8b9Smrg#else
8244d4fba8b9Smrg    (void) xw;
8245d4fba8b9Smrg    (void) value;
8246d4fba8b9Smrg#endif
8247d4fba8b9Smrg}
8248d4fba8b9Smrg
8249d4fba8b9Smrgvoid
8250d4fba8b9SmrgxtermPopColors(XtermWidget xw, int value)
8251d4fba8b9Smrg{
8252d4fba8b9Smrg#if OPT_ISO_COLORS
8253d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8254d4fba8b9Smrg    int popped = (s->used - 1);
8255d4fba8b9Smrg    int actual = (value <= 0) ? popped : (value - 1);
8256d4fba8b9Smrg
8257d4fba8b9Smrg    TRACE(("xtermPopColors %d:%d\n", actual, popped));
8258d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
8259d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
8260d4fba8b9Smrg	ColorSlot *palette;
8261d4fba8b9Smrg
8262d4fba8b9Smrg	if ((palette = s->palettes[actual]) != NULL) {
8263d4fba8b9Smrg	    Boolean changed = DiffColorSlot(screen->Acolors,
8264d4fba8b9Smrg					    palette->ansi,
8265d4fba8b9Smrg					    MAXCOLORS);
8266d4fba8b9Smrg
8267d4fba8b9Smrg	    GetOldColors(xw);
8268d4fba8b9Smrg	    popOldColors(xw, &(palette->base));
8269d4fba8b9Smrg	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
8270d4fba8b9Smrg	    s->used = actual;
8271d4fba8b9Smrg	    if (changed)
8272d4fba8b9Smrg		xtermRepaint(xw);
8273d4fba8b9Smrg	}
8274d4fba8b9Smrg    }
8275d4fba8b9Smrg#else
8276d4fba8b9Smrg    (void) xw;
8277d4fba8b9Smrg    (void) value;
8278d4fba8b9Smrg#endif
8279d4fba8b9Smrg}
8280d4fba8b9Smrg
8281d4fba8b9Smrgvoid
8282d4fba8b9SmrgxtermReportColors(XtermWidget xw)
8283d4fba8b9Smrg{
8284d4fba8b9Smrg    ANSI reply;
8285d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
8286d4fba8b9Smrg
8287d4fba8b9Smrg    memset(&reply, 0, sizeof(reply));
8288d4fba8b9Smrg    reply.a_type = ANSI_CSI;
8289d4fba8b9Smrg    reply.a_pintro = '?';
8290d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->used;
8291d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->last;
8292d4fba8b9Smrg    reply.a_inters = '#';
8293d4fba8b9Smrg    reply.a_final = 'Q';
8294d4fba8b9Smrg    unparseseq(xw, &reply);
8295d4fba8b9Smrg}
8296d4fba8b9Smrg#endif /* OPT_XTERM_SGR */
8297