misc.c revision d4fba8b9
1d4fba8b9Smrg/* $XTermId: misc.c,v 1.968 2021/02/10 00:50:59 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/*
4d4fba8b9Smrg * Copyright 1999-2020,2021 by Thomas E. Dickey
5d522f475Smrg *
6cd3331d0Smrg *                         All Rights Reserved
7d522f475Smrg *
8d522f475Smrg * Permission is hereby granted, free of charge, to any person obtaining a
9d522f475Smrg * copy of this software and associated documentation files (the
10d522f475Smrg * "Software"), to deal in the Software without restriction, including
11d522f475Smrg * without limitation the rights to use, copy, modify, merge, publish,
12d522f475Smrg * distribute, sublicense, and/or sell copies of the Software, and to
13d522f475Smrg * permit persons to whom the Software is furnished to do so, subject to
14d522f475Smrg * the following conditions:
15d522f475Smrg *
16d522f475Smrg * The above copyright notice and this permission notice shall be included
17d522f475Smrg * in all copies or substantial portions of the Software.
18d522f475Smrg *
19d522f475Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20d522f475Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21d522f475Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22d522f475Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23d522f475Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24d522f475Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25d522f475Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26d522f475Smrg *
27d522f475Smrg * Except as contained in this notice, the name(s) of the above copyright
28d522f475Smrg * holders shall not be used in advertising or otherwise to promote the
29d522f475Smrg * sale, use or other dealings in this Software without prior written
30d522f475Smrg * authorization.
31d522f475Smrg *
32d522f475Smrg *
33d522f475Smrg * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
34d522f475Smrg *
35d522f475Smrg *                         All Rights Reserved
36d522f475Smrg *
37d522f475Smrg * Permission to use, copy, modify, and distribute this software and its
38d522f475Smrg * documentation for any purpose and without fee is hereby granted,
39d522f475Smrg * provided that the above copyright notice appear in all copies and that
40d522f475Smrg * both that copyright notice and this permission notice appear in
41d522f475Smrg * supporting documentation, and that the name of Digital Equipment
42d522f475Smrg * Corporation not be used in advertising or publicity pertaining to
43d522f475Smrg * distribution of the software without specific, written prior permission.
44d522f475Smrg *
45d522f475Smrg *
46d522f475Smrg * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
47d522f475Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
48d522f475Smrg * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
49d522f475Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
50d522f475Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
51d522f475Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
52d522f475Smrg * SOFTWARE.
53d522f475Smrg */
54d522f475Smrg
55d522f475Smrg#include <version.h>
56b7c89284Ssnj#include <main.h>
57d522f475Smrg#include <xterm.h>
58dfb07bc7Smrg#include <xterm_io.h>
59d522f475Smrg
60d522f475Smrg#include <sys/stat.h>
61d522f475Smrg#include <stdio.h>
623367019cSmrg#include <stdarg.h>
63d522f475Smrg#include <signal.h>
64d522f475Smrg#include <ctype.h>
65d522f475Smrg#include <pwd.h>
66d522f475Smrg#include <sys/wait.h>
67d522f475Smrg
68d522f475Smrg#include <X11/keysym.h>
69d522f475Smrg#include <X11/Xatom.h>
70d522f475Smrg#include <X11/cursorfont.h>
71d522f475Smrg
72d522f475Smrg#include <X11/Xmu/Error.h>
73d522f475Smrg#include <X11/Xmu/SysUtil.h>
74d522f475Smrg#include <X11/Xmu/WinUtil.h>
75d522f475Smrg#include <X11/Xmu/Xmu.h>
76d522f475Smrg#if HAVE_X11_SUNKEYSYM_H
77d522f475Smrg#include <X11/Sunkeysym.h>
78d522f475Smrg#endif
79d522f475Smrg
803367019cSmrg#ifdef HAVE_LIBXPM
813367019cSmrg#include <X11/xpm.h>
823367019cSmrg#endif
833367019cSmrg
84d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
85d522f475Smrg#include <langinfo.h>
86d522f475Smrg#endif
87d522f475Smrg
88d522f475Smrg#include <xutf8.h>
89d522f475Smrg
90d522f475Smrg#include <data.h>
91d522f475Smrg#include <error.h>
92d522f475Smrg#include <menu.h>
93d522f475Smrg#include <fontutils.h>
94d522f475Smrg#include <xstrings.h>
95d522f475Smrg#include <xtermcap.h>
96d522f475Smrg#include <VTparse.h>
97fa3f02f3Smrg#include <graphics.h>
989a64e1c5Smrg#include <graphics_regis.h>
999a64e1c5Smrg#include <graphics_sixel.h>
100d522f475Smrg
101d522f475Smrg#include <assert.h>
102d522f475Smrg
103d522f475Smrg#ifdef VMS
104d522f475Smrg#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT"
105d522f475Smrg#ifdef ALLOWLOGFILEEXEC
106d522f475Smrg#undef ALLOWLOGFILEEXEC
107d522f475Smrg#endif
108d522f475Smrg#endif /* VMS */
109d522f475Smrg
110d4fba8b9Smrg#if USE_DOUBLE_BUFFER
111d4fba8b9Smrg#include <X11/extensions/Xdbe.h>
112d4fba8b9Smrg#endif
113d4fba8b9Smrg
114d4fba8b9Smrg#if OPT_WIDE_CHARS
115d4fba8b9Smrg#include <wctype.h>
116d4fba8b9Smrg#endif
117d4fba8b9Smrg
118d522f475Smrg#if OPT_TEK4014
119d522f475Smrg#define OUR_EVENT(event,Type) \
120d522f475Smrg		(event.type == Type && \
121d522f475Smrg		  (event.xcrossing.window == XtWindow(XtParent(xw)) || \
122d522f475Smrg		    (tekWidget && \
123d522f475Smrg		     event.xcrossing.window == XtWindow(XtParent(tekWidget)))))
124d522f475Smrg#else
125d522f475Smrg#define OUR_EVENT(event,Type) \
126d522f475Smrg		(event.type == Type && \
127d522f475Smrg		   (event.xcrossing.window == XtWindow(XtParent(xw))))
128d522f475Smrg#endif
129d522f475Smrg
130d4fba8b9Smrg#define VB_DELAY    screen->visualBellDelay
131d4fba8b9Smrg#define EVENT_DELAY TScreenOf(term)->nextEventDelay
132d4fba8b9Smrg
1333367019cSmrgstatic Boolean xtermAllocColor(XtermWidget, XColor *, const char *);
134d522f475Smrgstatic Cursor make_hidden_cursor(XtermWidget);
135d522f475Smrg
1363367019cSmrgstatic char emptyString[] = "";
1373367019cSmrg
138d522f475Smrg#if OPT_EXEC_XTERM
139d522f475Smrg/* Like readlink(2), but returns a malloc()ed buffer, or NULL on
140d522f475Smrg   error; adapted from libc docs */
141d522f475Smrgstatic char *
142d522f475SmrgReadlink(const char *filename)
143d522f475Smrg{
144d522f475Smrg    char *buf = NULL;
145cd3331d0Smrg    size_t size = 100;
146d522f475Smrg
147d522f475Smrg    for (;;) {
148037a25ddSmrg	int n;
149037a25ddSmrg	char *tmp = TypeRealloc(char, size, buf);
150037a25ddSmrg	if (tmp == NULL) {
151037a25ddSmrg	    free(buf);
152037a25ddSmrg	    return NULL;
153037a25ddSmrg	}
154037a25ddSmrg	buf = tmp;
155d522f475Smrg	memset(buf, 0, size);
156d522f475Smrg
157cd3331d0Smrg	n = (int) readlink(filename, buf, size);
158d522f475Smrg	if (n < 0) {
159d522f475Smrg	    free(buf);
160d522f475Smrg	    return NULL;
161d522f475Smrg	}
162d522f475Smrg
163d522f475Smrg	if ((unsigned) n < size) {
164d522f475Smrg	    return buf;
165d522f475Smrg	}
166d522f475Smrg
167d522f475Smrg	size *= 2;
168d522f475Smrg    }
169d522f475Smrg}
170d522f475Smrg#endif /* OPT_EXEC_XTERM */
171d522f475Smrg
172d522f475Smrgstatic void
173d522f475SmrgSleep(int msec)
174d522f475Smrg{
175d522f475Smrg    static struct timeval select_timeout;
176d522f475Smrg
177d522f475Smrg    select_timeout.tv_sec = 0;
178d522f475Smrg    select_timeout.tv_usec = msec * 1000;
179d522f475Smrg    select(0, 0, 0, 0, &select_timeout);
180d522f475Smrg}
181d522f475Smrg
182d522f475Smrgstatic void
1833367019cSmrgselectwindow(XtermWidget xw, int flag)
184d522f475Smrg{
1853367019cSmrg    TScreen *screen = TScreenOf(xw);
1863367019cSmrg
187d522f475Smrg    TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag));
188d522f475Smrg
189d522f475Smrg#if OPT_TEK4014
1903367019cSmrg    if (TEK4014_ACTIVE(xw)) {
191d522f475Smrg	if (!Ttoggled)
192d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
193d522f475Smrg	screen->select |= flag;
194d522f475Smrg	if (!Ttoggled)
195d522f475Smrg	    TCursorToggle(tekWidget, TOGGLE);
196d522f475Smrg    } else
197d522f475Smrg#endif
198d522f475Smrg    {
199d4fba8b9Smrg#if OPT_INPUT_METHOD
2003367019cSmrg	TInput *input = lookupTInput(xw, (Widget) xw);
2013367019cSmrg	if (input && input->xic)
2023367019cSmrg	    XSetICFocus(input->xic);
2033367019cSmrg#endif
204d522f475Smrg
205d522f475Smrg	if (screen->cursor_state && CursorMoved(screen))
206d4fba8b9Smrg	    HideCursor(xw);
207d522f475Smrg	screen->select |= flag;
208d522f475Smrg	if (screen->cursor_state)
209d4fba8b9Smrg	    ShowCursor(xw);
210d522f475Smrg    }
211cd3331d0Smrg    GetScrollLock(screen);
212d522f475Smrg}
213d522f475Smrg
214d522f475Smrgstatic void
2153367019cSmrgunselectwindow(XtermWidget xw, int flag)
216d522f475Smrg{
2173367019cSmrg    TScreen *screen = TScreenOf(xw);
2183367019cSmrg
219d522f475Smrg    TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag));
220d522f475Smrg
2213367019cSmrg    if (screen->hide_pointer && screen->pointer_mode < pFocused) {
222d522f475Smrg	screen->hide_pointer = False;
2233367019cSmrg	xtermDisplayCursor(xw);
224d522f475Smrg    }
225d522f475Smrg
226d4fba8b9Smrg    screen->select &= ~flag;
227d4fba8b9Smrg
228d522f475Smrg    if (!screen->always_highlight) {
229d522f475Smrg#if OPT_TEK4014
2303367019cSmrg	if (TEK4014_ACTIVE(xw)) {
231d522f475Smrg	    if (!Ttoggled)
232d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
233d522f475Smrg	    if (!Ttoggled)
234d522f475Smrg		TCursorToggle(tekWidget, TOGGLE);
235d522f475Smrg	} else
236d522f475Smrg#endif
237d522f475Smrg	{
238d4fba8b9Smrg#if OPT_INPUT_METHOD
2393367019cSmrg	    TInput *input = lookupTInput(xw, (Widget) xw);
2403367019cSmrg	    if (input && input->xic)
2413367019cSmrg		XUnsetICFocus(input->xic);
2423367019cSmrg#endif
243d522f475Smrg
244d522f475Smrg	    if (screen->cursor_state && CursorMoved(screen))
245d4fba8b9Smrg		HideCursor(xw);
246d522f475Smrg	    if (screen->cursor_state)
247d4fba8b9Smrg		ShowCursor(xw);
248d522f475Smrg	}
249d522f475Smrg    }
250d522f475Smrg}
251d522f475Smrg
252d522f475Smrgstatic void
2539a64e1c5SmrgDoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev)
254d522f475Smrg{
255d522f475Smrg    TScreen *screen = TScreenOf(xw);
256d522f475Smrg
257d522f475Smrg    TRACE(("DoSpecialEnterNotify(%d)\n", screen->select));
258cd3331d0Smrg    TRACE_FOCUS(xw, ev);
259cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
260cd3331d0Smrg	ev->focus &&
261cd3331d0Smrg	!(screen->select & FOCUS))
2623367019cSmrg	selectwindow(xw, INWINDOW);
263d522f475Smrg}
264d522f475Smrg
265d522f475Smrgstatic void
2669a64e1c5SmrgDoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev)
267d522f475Smrg{
268d522f475Smrg    TScreen *screen = TScreenOf(xw);
269d522f475Smrg
270d522f475Smrg    TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select));
271cd3331d0Smrg    TRACE_FOCUS(xw, ev);
272cd3331d0Smrg    if (((ev->detail) != NotifyInferior) &&
273cd3331d0Smrg	ev->focus &&
274cd3331d0Smrg	!(screen->select & FOCUS))
2753367019cSmrg	unselectwindow(xw, INWINDOW);
276d522f475Smrg}
277d522f475Smrg
278d522f475Smrg#ifndef XUrgencyHint
279d522f475Smrg#define XUrgencyHint (1L << 8)	/* X11R5 does not define */
280d522f475Smrg#endif
281d522f475Smrg
282d522f475Smrgstatic void
283c219fbebSmrgsetXUrgency(XtermWidget xw, Bool enable)
284d522f475Smrg{
285c219fbebSmrg    TScreen *screen = TScreenOf(xw);
286c219fbebSmrg
287d522f475Smrg    if (screen->bellIsUrgent) {
288c219fbebSmrg	XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw));
289d522f475Smrg	if (h != 0) {
290c219fbebSmrg	    if (enable && !(screen->select & FOCUS)) {
291d522f475Smrg		h->flags |= XUrgencyHint;
292d522f475Smrg	    } else {
293d522f475Smrg		h->flags &= ~XUrgencyHint;
294d522f475Smrg	    }
295c219fbebSmrg	    XSetWMHints(screen->display, VShellWindow(xw), h);
296d522f475Smrg	}
297d522f475Smrg    }
298d522f475Smrg}
299d522f475Smrg
300d522f475Smrgvoid
301d4fba8b9Smrgdo_xevents(XtermWidget xw)
302d522f475Smrg{
303d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
304d522f475Smrg
3053367019cSmrg    if (xtermAppPending()
306d522f475Smrg	||
307d522f475Smrg#if defined(VMS) || defined(__VMS)
308d522f475Smrg	screen->display->qlen > 0
309d522f475Smrg#else
310d522f475Smrg	GetBytesAvailable(ConnectionNumber(screen->display)) > 0
311d522f475Smrg#endif
312d522f475Smrg	)
313d4fba8b9Smrg	xevents(xw);
314d522f475Smrg}
315d522f475Smrg
316d522f475Smrgvoid
317d522f475SmrgxtermDisplayCursor(XtermWidget xw)
318d522f475Smrg{
319d522f475Smrg    TScreen *screen = TScreenOf(xw);
320d522f475Smrg
321d522f475Smrg    if (screen->Vshow) {
322d522f475Smrg	if (screen->hide_pointer) {
323d522f475Smrg	    TRACE(("Display hidden_cursor\n"));
324d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor);
325d522f475Smrg	} else {
326d522f475Smrg	    TRACE(("Display pointer_cursor\n"));
327d522f475Smrg	    recolor_cursor(screen,
328d522f475Smrg			   screen->pointer_cursor,
329d522f475Smrg			   T_COLOR(screen, MOUSE_FG),
330d522f475Smrg			   T_COLOR(screen, MOUSE_BG));
331d522f475Smrg	    XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor);
332d522f475Smrg	}
333d522f475Smrg    }
334d522f475Smrg}
335d522f475Smrg
336d522f475Smrgvoid
337d522f475SmrgxtermShowPointer(XtermWidget xw, Bool enable)
338d522f475Smrg{
339d522f475Smrg    static int tried = -1;
340d522f475Smrg    TScreen *screen = TScreenOf(xw);
341d522f475Smrg
342d522f475Smrg#if OPT_TEK4014
343d522f475Smrg    if (TEK4014_SHOWN(xw))
344d522f475Smrg	enable = True;
345d522f475Smrg#endif
346d522f475Smrg
347d522f475Smrg    /*
348d522f475Smrg     * Whether we actually hide the pointer depends on the pointer-mode and
349d522f475Smrg     * the mouse-mode:
350d522f475Smrg     */
351d522f475Smrg    if (!enable) {
352d522f475Smrg	switch (screen->pointer_mode) {
353d522f475Smrg	case pNever:
354d522f475Smrg	    enable = True;
355d522f475Smrg	    break;
356d522f475Smrg	case pNoMouse:
357d522f475Smrg	    if (screen->send_mouse_pos != MOUSE_OFF)
358d522f475Smrg		enable = True;
359d522f475Smrg	    break;
360d522f475Smrg	case pAlways:
3613367019cSmrg	case pFocused:
362d522f475Smrg	    break;
363d522f475Smrg	}
364d522f475Smrg    }
365d522f475Smrg
366d522f475Smrg    if (enable) {
367d522f475Smrg	if (screen->hide_pointer) {
368d522f475Smrg	    screen->hide_pointer = False;
369d522f475Smrg	    xtermDisplayCursor(xw);
370d522f475Smrg	    switch (screen->send_mouse_pos) {
371d522f475Smrg	    case ANY_EVENT_MOUSE:
372d522f475Smrg		break;
373d522f475Smrg	    default:
374d522f475Smrg		MotionOff(screen, xw);
375d522f475Smrg		break;
376d522f475Smrg	    }
377d522f475Smrg	}
378d522f475Smrg    } else if (!(screen->hide_pointer) && (tried <= 0)) {
379d522f475Smrg	if (screen->hidden_cursor == 0) {
380d522f475Smrg	    screen->hidden_cursor = make_hidden_cursor(xw);
381d522f475Smrg	}
382d522f475Smrg	if (screen->hidden_cursor == 0) {
383d522f475Smrg	    tried = 1;
384d522f475Smrg	} else {
385d522f475Smrg	    tried = 0;
386d522f475Smrg	    screen->hide_pointer = True;
387d522f475Smrg	    xtermDisplayCursor(xw);
388d522f475Smrg	    MotionOn(screen, xw);
389d522f475Smrg	}
390d522f475Smrg    }
391d522f475Smrg}
392d522f475Smrg
3933367019cSmrg/* true if p contains q */
3943367019cSmrg#define ExposeContains(p,q) \
3953367019cSmrg	    ((p)->y <= (q)->y \
3963367019cSmrg	  && (p)->x <= (q)->x \
3973367019cSmrg	  && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \
3983367019cSmrg	  && ((p)->x + (p)->width) >= ((q)->x + (q)->width))
3993367019cSmrg
4003367019cSmrgstatic XtInputMask
4019a64e1c5SmrgmergeExposeEvents(XEvent *target)
4023367019cSmrg{
4033367019cSmrg    XEvent next_event;
404037a25ddSmrg    XExposeEvent *p;
4053367019cSmrg
4063367019cSmrg    XtAppNextEvent(app_con, target);
4073367019cSmrg    p = (XExposeEvent *) target;
4083367019cSmrg
4093367019cSmrg    while (XtAppPending(app_con)
4103367019cSmrg	   && XtAppPeekEvent(app_con, &next_event)
4113367019cSmrg	   && next_event.type == Expose) {
4123367019cSmrg	Boolean merge_this = False;
413d4fba8b9Smrg	XExposeEvent *q = (XExposeEvent *) (&next_event);
4143367019cSmrg
4153367019cSmrg	XtAppNextEvent(app_con, &next_event);
416d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4173367019cSmrg
4183367019cSmrg	/*
4193367019cSmrg	 * If either window is contained within the other, merge the events.
4203367019cSmrg	 * The traces show that there are also cases where a full repaint of
4213367019cSmrg	 * a window is broken into 3 or more rectangles, which do not arrive
4223367019cSmrg	 * in the same instant.  We could merge those if xterm were modified
4233367019cSmrg	 * to skim several events ahead.
4243367019cSmrg	 */
4253367019cSmrg	if (p->window == q->window) {
4263367019cSmrg	    if (ExposeContains(p, q)) {
4273367019cSmrg		TRACE(("pending Expose...merged forward\n"));
4283367019cSmrg		merge_this = True;
4293367019cSmrg		next_event = *target;
4303367019cSmrg	    } else if (ExposeContains(q, p)) {
4313367019cSmrg		TRACE(("pending Expose...merged backward\n"));
4323367019cSmrg		merge_this = True;
4333367019cSmrg	    }
4343367019cSmrg	}
4353367019cSmrg	if (!merge_this) {
4363367019cSmrg	    XtDispatchEvent(target);
4373367019cSmrg	}
4383367019cSmrg	*target = next_event;
4393367019cSmrg    }
4403367019cSmrg    XtDispatchEvent(target);
4413367019cSmrg    return XtAppPending(app_con);
4423367019cSmrg}
4433367019cSmrg
4443367019cSmrg/*
4453367019cSmrg * On entry, we have peeked at the event queue and see a configure-notify
4463367019cSmrg * event.  Remove that from the queue so we can look further.
4473367019cSmrg *
4483367019cSmrg * Then, as long as there is a configure-notify event in the queue, remove
4493367019cSmrg * that.  If the adjacent events are for different windows, process the older
4503367019cSmrg * event and update the event used for comparing windows.  If they are for the
4513367019cSmrg * same window, only the newer event is of interest.
4523367019cSmrg *
4533367019cSmrg * Finally, process the (remaining) configure-notify event.
4543367019cSmrg */
4553367019cSmrgstatic XtInputMask
4569a64e1c5SmrgmergeConfigureEvents(XEvent *target)
4573367019cSmrg{
4583367019cSmrg    XEvent next_event;
459037a25ddSmrg    XConfigureEvent *p;
4603367019cSmrg
4613367019cSmrg    XtAppNextEvent(app_con, target);
4623367019cSmrg    p = (XConfigureEvent *) target;
4633367019cSmrg
4643367019cSmrg    if (XtAppPending(app_con)
4653367019cSmrg	&& XtAppPeekEvent(app_con, &next_event)
4663367019cSmrg	&& next_event.type == ConfigureNotify) {
4673367019cSmrg	Boolean merge_this = False;
468d4fba8b9Smrg	XConfigureEvent *q = (XConfigureEvent *) (&next_event);
4693367019cSmrg
4703367019cSmrg	XtAppNextEvent(app_con, &next_event);
471d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
4723367019cSmrg
4733367019cSmrg	if (p->window == q->window) {
4743367019cSmrg	    TRACE(("pending Configure...merged\n"));
4753367019cSmrg	    merge_this = True;
4763367019cSmrg	}
4773367019cSmrg	if (!merge_this) {
4783367019cSmrg	    TRACE(("pending Configure...skipped\n"));
4793367019cSmrg	    XtDispatchEvent(target);
4803367019cSmrg	}
4813367019cSmrg	*target = next_event;
4823367019cSmrg    }
4833367019cSmrg    XtDispatchEvent(target);
4843367019cSmrg    return XtAppPending(app_con);
4853367019cSmrg}
4863367019cSmrg
487d4fba8b9Smrg#define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name)
488d4fba8b9Smrg#define SameButtonEvent(a,b) ( \
489d4fba8b9Smrg	SAME(a,b,type) && \
490d4fba8b9Smrg	SAME(a,b,serial) && \
491d4fba8b9Smrg	SAME(a,b,send_event) && \
492d4fba8b9Smrg	SAME(a,b,display) && \
493d4fba8b9Smrg	SAME(a,b,window) && \
494d4fba8b9Smrg	SAME(a,b,root) && \
495d4fba8b9Smrg	SAME(a,b,subwindow) && \
496d4fba8b9Smrg	SAME(a,b,time) && \
497d4fba8b9Smrg	SAME(a,b,x) && \
498d4fba8b9Smrg	SAME(a,b,y) && \
499d4fba8b9Smrg	SAME(a,b,x_root) && \
500d4fba8b9Smrg	SAME(a,b,y_root) && \
501d4fba8b9Smrg	SAME(a,b,state) && \
502d4fba8b9Smrg	SAME(a,b,button) && \
503d4fba8b9Smrg	SAME(a,b,same_screen))
504d4fba8b9Smrg
505d4fba8b9Smrg/*
506d4fba8b9Smrg * Work around a bug in the X mouse code, which delivers duplicate events.
507d4fba8b9Smrg */
508d4fba8b9Smrgstatic XtInputMask
509d4fba8b9SmrgmergeButtonEvents(XEvent *target)
510d4fba8b9Smrg{
511d4fba8b9Smrg    XEvent next_event;
512d4fba8b9Smrg    XButtonEvent *p;
513d4fba8b9Smrg
514d4fba8b9Smrg    XtAppNextEvent(app_con, target);
515d4fba8b9Smrg    p = (XButtonEvent *) target;
516d4fba8b9Smrg
517d4fba8b9Smrg    if (XtAppPending(app_con)
518d4fba8b9Smrg	&& XtAppPeekEvent(app_con, &next_event)
519d4fba8b9Smrg	&& SameButtonEvent(target, &next_event)) {
520d4fba8b9Smrg	Boolean merge_this = False;
521d4fba8b9Smrg	XButtonEvent *q = (XButtonEvent *) (&next_event);
522d4fba8b9Smrg
523d4fba8b9Smrg	XtAppNextEvent(app_con, &next_event);
524d4fba8b9Smrg	TRACE_EVENT("pending", &next_event, (String *) 0, 0);
525d4fba8b9Smrg
526d4fba8b9Smrg	if (p->window == q->window) {
527d4fba8b9Smrg	    TRACE(("pending ButtonEvent...merged\n"));
528d4fba8b9Smrg	    merge_this = True;
529d4fba8b9Smrg	}
530d4fba8b9Smrg	if (!merge_this) {
531d4fba8b9Smrg	    TRACE(("pending ButtonEvent...skipped\n"));
532d4fba8b9Smrg	    XtDispatchEvent(target);
533d4fba8b9Smrg	}
534d4fba8b9Smrg	*target = next_event;
535d4fba8b9Smrg    }
536d4fba8b9Smrg    XtDispatchEvent(target);
537d4fba8b9Smrg    return XtAppPending(app_con);
538d4fba8b9Smrg}
539d4fba8b9Smrg
5403367019cSmrg/*
5413367019cSmrg * Filter redundant Expose- and ConfigureNotify-events.  This is limited to
5423367019cSmrg * adjacent events because there could be other event-loop processing.  Absent
5433367019cSmrg * that limitation, it might be possible to scan ahead to find when the screen
5443367019cSmrg * would be completely updated, skipping unnecessary re-repainting before that
5453367019cSmrg * point.
5463367019cSmrg *
5473367019cSmrg * Note: all cases should allow doing XtAppNextEvent if result is true.
5483367019cSmrg */
5493367019cSmrgXtInputMask
5503367019cSmrgxtermAppPending(void)
5513367019cSmrg{
5523367019cSmrg    XtInputMask result = XtAppPending(app_con);
5533367019cSmrg    XEvent this_event;
554037a25ddSmrg    Boolean found = False;
5553367019cSmrg
5563367019cSmrg    while (result && XtAppPeekEvent(app_con, &this_event)) {
557037a25ddSmrg	found = True;
558d4fba8b9Smrg	TRACE_EVENT("pending", &this_event, (String *) 0, 0);
5593367019cSmrg	if (this_event.type == Expose) {
5603367019cSmrg	    result = mergeExposeEvents(&this_event);
5613367019cSmrg	} else if (this_event.type == ConfigureNotify) {
5623367019cSmrg	    result = mergeConfigureEvents(&this_event);
563d4fba8b9Smrg	} else if (this_event.type == ButtonPress ||
564d4fba8b9Smrg		   this_event.type == ButtonRelease) {
565d4fba8b9Smrg	    result = mergeButtonEvents(&this_event);
5663367019cSmrg	} else {
5673367019cSmrg	    break;
5683367019cSmrg	}
5693367019cSmrg    }
570037a25ddSmrg
571037a25ddSmrg    /*
572037a25ddSmrg     * With NetBSD, closing a shell results in closing the X input event
573037a25ddSmrg     * stream, which interferes with the "-hold" option.  Wait a short time in
574037a25ddSmrg     * this case, to avoid max'ing the CPU.
575037a25ddSmrg     */
576037a25ddSmrg    if (hold_screen && caught_intr && !found) {
577d4fba8b9Smrg	Sleep(EVENT_DELAY);
578037a25ddSmrg    }
5793367019cSmrg    return result;
5803367019cSmrg}
5813367019cSmrg
582d522f475Smrgvoid
583d4fba8b9Smrgxevents(XtermWidget xw)
584d522f475Smrg{
585d522f475Smrg    TScreen *screen = TScreenOf(xw);
586d522f475Smrg    XEvent event;
587d522f475Smrg    XtInputMask input_mask;
588d522f475Smrg
589d522f475Smrg    if (need_cleanup)
5903367019cSmrg	NormalExit();
591d522f475Smrg
592d522f475Smrg    if (screen->scroll_amt)
593d522f475Smrg	FlushScroll(xw);
594d522f475Smrg    /*
595d522f475Smrg     * process timeouts, relying on the fact that XtAppProcessEvent
596d522f475Smrg     * will process the timeout and return without blockng on the
597cd3331d0Smrg     * XEvent queue.  Other sources i.e., the pty are handled elsewhere
598d522f475Smrg     * with select().
599d522f475Smrg     */
6003367019cSmrg    while ((input_mask = xtermAppPending()) != 0) {
601cd3331d0Smrg	if (input_mask & XtIMTimer)
602cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer);
603d522f475Smrg#if OPT_SESSION_MGT
604cd3331d0Smrg	/*
605cd3331d0Smrg	 * Session management events are alternative input events. Deal with
606cd3331d0Smrg	 * them in the same way.
607cd3331d0Smrg	 */
608cd3331d0Smrg	else if (input_mask & XtIMAlternateInput)
609cd3331d0Smrg	    XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput);
610d522f475Smrg#endif
611cd3331d0Smrg	else
612cd3331d0Smrg	    break;
613cd3331d0Smrg    }
614d522f475Smrg
615d522f475Smrg    /*
616d4fba8b9Smrg     * If there are no XEvents, don't wait around...
617d522f475Smrg     */
618d522f475Smrg    if ((input_mask & XtIMXEvent) != XtIMXEvent)
619d522f475Smrg	return;
620d522f475Smrg    do {
621d522f475Smrg	/*
622d522f475Smrg	 * This check makes xterm hang when in mouse hilite tracking mode.
623d522f475Smrg	 * We simply ignore all events except for those not passed down to
624d522f475Smrg	 * this function, e.g., those handled in in_put().
625d522f475Smrg	 */
626d522f475Smrg	if (screen->waitingForTrackInfo) {
627d4fba8b9Smrg	    Sleep(EVENT_DELAY);
628d522f475Smrg	    return;
629d522f475Smrg	}
630d522f475Smrg	XtAppNextEvent(app_con, &event);
631d522f475Smrg	/*
632d522f475Smrg	 * Hack to get around problems with the toolkit throwing away
633d522f475Smrg	 * eventing during the exclusive grab of the menu popup.  By
634d522f475Smrg	 * looking at the event ourselves we make sure that we can
635d522f475Smrg	 * do the right thing.
636d522f475Smrg	 */
637d522f475Smrg	if (OUR_EVENT(event, EnterNotify)) {
638d522f475Smrg	    DoSpecialEnterNotify(xw, &event.xcrossing);
639d522f475Smrg	} else if (OUR_EVENT(event, LeaveNotify)) {
640d522f475Smrg	    DoSpecialLeaveNotify(xw, &event.xcrossing);
641d4fba8b9Smrg	} else if (event.xany.type == MotionNotify
642d4fba8b9Smrg		   && event.xcrossing.window == XtWindow(xw)) {
643d4fba8b9Smrg	    switch (screen->send_mouse_pos) {
644d4fba8b9Smrg	    case ANY_EVENT_MOUSE:
645d522f475Smrg#if OPT_DEC_LOCATOR
646d4fba8b9Smrg	    case DEC_LOCATOR:
647d522f475Smrg#endif /* OPT_DEC_LOCATOR */
648d4fba8b9Smrg		SendMousePosition(xw, &event);
649d4fba8b9Smrg		xtermShowPointer(xw, True);
650d4fba8b9Smrg		continue;
651d4fba8b9Smrg	    case BTN_EVENT_MOUSE:
652d4fba8b9Smrg		SendMousePosition(xw, &event);
653d4fba8b9Smrg		xtermShowPointer(xw, True);
654d4fba8b9Smrg	    }
655d522f475Smrg	}
656d522f475Smrg
657cb4a1343Smrg	/*
658cb4a1343Smrg	 * If the event is interesting (and not a keyboard event), turn the
659cb4a1343Smrg	 * mouse pointer back on.
660cb4a1343Smrg	 */
661cb4a1343Smrg	if (screen->hide_pointer) {
6623367019cSmrg	    if (screen->pointer_mode >= pFocused) {
6633367019cSmrg		switch (event.xany.type) {
6643367019cSmrg		case MotionNotify:
6653367019cSmrg		    xtermShowPointer(xw, True);
6663367019cSmrg		    break;
6673367019cSmrg		}
6683367019cSmrg	    } else {
6693367019cSmrg		switch (event.xany.type) {
6703367019cSmrg		case KeyPress:
6713367019cSmrg		case KeyRelease:
6723367019cSmrg		case ButtonPress:
6733367019cSmrg		case ButtonRelease:
6743367019cSmrg		    /* also these... */
6753367019cSmrg		case Expose:
676037a25ddSmrg		case GraphicsExpose:
6773367019cSmrg		case NoExpose:
6783367019cSmrg		case PropertyNotify:
6793367019cSmrg		case ClientMessage:
6803367019cSmrg		    break;
6813367019cSmrg		default:
6823367019cSmrg		    xtermShowPointer(xw, True);
6833367019cSmrg		    break;
6843367019cSmrg		}
685cb4a1343Smrg	    }
686cb4a1343Smrg	}
687cb4a1343Smrg
688d522f475Smrg	if (!event.xany.send_event ||
689d522f475Smrg	    screen->allowSendEvents ||
690d522f475Smrg	    ((event.xany.type != KeyPress) &&
691d522f475Smrg	     (event.xany.type != KeyRelease) &&
692d522f475Smrg	     (event.xany.type != ButtonPress) &&
693d522f475Smrg	     (event.xany.type != ButtonRelease))) {
694d522f475Smrg
695d4fba8b9Smrg	    if (event.xany.type == MappingNotify) {
696d4fba8b9Smrg		XRefreshKeyboardMapping(&(event.xmapping));
697d4fba8b9Smrg		VTInitModifiers(xw);
698d4fba8b9Smrg	    }
699d522f475Smrg	    XtDispatchEvent(&event);
700d522f475Smrg	}
7013367019cSmrg    } while (xtermAppPending() & XtIMXEvent);
702d522f475Smrg}
703d522f475Smrg
704d522f475Smrgstatic Cursor
705d522f475Smrgmake_hidden_cursor(XtermWidget xw)
706d522f475Smrg{
707d522f475Smrg    TScreen *screen = TScreenOf(xw);
708d522f475Smrg    Cursor c;
709d522f475Smrg    Display *dpy = screen->display;
710d522f475Smrg    XFontStruct *fn;
711d522f475Smrg
712d522f475Smrg    static XColor dummy;
713d522f475Smrg
714d522f475Smrg    /*
715d522f475Smrg     * Prefer nil2 (which is normally available) to "fixed" (which is supposed
716d522f475Smrg     * to be "always" available), since it's a smaller glyph in case the
717b7c89284Ssnj     * server insists on drawing _something_.
718d522f475Smrg     */
719d522f475Smrg    TRACE(("Ask for nil2 font\n"));
720d522f475Smrg    if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) {
721d522f475Smrg	TRACE(("...Ask for fixed font\n"));
722b7c89284Ssnj	fn = XLoadQueryFont(dpy, DEFFONT);
723d522f475Smrg    }
724d522f475Smrg
725d4fba8b9Smrg    if (fn != None) {
726d522f475Smrg	/* a space character seems to work as a cursor (dots are not needed) */
727d522f475Smrg	c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy);
728d522f475Smrg	XFreeFont(dpy, fn);
729d522f475Smrg    } else {
730d4fba8b9Smrg	c = None;
731d522f475Smrg    }
732d522f475Smrg    TRACE(("XCreateGlyphCursor ->%#lx\n", c));
733d4fba8b9Smrg    return c;
734d522f475Smrg}
735d522f475Smrg
736fa3f02f3Smrg/*
737fa3f02f3Smrg * Xlib uses Xcursor to customize cursor coloring, which interferes with
738fa3f02f3Smrg * xterm's pointerColor resource.  Work around this by providing our own
739fa3f02f3Smrg * default theme.  Testing seems to show that we only have to provide this
740fa3f02f3Smrg * until the window is initialized.
741fa3f02f3Smrg */
742fa3f02f3Smrgvoid
743037a25ddSmrginit_colored_cursor(Display *dpy)
744fa3f02f3Smrg{
745fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
74694644356Smrg    static const char theme[] = "index.theme";
74794644356Smrg    static const char pattern[] = "xtermXXXXXX";
748fa3f02f3Smrg    char *env = getenv("XCURSOR_THEME");
749fa3f02f3Smrg
750fa3f02f3Smrg    xterm_cursor_theme = 0;
751037a25ddSmrg    /*
752037a25ddSmrg     * The environment variable overrides a (possible) resource Xcursor.theme
753037a25ddSmrg     */
754fa3f02f3Smrg    if (IsEmpty(env)) {
755037a25ddSmrg	env = XGetDefault(dpy, "Xcursor", "theme");
756037a25ddSmrg    }
757037a25ddSmrg    /*
758037a25ddSmrg     * If neither found, provide our own default theme.
759037a25ddSmrg     */
760037a25ddSmrg    if (IsEmpty(env)) {
761037a25ddSmrg	const char *tmp_dir;
762037a25ddSmrg	char *filename;
763037a25ddSmrg	size_t needed;
764037a25ddSmrg
765fa3f02f3Smrg	if ((tmp_dir = getenv("TMPDIR")) == 0) {
766fa3f02f3Smrg	    tmp_dir = P_tmpdir;
767fa3f02f3Smrg	}
768fa3f02f3Smrg	needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern);
769fa3f02f3Smrg	if ((filename = malloc(needed)) != 0) {
770fa3f02f3Smrg	    sprintf(filename, "%s/%s", tmp_dir, pattern);
771fa3f02f3Smrg
772fa3f02f3Smrg#ifdef HAVE_MKDTEMP
773fa3f02f3Smrg	    xterm_cursor_theme = mkdtemp(filename);
774fa3f02f3Smrg#else
775fa3f02f3Smrg	    if (mktemp(filename) != 0
776fa3f02f3Smrg		&& mkdir(filename, 0700) == 0) {
777fa3f02f3Smrg		xterm_cursor_theme = filename;
778fa3f02f3Smrg	    }
779fa3f02f3Smrg#endif
780d4fba8b9Smrg	    if (xterm_cursor_theme != filename)
781d4fba8b9Smrg		free(filename);
782fa3f02f3Smrg	    /*
783fa3f02f3Smrg	     * Actually, Xcursor does what _we_ want just by steering its
784fa3f02f3Smrg	     * search path away from home.  We are setting up the complete
785fa3f02f3Smrg	     * theme just in case the library ever acquires a maintainer.
786fa3f02f3Smrg	     */
787fa3f02f3Smrg	    if (xterm_cursor_theme != 0) {
788fa3f02f3Smrg		char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme);
789037a25ddSmrg		FILE *fp;
790037a25ddSmrg
791fa3f02f3Smrg		strcat(leaf, "/");
792fa3f02f3Smrg		strcat(leaf, theme);
793fa3f02f3Smrg		if ((fp = fopen(xterm_cursor_theme, "w")) != 0) {
794fa3f02f3Smrg		    fprintf(fp, "[Icon Theme]\n");
795fa3f02f3Smrg		    fclose(fp);
796fa3f02f3Smrg		    *leaf = '\0';
797fa3f02f3Smrg		    xtermSetenv("XCURSOR_PATH", xterm_cursor_theme);
798fa3f02f3Smrg		    *leaf = '/';
799fa3f02f3Smrg		}
800fa3f02f3Smrg		atexit(cleanup_colored_cursor);
801fa3f02f3Smrg	    }
802fa3f02f3Smrg	}
803fa3f02f3Smrg    }
804037a25ddSmrg#else
805037a25ddSmrg    (void) dpy;
806fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
807fa3f02f3Smrg}
808fa3f02f3Smrg
809fa3f02f3Smrg/*
810fa3f02f3Smrg * Once done, discard the file and directory holding it.
811fa3f02f3Smrg */
812fa3f02f3Smrgvoid
813fa3f02f3Smrgcleanup_colored_cursor(void)
814fa3f02f3Smrg{
815fa3f02f3Smrg#ifdef HAVE_LIB_XCURSOR
816fa3f02f3Smrg    if (xterm_cursor_theme != 0) {
817fa3f02f3Smrg	char *my_path = getenv("XCURSOR_PATH");
818fa3f02f3Smrg	struct stat sb;
819fa3f02f3Smrg	if (!IsEmpty(my_path)
820fa3f02f3Smrg	    && stat(my_path, &sb) == 0
821fa3f02f3Smrg	    && (sb.st_mode & S_IFMT) == S_IFDIR) {
822fa3f02f3Smrg	    unlink(xterm_cursor_theme);
823fa3f02f3Smrg	    rmdir(my_path);
824fa3f02f3Smrg	    free(xterm_cursor_theme);
825fa3f02f3Smrg	    xterm_cursor_theme = 0;
826fa3f02f3Smrg	}
827fa3f02f3Smrg    }
828fa3f02f3Smrg#endif /* HAVE_LIB_XCURSOR */
829fa3f02f3Smrg}
830fa3f02f3Smrg
831d522f475SmrgCursor
832d4fba8b9Smrgmake_colored_cursor(unsigned c_index,		/* index into font */
833d522f475Smrg		    unsigned long fg,	/* pixel value */
834d522f475Smrg		    unsigned long bg)	/* pixel value */
835d522f475Smrg{
836d522f475Smrg    TScreen *screen = TScreenOf(term);
837d4fba8b9Smrg    Cursor c = None;
838d522f475Smrg    Display *dpy = screen->display;
839d522f475Smrg
840d4fba8b9Smrg    TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name));
841d4fba8b9Smrg    if (!IsEmpty(screen->cursor_font_name)) {
842d4fba8b9Smrg	static XTermFonts myFont;
843d4fba8b9Smrg
844d4fba8b9Smrg	/* adapted from XCreateFontCursor(), which hardcodes the font name */
845d4fba8b9Smrg	TRACE(("loading cursor from alternate cursor font\n"));
846d4fba8b9Smrg	if ((myFont.fs = XLoadQueryFont(dpy, screen->cursor_font_name)) != 0) {
847d4fba8b9Smrg	    if (!xtermMissingChar(c_index, &myFont)
848d4fba8b9Smrg		&& !xtermMissingChar(c_index + 1, &myFont)) {
849d4fba8b9Smrg#define DATA(c) { 0UL, c, c, c, 0, 0 }
850d4fba8b9Smrg		static XColor foreground = DATA(0);
851d4fba8b9Smrg		static XColor background = DATA(65535);
852d4fba8b9Smrg#undef DATA
853d4fba8b9Smrg
854d4fba8b9Smrg		/*
855d4fba8b9Smrg		 * Cursor fonts follow each shape glyph with a mask glyph; so
856d4fba8b9Smrg		 * that character position 0 contains a shape, 1 the mask for
857d4fba8b9Smrg		 * 0, 2 a shape, 3 a mask for 2, etc.  <X11/cursorfont.h>
858d4fba8b9Smrg		 * contains defined names for each shape.
859d4fba8b9Smrg		 */
860d4fba8b9Smrg		c = XCreateGlyphCursor(dpy,
861d4fba8b9Smrg				       myFont.fs->fid,	/* source_font */
862d4fba8b9Smrg				       myFont.fs->fid,	/* mask_font */
863d4fba8b9Smrg				       c_index + 0,	/* source_char */
864d4fba8b9Smrg				       c_index + 1,	/* mask_char */
865d4fba8b9Smrg				       &foreground,
866d4fba8b9Smrg				       &background);
867d4fba8b9Smrg	    }
868d4fba8b9Smrg	    XFreeFont(dpy, myFont.fs);
869d4fba8b9Smrg	}
870d4fba8b9Smrg	if (c == None) {
871d4fba8b9Smrg	    xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n",
872d4fba8b9Smrg			 c_index, screen->cursor_font_name);
873d4fba8b9Smrg	}
874d4fba8b9Smrg    }
875d4fba8b9Smrg    if (c == None)
876d4fba8b9Smrg	c = XCreateFontCursor(dpy, c_index);
877d4fba8b9Smrg
878d522f475Smrg    if (c != None) {
879d522f475Smrg	recolor_cursor(screen, c, fg, bg);
880d522f475Smrg    }
881d4fba8b9Smrg    return c;
882d522f475Smrg}
883d522f475Smrg
884d522f475Smrg/* ARGSUSED */
885d522f475Smrgvoid
886d522f475SmrgHandleKeyPressed(Widget w GCC_UNUSED,
8879a64e1c5Smrg		 XEvent *event,
888fa3f02f3Smrg		 String *params GCC_UNUSED,
889d522f475Smrg		 Cardinal *nparams GCC_UNUSED)
890d522f475Smrg{
891cd3331d0Smrg    TRACE(("Handle insert-seven-bit for %p\n", (void *) w));
892cd3331d0Smrg    Input(term, &event->xkey, False);
893d522f475Smrg}
894d522f475Smrg
895d522f475Smrg/* ARGSUSED */
896d522f475Smrgvoid
897d522f475SmrgHandleEightBitKeyPressed(Widget w GCC_UNUSED,
8989a64e1c5Smrg			 XEvent *event,
899fa3f02f3Smrg			 String *params GCC_UNUSED,
900d522f475Smrg			 Cardinal *nparams GCC_UNUSED)
901d522f475Smrg{
902cd3331d0Smrg    TRACE(("Handle insert-eight-bit for %p\n", (void *) w));
903cd3331d0Smrg    Input(term, &event->xkey, True);
904d522f475Smrg}
905d522f475Smrg
906d522f475Smrg/* ARGSUSED */
907d522f475Smrgvoid
908d522f475SmrgHandleStringEvent(Widget w GCC_UNUSED,
9099a64e1c5Smrg		  XEvent *event GCC_UNUSED,
910fa3f02f3Smrg		  String *params,
911d522f475Smrg		  Cardinal *nparams)
912d522f475Smrg{
913d522f475Smrg
914d522f475Smrg    if (*nparams != 1)
915d522f475Smrg	return;
916d522f475Smrg
917d522f475Smrg    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
9180d92cbfdSchristos	const char *abcdef = "ABCDEF";
9190d92cbfdSchristos	const char *xxxxxx;
920cd3331d0Smrg	Char c;
921cd3331d0Smrg	UString p;
9220d92cbfdSchristos	unsigned value = 0;
9230d92cbfdSchristos
924cd3331d0Smrg	for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) !=
9250d92cbfdSchristos	     '\0'; p++) {
9260d92cbfdSchristos	    value *= 16;
927d522f475Smrg	    if (c >= '0' && c <= '9')
9280d92cbfdSchristos		value += (unsigned) (c - '0');
929fa3f02f3Smrg	    else if ((xxxxxx = (strchr) (abcdef, c)) != 0)
9300d92cbfdSchristos		value += (unsigned) (xxxxxx - abcdef) + 10;
931d522f475Smrg	    else
932d522f475Smrg		break;
933d522f475Smrg	}
9340d92cbfdSchristos	if (c == '\0') {
9350d92cbfdSchristos	    Char hexval[2];
9360d92cbfdSchristos	    hexval[0] = (Char) value;
9370d92cbfdSchristos	    hexval[1] = 0;
938b7c89284Ssnj	    StringInput(term, hexval, (size_t) 1);
9390d92cbfdSchristos	}
940d522f475Smrg    } else {
941cd3331d0Smrg	StringInput(term, (const Char *) *params, strlen(*params));
942d522f475Smrg    }
943d522f475Smrg}
944d522f475Smrg
945d522f475Smrg#if OPT_EXEC_XTERM
946d522f475Smrg
947d522f475Smrg#ifndef PROCFS_ROOT
948d522f475Smrg#define PROCFS_ROOT "/proc"
949d522f475Smrg#endif
950d522f475Smrg
951037a25ddSmrg/*
952037a25ddSmrg * Determine the current working directory of the child so that we can
953037a25ddSmrg * spawn a new terminal in the same directory.
954037a25ddSmrg *
955037a25ddSmrg * If we cannot get the CWD of the child, just use our own.
956037a25ddSmrg */
957037a25ddSmrgchar *
958037a25ddSmrgProcGetCWD(pid_t pid)
959037a25ddSmrg{
960037a25ddSmrg    char *child_cwd = NULL;
961037a25ddSmrg
962037a25ddSmrg    if (pid) {
963037a25ddSmrg	char child_cwd_link[sizeof(PROCFS_ROOT) + 80];
964037a25ddSmrg	sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid);
965037a25ddSmrg	child_cwd = Readlink(child_cwd_link);
966037a25ddSmrg    }
967037a25ddSmrg    return child_cwd;
968037a25ddSmrg}
969037a25ddSmrg
970d522f475Smrg/* ARGSUSED */
971d522f475Smrgvoid
972d522f475SmrgHandleSpawnTerminal(Widget w GCC_UNUSED,
9739a64e1c5Smrg		    XEvent *event GCC_UNUSED,
974fa3f02f3Smrg		    String *params,
975d522f475Smrg		    Cardinal *nparams)
976d522f475Smrg{
977cd3331d0Smrg    TScreen *screen = TScreenOf(term);
978d522f475Smrg    char *child_cwd = NULL;
979d522f475Smrg    char *child_exe;
980d522f475Smrg    pid_t pid;
981d522f475Smrg
982d522f475Smrg    /*
983d522f475Smrg     * Try to find the actual program which is running in the child process.
984d522f475Smrg     * This works for Linux.  If we cannot find the program, fall back to the
985d522f475Smrg     * xterm program (which is usually adequate).  Give up if we are given only
986d522f475Smrg     * a relative path to xterm, since that would not always match $PATH.
987d522f475Smrg     */
988d522f475Smrg    child_exe = Readlink(PROCFS_ROOT "/self/exe");
989d522f475Smrg    if (!child_exe) {
990cd3331d0Smrg	if (strncmp(ProgramName, "./", (size_t) 2)
991cd3331d0Smrg	    && strncmp(ProgramName, "../", (size_t) 3)) {
992d522f475Smrg	    child_exe = xtermFindShell(ProgramName, True);
993d522f475Smrg	} else {
9943367019cSmrg	    xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName);
995d522f475Smrg	}
996d522f475Smrg	if (child_exe == 0)
997d522f475Smrg	    return;
998d522f475Smrg    }
999d522f475Smrg
1000037a25ddSmrg    child_cwd = ProcGetCWD(screen->pid);
1001d522f475Smrg
1002d522f475Smrg    /* The reaper will take care of cleaning up the child */
1003d522f475Smrg    pid = fork();
1004d522f475Smrg    if (pid == -1) {
10053367019cSmrg	xtermWarning("Could not fork: %s\n", SysErrorMsg(errno));
1006d522f475Smrg    } else if (!pid) {
1007d522f475Smrg	/* We are the child */
1008d522f475Smrg	if (child_cwd) {
1009cd3331d0Smrg	    IGNORE_RC(chdir(child_cwd));	/* We don't care if this fails */
1010d522f475Smrg	}
1011d522f475Smrg
1012d522f475Smrg	if (setuid(screen->uid) == -1
1013d522f475Smrg	    || setgid(screen->gid) == -1) {
10143367019cSmrg	    xtermWarning("Cannot reset uid/gid\n");
1015d522f475Smrg	} else {
10160d92cbfdSchristos	    unsigned myargc = *nparams + 1;
1017d522f475Smrg	    char **myargv = TypeMallocN(char *, myargc + 1);
1018d522f475Smrg
101994644356Smrg	    if (myargv != 0) {
102094644356Smrg		unsigned n = 0;
1021d522f475Smrg
102294644356Smrg		myargv[n++] = child_exe;
1023d522f475Smrg
102494644356Smrg		while (n < myargc) {
102594644356Smrg		    myargv[n++] = (char *) *params++;
102694644356Smrg		}
102794644356Smrg
102894644356Smrg		myargv[n] = 0;
102994644356Smrg		execv(child_exe, myargv);
103094644356Smrg	    }
1031d522f475Smrg
1032d522f475Smrg	    /* If we get here, we've failed */
10333367019cSmrg	    xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno));
1034d522f475Smrg	}
1035d522f475Smrg	_exit(0);
1036d522f475Smrg    }
10373367019cSmrg
10383367019cSmrg    /* We are the parent; clean up */
1039d4fba8b9Smrg    free(child_cwd);
10403367019cSmrg    free(child_exe);
1041d522f475Smrg}
1042d522f475Smrg#endif /* OPT_EXEC_XTERM */
1043d522f475Smrg
1044d522f475Smrg/*
1045d522f475Smrg * Rather than sending characters to the host, put them directly into our
1046d522f475Smrg * input queue.  That lets a user have access to any of the control sequences
1047d522f475Smrg * for a key binding.  This is the equivalent of local function key support.
1048d522f475Smrg *
1049d522f475Smrg * NOTE:  This code does not support the hexadecimal kludge used in
1050d522f475Smrg * HandleStringEvent because it prevents us from sending an arbitrary string
1051d522f475Smrg * (but it appears in a lot of examples - so we are stuck with it).  The
1052d522f475Smrg * standard string converter does recognize "\" for newline ("\n") and for
1053d522f475Smrg * octal constants (e.g., "\007" for BEL).  So we assume the user can make do
1054d522f475Smrg * without a specialized converter.  (Don't try to use \000, though).
1055d522f475Smrg */
1056d522f475Smrg/* ARGSUSED */
1057d522f475Smrgvoid
1058d522f475SmrgHandleInterpret(Widget w GCC_UNUSED,
10599a64e1c5Smrg		XEvent *event GCC_UNUSED,
1060fa3f02f3Smrg		String *params,
1061d522f475Smrg		Cardinal *param_count)
1062d522f475Smrg{
1063d522f475Smrg    if (*param_count == 1) {
1064cd3331d0Smrg	const char *value = params[0];
1065b7c89284Ssnj	int need = (int) strlen(value);
1066cd3331d0Smrg	int used = (int) (VTbuffer->next - VTbuffer->buffer);
1067cd3331d0Smrg	int have = (int) (VTbuffer->last - VTbuffer->buffer);
1068d522f475Smrg
1069d522f475Smrg	if (have - used + need < BUF_SIZE) {
1070d522f475Smrg
1071cd3331d0Smrg	    fillPtyData(term, VTbuffer, value, (int) strlen(value));
1072d522f475Smrg
1073d522f475Smrg	    TRACE(("Interpret %s\n", value));
1074d522f475Smrg	    VTbuffer->update++;
1075d522f475Smrg	}
1076d522f475Smrg    }
1077d522f475Smrg}
1078d522f475Smrg
1079d522f475Smrg/*ARGSUSED*/
1080d522f475Smrgvoid
1081d522f475SmrgHandleEnterWindow(Widget w GCC_UNUSED,
1082d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10839a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1084fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1085d522f475Smrg{
1086d522f475Smrg    /* NOP since we handled it above */
1087d522f475Smrg    TRACE(("HandleEnterWindow ignored\n"));
1088cd3331d0Smrg    TRACE_FOCUS(w, event);
1089d522f475Smrg}
1090d522f475Smrg
1091d522f475Smrg/*ARGSUSED*/
1092d522f475Smrgvoid
1093d522f475SmrgHandleLeaveWindow(Widget w GCC_UNUSED,
1094d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
10959a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1096fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1097d522f475Smrg{
1098d522f475Smrg    /* NOP since we handled it above */
1099d522f475Smrg    TRACE(("HandleLeaveWindow ignored\n"));
1100cd3331d0Smrg    TRACE_FOCUS(w, event);
1101d522f475Smrg}
1102d522f475Smrg
1103d522f475Smrg/*ARGSUSED*/
1104d522f475Smrgvoid
1105d522f475SmrgHandleFocusChange(Widget w GCC_UNUSED,
1106d522f475Smrg		  XtPointer eventdata GCC_UNUSED,
11079a64e1c5Smrg		  XEvent *ev,
1108fa3f02f3Smrg		  Boolean *cont GCC_UNUSED)
1109d522f475Smrg{
1110d522f475Smrg    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
1111d522f475Smrg    XtermWidget xw = term;
1112d522f475Smrg    TScreen *screen = TScreenOf(xw);
1113d522f475Smrg
11143367019cSmrg    TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n",
1115d522f475Smrg	   visibleEventType(event->type),
11163367019cSmrg	   visibleNotifyMode(event->mode),
11173367019cSmrg	   visibleNotifyDetail(event->detail)));
1118cd3331d0Smrg    TRACE_FOCUS(xw, event);
1119d522f475Smrg
1120d522f475Smrg    if (screen->quiet_grab
1121d522f475Smrg	&& (event->mode == NotifyGrab || event->mode == NotifyUngrab)) {
1122c219fbebSmrg	/* EMPTY */ ;
1123d522f475Smrg    } else if (event->type == FocusIn) {
112494644356Smrg	if (event->detail != NotifyPointer) {
112594644356Smrg	    setXUrgency(xw, False);
112694644356Smrg	}
1127d522f475Smrg
1128d522f475Smrg	/*
1129d522f475Smrg	 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in
1130d522f475Smrg	 * one of our windows.  Use this to reset a case where one xterm is
1131d522f475Smrg	 * partly obscuring another, and X gets (us) confused about whether the
1132d522f475Smrg	 * pointer was in the window.  In particular, this can happen if the
1133d522f475Smrg	 * user is resizing the obscuring window, causing some events to not be
1134d522f475Smrg	 * delivered to the obscured window.
1135d522f475Smrg	 */
1136d522f475Smrg	if (event->detail == NotifyNonlinear
1137d522f475Smrg	    && (screen->select & INWINDOW) != 0) {
11383367019cSmrg	    unselectwindow(xw, INWINDOW);
1139d522f475Smrg	}
11403367019cSmrg	selectwindow(xw,
1141d522f475Smrg		     ((event->detail == NotifyPointer)
1142d522f475Smrg		      ? INWINDOW
1143d522f475Smrg		      : FOCUS));
1144d522f475Smrg	SendFocusButton(xw, event);
1145d522f475Smrg    } else {
1146d522f475Smrg#if OPT_FOCUS_EVENT
1147d522f475Smrg	if (event->type == FocusOut) {
1148d522f475Smrg	    SendFocusButton(xw, event);
1149d522f475Smrg	}
1150d522f475Smrg#endif
1151d522f475Smrg	/*
1152d522f475Smrg	 * XGrabKeyboard() will generate NotifyGrab event that we want to
1153d522f475Smrg	 * ignore.
1154d522f475Smrg	 */
1155d522f475Smrg	if (event->mode != NotifyGrab) {
11563367019cSmrg	    unselectwindow(xw,
1157d522f475Smrg			   ((event->detail == NotifyPointer)
1158d522f475Smrg			    ? INWINDOW
1159d522f475Smrg			    : FOCUS));
1160d522f475Smrg	}
1161d522f475Smrg	if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
1162cd3331d0Smrg	    Bell(xw, XkbBI_Info, 100);
1163d522f475Smrg	    ReverseVideo(xw);
1164d522f475Smrg	    screen->grabbedKbd = False;
1165d522f475Smrg	    update_securekbd();
1166d522f475Smrg	}
1167d522f475Smrg    }
1168d522f475Smrg}
1169d522f475Smrg
1170d522f475Smrgstatic long lastBellTime;	/* in milliseconds */
1171d522f475Smrg
1172b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1173b7c89284Ssnjstatic Atom
1174b7c89284SsnjAtomBell(XtermWidget xw, int which)
1175b7c89284Ssnj{
1176b7c89284Ssnj#define DATA(name) { XkbBI_##name, XkbBN_##name }
1177b7c89284Ssnj    static struct {
1178b7c89284Ssnj	int value;
1179b7c89284Ssnj	const char *name;
1180b7c89284Ssnj    } table[] = {
1181b7c89284Ssnj	DATA(Info),
1182b7c89284Ssnj	    DATA(MarginBell),
1183b7c89284Ssnj	    DATA(MinorError),
1184b7c89284Ssnj	    DATA(TerminalBell)
1185b7c89284Ssnj    };
1186d4fba8b9Smrg#undef DATA
1187b7c89284Ssnj    Cardinal n;
1188b7c89284Ssnj    Atom result = None;
1189b7c89284Ssnj
1190b7c89284Ssnj    for (n = 0; n < XtNumber(table); ++n) {
1191b7c89284Ssnj	if (table[n].value == which) {
1192cd3331d0Smrg	    result = XInternAtom(XtDisplay(xw), table[n].name, False);
1193b7c89284Ssnj	    break;
1194b7c89284Ssnj	}
1195b7c89284Ssnj    }
1196b7c89284Ssnj    return result;
1197b7c89284Ssnj}
1198b7c89284Ssnj#endif
1199b7c89284Ssnj
1200d522f475Smrgvoid
1201b7c89284SsnjxtermBell(XtermWidget xw, int which, int percent)
1202d522f475Smrg{
1203b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1204b7c89284Ssnj#if defined(HAVE_XKB_BELL_EXT)
1205b7c89284Ssnj    Atom tony = AtomBell(xw, which);
1206cd3331d0Smrg#endif
1207cd3331d0Smrg
1208cd3331d0Smrg    switch (which) {
1209cd3331d0Smrg    case XkbBI_Info:
1210cd3331d0Smrg    case XkbBI_MinorError:
1211cd3331d0Smrg    case XkbBI_MajorError:
1212cd3331d0Smrg    case XkbBI_TerminalBell:
1213cd3331d0Smrg	switch (screen->warningVolume) {
1214cd3331d0Smrg	case bvOff:
1215cd3331d0Smrg	    percent = -100;
1216cd3331d0Smrg	    break;
1217cd3331d0Smrg	case bvLow:
1218cd3331d0Smrg	    break;
1219cd3331d0Smrg	case bvHigh:
1220cd3331d0Smrg	    percent = 100;
1221cd3331d0Smrg	    break;
1222cd3331d0Smrg	}
1223cd3331d0Smrg	break;
1224cd3331d0Smrg    case XkbBI_MarginBell:
1225cd3331d0Smrg	switch (screen->marginVolume) {
1226cd3331d0Smrg	case bvOff:
1227cd3331d0Smrg	    percent = -100;
1228cd3331d0Smrg	    break;
1229cd3331d0Smrg	case bvLow:
1230cd3331d0Smrg	    break;
1231cd3331d0Smrg	case bvHigh:
1232cd3331d0Smrg	    percent = 100;
1233cd3331d0Smrg	    break;
1234cd3331d0Smrg	}
1235cd3331d0Smrg	break;
1236cd3331d0Smrg    default:
1237cd3331d0Smrg	break;
1238cd3331d0Smrg    }
1239cd3331d0Smrg
1240cd3331d0Smrg#if defined(HAVE_XKB_BELL_EXT)
1241b7c89284Ssnj    if (tony != None) {
1242c219fbebSmrg	XkbBell(screen->display, VShellWindow(xw), percent, tony);
1243b7c89284Ssnj    } else
1244b7c89284Ssnj#endif
1245b7c89284Ssnj	XBell(screen->display, percent);
1246b7c89284Ssnj}
1247b7c89284Ssnj
1248b7c89284Ssnjvoid
1249cd3331d0SmrgBell(XtermWidget xw, int which, int percent)
1250b7c89284Ssnj{
1251b7c89284Ssnj    TScreen *screen = TScreenOf(xw);
1252d522f475Smrg    struct timeval curtime;
1253d522f475Smrg
1254b7c89284Ssnj    TRACE(("BELL %d %d%%\n", which, percent));
1255b7c89284Ssnj    if (!XtIsRealized((Widget) xw)) {
1256d522f475Smrg	return;
1257d522f475Smrg    }
1258d522f475Smrg
1259c219fbebSmrg    setXUrgency(xw, True);
1260d522f475Smrg
1261d522f475Smrg    /* has enough time gone by that we are allowed to ring
1262d522f475Smrg       the bell again? */
1263d522f475Smrg    if (screen->bellSuppressTime) {
1264037a25ddSmrg	long now_msecs;
1265037a25ddSmrg
1266d522f475Smrg	if (screen->bellInProgress) {
1267d4fba8b9Smrg	    do_xevents(xw);
1268d522f475Smrg	    if (screen->bellInProgress) {	/* even after new events? */
1269d522f475Smrg		return;
1270d522f475Smrg	    }
1271d522f475Smrg	}
1272d522f475Smrg	X_GETTIMEOFDAY(&curtime);
1273d522f475Smrg	now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000;
1274d522f475Smrg	if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 &&
1275d522f475Smrg	    now_msecs - lastBellTime < screen->bellSuppressTime) {
1276d522f475Smrg	    return;
1277d522f475Smrg	}
1278d522f475Smrg	lastBellTime = now_msecs;
1279d522f475Smrg    }
1280d522f475Smrg
1281d522f475Smrg    if (screen->visualbell) {
1282d522f475Smrg	VisualBell();
1283d522f475Smrg    } else {
1284b7c89284Ssnj	xtermBell(xw, which, percent);
1285d522f475Smrg    }
1286d522f475Smrg
1287d522f475Smrg    if (screen->poponbell)
1288c219fbebSmrg	XRaiseWindow(screen->display, VShellWindow(xw));
1289d522f475Smrg
1290d522f475Smrg    if (screen->bellSuppressTime) {
1291d522f475Smrg	/* now we change a property and wait for the notify event to come
1292d522f475Smrg	   back.  If the server is suspending operations while the bell
1293d522f475Smrg	   is being emitted (problematic for audio bell), this lets us
1294d522f475Smrg	   know when the previous bell has finished */
1295d522f475Smrg	Widget w = CURRENT_EMU();
1296d522f475Smrg	XChangeProperty(XtDisplay(w), XtWindow(w),
1297d522f475Smrg			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
1298d522f475Smrg	screen->bellInProgress = True;
1299d522f475Smrg    }
1300d522f475Smrg}
1301d522f475Smrg
1302d522f475Smrgstatic void
1303fa3f02f3SmrgflashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height)
1304d522f475Smrg{
13053367019cSmrg    int y = 0;
13063367019cSmrg    int x = 0;
13073367019cSmrg
13083367019cSmrg    if (screen->flash_line) {
13093367019cSmrg	y = CursorY(screen, screen->cur_row);
13103367019cSmrg	height = (unsigned) FontHeight(screen);
13113367019cSmrg    }
13123367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1313d522f475Smrg    XFlush(screen->display);
1314d522f475Smrg    Sleep(VB_DELAY);
13153367019cSmrg    XFillRectangle(screen->display, window, visualGC, x, y, width, height);
1316d522f475Smrg}
1317d522f475Smrg
1318d522f475Smrgvoid
1319d522f475SmrgVisualBell(void)
1320d522f475Smrg{
1321d4fba8b9Smrg    XtermWidget xw = term;
1322d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1323d522f475Smrg
1324d522f475Smrg    if (VB_DELAY > 0) {
1325d522f475Smrg	Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^
1326d522f475Smrg			  T_COLOR(screen, TEXT_BG));
1327d522f475Smrg	XGCValues gcval;
1328d522f475Smrg	GC visualGC;
1329d522f475Smrg
1330d522f475Smrg	gcval.function = GXxor;
1331d522f475Smrg	gcval.foreground = xorPixel;
1332d4fba8b9Smrg	visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval);
1333d522f475Smrg#if OPT_TEK4014
1334d4fba8b9Smrg	if (TEK4014_ACTIVE(xw)) {
1335cd3331d0Smrg	    TekScreen *tekscr = TekScreenOf(tekWidget);
1336d522f475Smrg	    flashWindow(screen, TWindow(tekscr), visualGC,
1337d522f475Smrg			TFullWidth(tekscr),
1338d522f475Smrg			TFullHeight(tekscr));
1339d522f475Smrg	} else
1340d522f475Smrg#endif
1341d522f475Smrg	{
1342d522f475Smrg	    flashWindow(screen, VWindow(screen), visualGC,
1343d522f475Smrg			FullWidth(screen),
1344d522f475Smrg			FullHeight(screen));
1345d522f475Smrg	}
1346d4fba8b9Smrg	XtReleaseGC((Widget) xw, visualGC);
1347d522f475Smrg    }
1348d522f475Smrg}
1349d522f475Smrg
1350d522f475Smrg/* ARGSUSED */
1351d522f475Smrgvoid
1352d522f475SmrgHandleBellPropertyChange(Widget w GCC_UNUSED,
1353d522f475Smrg			 XtPointer data GCC_UNUSED,
13549a64e1c5Smrg			 XEvent *ev,
1355fa3f02f3Smrg			 Boolean *more GCC_UNUSED)
1356d522f475Smrg{
1357d522f475Smrg    TScreen *screen = TScreenOf(term);
1358d522f475Smrg
1359d522f475Smrg    if (ev->xproperty.atom == XA_NOTICE) {
1360d522f475Smrg	screen->bellInProgress = False;
1361d522f475Smrg    }
1362d522f475Smrg}
1363d522f475Smrg
13643367019cSmrgvoid
1365d4fba8b9SmrgxtermWarning(const char *fmt, ...)
13663367019cSmrg{
13673367019cSmrg    int save_err = errno;
13683367019cSmrg    va_list ap;
13693367019cSmrg
1370dfb07bc7Smrg    fflush(stdout);
1371d4fba8b9Smrg
1372d4fba8b9Smrg#if OPT_TRACE
1373d4fba8b9Smrg    va_start(ap, fmt);
1374d4fba8b9Smrg    Trace("xtermWarning: ");
1375d4fba8b9Smrg    TraceVA(fmt, ap);
1376d4fba8b9Smrg    va_end(ap);
1377d4fba8b9Smrg#endif
1378d4fba8b9Smrg
13793367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
13803367019cSmrg    va_start(ap, fmt);
13813367019cSmrg    vfprintf(stderr, fmt, ap);
13823367019cSmrg    (void) fflush(stderr);
13833367019cSmrg
13843367019cSmrg    va_end(ap);
13853367019cSmrg    errno = save_err;
13863367019cSmrg}
13873367019cSmrg
13883367019cSmrgvoid
1389d4fba8b9SmrgxtermPerror(const char *fmt, ...)
13903367019cSmrg{
13913367019cSmrg    int save_err = errno;
1392d4fba8b9Smrg    const char *msg = strerror(errno);
13933367019cSmrg    va_list ap;
13943367019cSmrg
1395dfb07bc7Smrg    fflush(stdout);
1396d4fba8b9Smrg
1397d4fba8b9Smrg#if OPT_TRACE
1398d4fba8b9Smrg    va_start(ap, fmt);
1399d4fba8b9Smrg    Trace("xtermPerror: ");
1400d4fba8b9Smrg    TraceVA(fmt, ap);
1401d4fba8b9Smrg    va_end(ap);
1402d4fba8b9Smrg#endif
1403d4fba8b9Smrg
14043367019cSmrg    fprintf(stderr, "%s: ", ProgramName);
14053367019cSmrg    va_start(ap, fmt);
14063367019cSmrg    vfprintf(stderr, fmt, ap);
14073367019cSmrg    fprintf(stderr, ": %s\n", msg);
14083367019cSmrg    (void) fflush(stderr);
14093367019cSmrg
14103367019cSmrg    va_end(ap);
14113367019cSmrg    errno = save_err;
14123367019cSmrg}
14133367019cSmrg
1414d522f475SmrgWindow
1415c219fbebSmrgWMFrameWindow(XtermWidget xw)
1416d522f475Smrg{
1417d522f475Smrg    Window win_root, win_current, *children;
1418d522f475Smrg    Window win_parent = 0;
1419d522f475Smrg    unsigned int nchildren;
1420d522f475Smrg
1421c219fbebSmrg    win_current = XtWindow(xw);
1422d522f475Smrg
1423d522f475Smrg    /* find the parent which is child of root */
1424d522f475Smrg    do {
1425d522f475Smrg	if (win_parent)
1426d522f475Smrg	    win_current = win_parent;
1427c219fbebSmrg	XQueryTree(TScreenOf(xw)->display,
1428d522f475Smrg		   win_current,
1429d522f475Smrg		   &win_root,
1430d522f475Smrg		   &win_parent,
1431d522f475Smrg		   &children,
1432d522f475Smrg		   &nchildren);
1433d522f475Smrg	XFree(children);
1434d522f475Smrg    } while (win_root != win_parent);
1435d522f475Smrg
1436d522f475Smrg    return win_current;
1437d522f475Smrg}
1438d522f475Smrg
1439d522f475Smrg#if OPT_DABBREV
1440d522f475Smrg/*
1441d522f475Smrg * The following code implements `dynamic abbreviation' expansion a la
1442d522f475Smrg * Emacs.  It looks in the preceding visible screen and its scrollback
1443d522f475Smrg * to find expansions of a typed word.  It compares consecutive
1444d522f475Smrg * expansions and ignores one of them if they are identical.
1445d522f475Smrg * (Tomasz J. Cholewo, t.cholewo@ieee.org)
1446d522f475Smrg */
1447d522f475Smrg
1448d522f475Smrg#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0')
1449d522f475Smrg
1450d522f475Smrgstatic int
1451fa3f02f3Smrgdabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld)
1452d522f475Smrg{
1453b7c89284Ssnj    int result = -1;
1454b7c89284Ssnj    int firstLine = -(screen->savedlines);
1455d522f475Smrg
1456b7c89284Ssnj    *ld = getLineData(screen, cell->row);
1457b7c89284Ssnj    while (cell->row >= firstLine) {
1458b7c89284Ssnj	if (--(cell->col) >= 0) {
1459b7c89284Ssnj	    result = (int) (*ld)->charData[cell->col];
1460b7c89284Ssnj	    break;
1461b7c89284Ssnj	}
1462b7c89284Ssnj	if (--(cell->row) < firstLine)
1463b7c89284Ssnj	    break;		/* ...there is no previous line */
1464b7c89284Ssnj	*ld = getLineData(screen, cell->row);
1465b7c89284Ssnj	cell->col = MaxCols(screen);
1466b7c89284Ssnj	if (!LineTstWrapped(*ld)) {
1467b7c89284Ssnj	    result = ' ';	/* treat lines as separate */
1468d522f475Smrg	    break;
1469b7c89284Ssnj	}
1470d522f475Smrg    }
1471b7c89284Ssnj    return result;
1472d522f475Smrg}
1473d522f475Smrg
1474d522f475Smrgstatic char *
14759a64e1c5Smrgdabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld)
1476d522f475Smrg{
14779a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1478d522f475Smrg    char *abword;
1479d522f475Smrg    int c;
14809a64e1c5Smrg    char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1);
1481b7c89284Ssnj    char *result = 0;
1482d522f475Smrg
1483b7c89284Ssnj    abword = ab_end;
1484d522f475Smrg    *abword = '\0';		/* end of string marker */
1485d522f475Smrg
1486b7c89284Ssnj    while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1487b7c89284Ssnj	   IS_WORD_CONSTITUENT(c)) {
14889a64e1c5Smrg	if (abword > xw->work.dabbrev_data)	/* store only the last chars */
1489b7c89284Ssnj	    *(--abword) = (char) c;
1490d522f475Smrg    }
1491d522f475Smrg
1492b7c89284Ssnj    if (c >= 0) {
1493b7c89284Ssnj	result = abword;
1494b7c89284Ssnj    } else if (abword != ab_end) {
1495b7c89284Ssnj	result = abword;
1496b7c89284Ssnj    }
1497b7c89284Ssnj
1498b7c89284Ssnj    if (result != 0) {
1499b7c89284Ssnj	while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 &&
1500b7c89284Ssnj	       !IS_WORD_CONSTITUENT(c)) {
1501b7c89284Ssnj	    ;			/* skip preceding spaces */
1502b7c89284Ssnj	}
1503b7c89284Ssnj	(cell->col)++;		/* can be | > screen->max_col| */
1504b7c89284Ssnj    }
1505b7c89284Ssnj    return result;
1506d522f475Smrg}
1507d522f475Smrg
1508d522f475Smrgstatic int
15099a64e1c5Smrgdabbrev_expand(XtermWidget xw)
1510d522f475Smrg{
15119a64e1c5Smrg    TScreen *screen = TScreenOf(xw);
1512d522f475Smrg    int pty = screen->respond;	/* file descriptor of pty */
1513d522f475Smrg
1514b7c89284Ssnj    static CELL cell;
1515d522f475Smrg    static char *dabbrev_hint = 0, *lastexpansion = 0;
1516d522f475Smrg    static unsigned int expansions;
1517d522f475Smrg
1518d522f475Smrg    char *expansion;
1519d522f475Smrg    size_t hint_len;
1520b7c89284Ssnj    int result = 0;
1521b7c89284Ssnj    LineData *ld;
1522d522f475Smrg
1523d522f475Smrg    if (!screen->dabbrev_working) {	/* initialize */
1524d522f475Smrg	expansions = 0;
1525b7c89284Ssnj	cell.col = screen->cur_col;
1526b7c89284Ssnj	cell.row = screen->cur_row;
1527b7c89284Ssnj
1528d4fba8b9Smrg	free(dabbrev_hint);
1529b7c89284Ssnj
15309a64e1c5Smrg	if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) {
1531b7c89284Ssnj
1532d4fba8b9Smrg	    free(lastexpansion);
1533b7c89284Ssnj
1534b7c89284Ssnj	    if ((lastexpansion = strdup(dabbrev_hint)) != 0) {
1535b7c89284Ssnj
1536b7c89284Ssnj		/* make own copy */
1537b7c89284Ssnj		if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) {
1538b7c89284Ssnj		    screen->dabbrev_working = True;
1539b7c89284Ssnj		    /* we are in the middle of dabbrev process */
1540b7c89284Ssnj		}
1541cd3331d0Smrg	    } else {
1542cd3331d0Smrg		return result;
1543b7c89284Ssnj	    }
1544cd3331d0Smrg	} else {
1545cd3331d0Smrg	    return result;
1546d522f475Smrg	}
1547b7c89284Ssnj	if (!screen->dabbrev_working) {
1548d4fba8b9Smrg	    free(lastexpansion);
1549d4fba8b9Smrg	    lastexpansion = 0;
1550b7c89284Ssnj	    return result;
1551b7c89284Ssnj	}
1552d522f475Smrg    }
1553d522f475Smrg
1554cd3331d0Smrg    if (dabbrev_hint == 0)
1555cd3331d0Smrg	return result;
1556cd3331d0Smrg
1557d522f475Smrg    hint_len = strlen(dabbrev_hint);
1558d522f475Smrg    for (;;) {
15599a64e1c5Smrg	if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) {
1560d522f475Smrg	    if (expansions >= 2) {
1561d522f475Smrg		expansions = 0;
1562b7c89284Ssnj		cell.col = screen->cur_col;
1563b7c89284Ssnj		cell.row = screen->cur_row;
1564d522f475Smrg		continue;
1565d522f475Smrg	    }
1566d522f475Smrg	    break;
1567d522f475Smrg	}
1568d522f475Smrg	if (!strncmp(dabbrev_hint, expansion, hint_len) &&	/* empty hint matches everything */
1569d522f475Smrg	    strlen(expansion) > hint_len &&	/* trivial expansion disallowed */
1570d522f475Smrg	    strcmp(expansion, lastexpansion))	/* different from previous */
1571d522f475Smrg	    break;
1572d522f475Smrg    }
1573d522f475Smrg
1574b7c89284Ssnj    if (expansion != 0) {
1575037a25ddSmrg	Char *copybuffer;
1576037a25ddSmrg	size_t del_cnt = strlen(lastexpansion) - hint_len;
1577037a25ddSmrg	size_t buf_cnt = del_cnt + strlen(expansion) - hint_len;
1578b7c89284Ssnj
1579b7c89284Ssnj	if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) {
1580b7c89284Ssnj	    /* delete previous expansion */
1581b7c89284Ssnj	    memset(copybuffer, screen->dabbrev_erase_char, del_cnt);
1582b7c89284Ssnj	    memmove(copybuffer + del_cnt,
1583b7c89284Ssnj		    expansion + hint_len,
1584b7c89284Ssnj		    strlen(expansion) - hint_len);
1585cd3331d0Smrg	    v_write(pty, copybuffer, (unsigned) buf_cnt);
1586b7c89284Ssnj	    /* v_write() just reset our flag */
1587b7c89284Ssnj	    screen->dabbrev_working = True;
1588b7c89284Ssnj	    free(copybuffer);
1589b7c89284Ssnj
1590b7c89284Ssnj	    free(lastexpansion);
1591b7c89284Ssnj
1592b7c89284Ssnj	    if ((lastexpansion = strdup(expansion)) != 0) {
1593b7c89284Ssnj		result = 1;
1594b7c89284Ssnj		expansions++;
1595b7c89284Ssnj	    }
1596b7c89284Ssnj	}
1597b7c89284Ssnj    }
1598b7c89284Ssnj
1599b7c89284Ssnj    return result;
1600d522f475Smrg}
1601d522f475Smrg
1602d522f475Smrg/*ARGSUSED*/
1603d522f475Smrgvoid
1604b7c89284SsnjHandleDabbrevExpand(Widget w,
16059a64e1c5Smrg		    XEvent *event GCC_UNUSED,
1606fa3f02f3Smrg		    String *params GCC_UNUSED,
1607d522f475Smrg		    Cardinal *nparams GCC_UNUSED)
1608d522f475Smrg{
1609b7c89284Ssnj    XtermWidget xw;
1610b7c89284Ssnj
1611cd3331d0Smrg    TRACE(("Handle dabbrev-expand for %p\n", (void *) w));
1612b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
16139a64e1c5Smrg	if (!dabbrev_expand(xw))
1614cd3331d0Smrg	    Bell(xw, XkbBI_TerminalBell, 0);
1615d522f475Smrg    }
1616d522f475Smrg}
1617d522f475Smrg#endif /* OPT_DABBREV */
1618d522f475Smrg
1619d4fba8b9Smrgvoid
1620d4fba8b9SmrgxtermDeiconify(XtermWidget xw)
1621d4fba8b9Smrg{
1622d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1623d4fba8b9Smrg    Display *dpy = screen->display;
1624d4fba8b9Smrg    Window target = VShellWindow(xw);
1625d4fba8b9Smrg    XEvent e;
1626d4fba8b9Smrg    Atom atom_state = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
1627d4fba8b9Smrg
1628d4fba8b9Smrg    if (xtermIsIconified(xw)) {
1629d4fba8b9Smrg	TRACE(("...de-iconify window %#lx\n", target));
1630d4fba8b9Smrg	XMapWindow(dpy, target);
1631d4fba8b9Smrg
1632d4fba8b9Smrg	memset(&e, 0, sizeof(e));
1633d4fba8b9Smrg	e.xclient.type = ClientMessage;
1634d4fba8b9Smrg	e.xclient.message_type = atom_state;
1635d4fba8b9Smrg	e.xclient.display = dpy;
1636d4fba8b9Smrg	e.xclient.window = target;
1637d4fba8b9Smrg	e.xclient.format = 32;
1638d4fba8b9Smrg	e.xclient.data.l[0] = 1;
1639d4fba8b9Smrg	e.xclient.data.l[1] = CurrentTime;
1640d4fba8b9Smrg
1641d4fba8b9Smrg	XSendEvent(dpy, DefaultRootWindow(dpy), False,
1642d4fba8b9Smrg		   SubstructureRedirectMask | SubstructureNotifyMask, &e);
1643d4fba8b9Smrg	xevents(xw);
1644d4fba8b9Smrg    }
1645d4fba8b9Smrg}
1646d4fba8b9Smrg
1647d4fba8b9Smrgvoid
1648d4fba8b9SmrgxtermIconify(XtermWidget xw)
1649d4fba8b9Smrg{
1650d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1651d4fba8b9Smrg    Window target = VShellWindow(xw);
1652d4fba8b9Smrg
1653d4fba8b9Smrg    if (!xtermIsIconified(xw)) {
1654d4fba8b9Smrg	TRACE(("...iconify window %#lx\n", target));
1655d4fba8b9Smrg	XIconifyWindow(screen->display,
1656d4fba8b9Smrg		       target,
1657d4fba8b9Smrg		       DefaultScreen(screen->display));
1658d4fba8b9Smrg	xevents(xw);
1659d4fba8b9Smrg    }
1660d4fba8b9Smrg}
1661d4fba8b9Smrg
1662d4fba8b9SmrgBoolean
1663d4fba8b9SmrgxtermIsIconified(XtermWidget xw)
1664d4fba8b9Smrg{
1665d4fba8b9Smrg    XWindowAttributes win_attrs;
1666d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1667d4fba8b9Smrg    Window target = VShellWindow(xw);
1668d4fba8b9Smrg    Display *dpy = screen->display;
1669d4fba8b9Smrg    Boolean result = False;
1670d4fba8b9Smrg
1671d4fba8b9Smrg    if (xtermGetWinAttrs(dpy, target, &win_attrs)) {
1672d4fba8b9Smrg	Atom actual_return_type;
1673d4fba8b9Smrg	int actual_format_return = 0;
1674d4fba8b9Smrg	unsigned long nitems_return = 0;
1675d4fba8b9Smrg	unsigned long bytes_after_return = 0;
1676d4fba8b9Smrg	unsigned char *prop_return = 0;
1677d4fba8b9Smrg	long long_length = 1024;
1678d4fba8b9Smrg	Atom requested_type = XA_ATOM;
1679d4fba8b9Smrg	Atom is_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
1680d4fba8b9Smrg	Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False);
1681d4fba8b9Smrg
1682d4fba8b9Smrg	/* this works with non-EWMH */
1683d4fba8b9Smrg	result = (win_attrs.map_state != IsViewable) ? True : False;
1684d4fba8b9Smrg
1685d4fba8b9Smrg	/* this is a convention used by some EWMH applications */
1686d4fba8b9Smrg	if (xtermGetWinProp(dpy,
1687d4fba8b9Smrg			    target,
1688d4fba8b9Smrg			    wm_state,
1689d4fba8b9Smrg			    0L,
1690d4fba8b9Smrg			    long_length,
1691d4fba8b9Smrg			    requested_type,
1692d4fba8b9Smrg			    &actual_return_type,
1693d4fba8b9Smrg			    &actual_format_return,
1694d4fba8b9Smrg			    &nitems_return,
1695d4fba8b9Smrg			    &bytes_after_return,
1696d4fba8b9Smrg			    &prop_return)
1697d4fba8b9Smrg	    && prop_return != 0
1698d4fba8b9Smrg	    && actual_return_type == requested_type
1699d4fba8b9Smrg	    && actual_format_return == 32) {
1700d4fba8b9Smrg	    unsigned long n;
1701d4fba8b9Smrg	    for (n = 0; n < nitems_return; ++n) {
1702d4fba8b9Smrg		unsigned long check = (((unsigned long *)
1703d4fba8b9Smrg					(void *) prop_return)[n]);
1704d4fba8b9Smrg		if (check == is_hidden) {
1705d4fba8b9Smrg		    result = True;
1706d4fba8b9Smrg		    break;
1707d4fba8b9Smrg		}
1708d4fba8b9Smrg	    }
1709d4fba8b9Smrg	}
1710d4fba8b9Smrg    }
1711d4fba8b9Smrg    TRACE(("...window %#lx is%s iconified\n",
1712d4fba8b9Smrg	   target,
1713d4fba8b9Smrg	   result ? "" : " not"));
1714d4fba8b9Smrg    return result;
1715d4fba8b9Smrg}
1716d4fba8b9Smrg
1717d522f475Smrg#if OPT_MAXIMIZE
1718d522f475Smrg/*ARGSUSED*/
1719d522f475Smrgvoid
1720b7c89284SsnjHandleDeIconify(Widget w,
17219a64e1c5Smrg		XEvent *event GCC_UNUSED,
1722fa3f02f3Smrg		String *params GCC_UNUSED,
1723d522f475Smrg		Cardinal *nparams GCC_UNUSED)
1724d522f475Smrg{
1725b7c89284Ssnj    XtermWidget xw;
1726b7c89284Ssnj
1727b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1728d4fba8b9Smrg	xtermDeiconify(xw);
1729d522f475Smrg    }
1730d522f475Smrg}
1731d522f475Smrg
1732d522f475Smrg/*ARGSUSED*/
1733d522f475Smrgvoid
1734b7c89284SsnjHandleIconify(Widget w,
17359a64e1c5Smrg	      XEvent *event GCC_UNUSED,
1736fa3f02f3Smrg	      String *params GCC_UNUSED,
1737d522f475Smrg	      Cardinal *nparams GCC_UNUSED)
1738d522f475Smrg{
1739b7c89284Ssnj    XtermWidget xw;
1740b7c89284Ssnj
1741b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1742d4fba8b9Smrg	xtermIconify(xw);
1743d522f475Smrg    }
1744d522f475Smrg}
1745d522f475Smrg
1746d522f475Smrgint
1747c219fbebSmrgQueryMaximize(XtermWidget xw, unsigned *width, unsigned *height)
1748d522f475Smrg{
1749c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1750d522f475Smrg    XSizeHints hints;
1751d522f475Smrg    long supp = 0;
1752d522f475Smrg    Window root_win;
1753d522f475Smrg    int root_x = -1;		/* saved co-ordinates */
1754d522f475Smrg    int root_y = -1;
1755d522f475Smrg    unsigned root_border;
1756d522f475Smrg    unsigned root_depth;
17573367019cSmrg    int code;
1758d522f475Smrg
1759d522f475Smrg    if (XGetGeometry(screen->display,
1760c219fbebSmrg		     RootWindowOfScreen(XtScreen(xw)),
1761d522f475Smrg		     &root_win,
1762d522f475Smrg		     &root_x,
1763d522f475Smrg		     &root_y,
1764d522f475Smrg		     width,
1765d522f475Smrg		     height,
1766d522f475Smrg		     &root_border,
1767d522f475Smrg		     &root_depth)) {
1768d522f475Smrg	TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n",
1769d522f475Smrg	       root_x,
1770d522f475Smrg	       root_y,
1771d522f475Smrg	       *width,
1772d522f475Smrg	       *height,
1773d522f475Smrg	       root_border));
1774d522f475Smrg
1775d522f475Smrg	*width -= (root_border * 2);
1776d522f475Smrg	*height -= (root_border * 2);
1777d522f475Smrg
1778d522f475Smrg	hints.flags = PMaxSize;
1779d522f475Smrg	if (XGetWMNormalHints(screen->display,
1780c219fbebSmrg			      VShellWindow(xw),
1781d522f475Smrg			      &hints,
1782d522f475Smrg			      &supp)
1783d522f475Smrg	    && (hints.flags & PMaxSize) != 0) {
1784d522f475Smrg
1785d522f475Smrg	    TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n",
1786d522f475Smrg		   hints.max_width,
1787d522f475Smrg		   hints.max_height));
1788d522f475Smrg
1789d522f475Smrg	    if ((unsigned) hints.max_width < *width)
1790b7c89284Ssnj		*width = (unsigned) hints.max_width;
1791d522f475Smrg	    if ((unsigned) hints.max_height < *height)
1792b7c89284Ssnj		*height = (unsigned) hints.max_height;
1793d522f475Smrg	}
17943367019cSmrg	code = 1;
17953367019cSmrg    } else {
17963367019cSmrg	*width = 0;
17973367019cSmrg	*height = 0;
17983367019cSmrg	code = 0;
1799d522f475Smrg    }
18003367019cSmrg    return code;
1801d522f475Smrg}
1802d522f475Smrg
1803d522f475Smrgvoid
1804c219fbebSmrgRequestMaximize(XtermWidget xw, int maximize)
1805d522f475Smrg{
1806c219fbebSmrg    TScreen *screen = TScreenOf(xw);
1807d522f475Smrg    XWindowAttributes wm_attrs, vshell_attrs;
1808d4fba8b9Smrg    unsigned root_width = 0, root_height = 0;
18093367019cSmrg    Boolean success = False;
1810d522f475Smrg
18113367019cSmrg    TRACE(("RequestMaximize %d:%s\n",
18123367019cSmrg	   maximize,
18133367019cSmrg	   (maximize
18143367019cSmrg	    ? "maximize"
18153367019cSmrg	    : "restore")));
1816d522f475Smrg
18173367019cSmrg    /*
18183367019cSmrg     * Before any maximize, ensure that we can capture the current screensize
18193367019cSmrg     * as well as the estimated root-window size.
18203367019cSmrg     */
18213367019cSmrg    if (maximize
18223367019cSmrg	&& QueryMaximize(xw, &root_width, &root_height)
18233367019cSmrg	&& xtermGetWinAttrs(screen->display,
18243367019cSmrg			    WMFrameWindow(xw),
18253367019cSmrg			    &wm_attrs)
18263367019cSmrg	&& xtermGetWinAttrs(screen->display,
18273367019cSmrg			    VShellWindow(xw),
18283367019cSmrg			    &vshell_attrs)) {
18293367019cSmrg
18303367019cSmrg	if (screen->restore_data != True
18313367019cSmrg	    || screen->restore_width != root_width
18323367019cSmrg	    || screen->restore_height != root_height) {
18333367019cSmrg	    screen->restore_data = True;
1834d4fba8b9Smrg	    screen->restore_x = wm_attrs.x;
1835d4fba8b9Smrg	    screen->restore_y = wm_attrs.y;
18363367019cSmrg	    screen->restore_width = (unsigned) vshell_attrs.width;
18373367019cSmrg	    screen->restore_height = (unsigned) vshell_attrs.height;
18383367019cSmrg	    TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n",
1839d522f475Smrg		   screen->restore_x,
1840d522f475Smrg		   screen->restore_y,
1841d522f475Smrg		   screen->restore_width,
1842d522f475Smrg		   screen->restore_height));
18433367019cSmrg	}
1844d522f475Smrg
18453367019cSmrg	/* subtract wm decoration dimensions */
1846d4fba8b9Smrg	root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width);
1847d4fba8b9Smrg	root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height);
18483367019cSmrg	success = True;
18493367019cSmrg    } else if (screen->restore_data) {
18503367019cSmrg	success = True;
18513367019cSmrg	maximize = 0;
18523367019cSmrg    }
18533367019cSmrg
18543367019cSmrg    if (success) {
18553367019cSmrg	switch (maximize) {
18563367019cSmrg	case 3:
18573367019cSmrg	    FullScreen(xw, 3);	/* depends on EWMH */
18583367019cSmrg	    break;
18593367019cSmrg	case 2:
18603367019cSmrg	    FullScreen(xw, 2);	/* depends on EWMH */
18613367019cSmrg	    break;
18623367019cSmrg	case 1:
18633367019cSmrg	    FullScreen(xw, 0);	/* overrides any EWMH hint */
1864d4fba8b9Smrg	    TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n",
1865d4fba8b9Smrg		   0,
1866d4fba8b9Smrg		   0,
1867d4fba8b9Smrg		   root_width,
1868d4fba8b9Smrg		   root_height));
18693367019cSmrg	    XMoveResizeWindow(screen->display, VShellWindow(xw),
1870d4fba8b9Smrg			      0,	/* x */
1871d4fba8b9Smrg			      0,	/* y */
18723367019cSmrg			      root_width,
18733367019cSmrg			      root_height);
18743367019cSmrg	    break;
18753367019cSmrg
18763367019cSmrg	default:
18773367019cSmrg	    FullScreen(xw, 0);	/* reset any EWMH hint */
18783367019cSmrg	    if (screen->restore_data) {
18793367019cSmrg		screen->restore_data = False;
18803367019cSmrg
1881d4fba8b9Smrg		TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n",
18823367019cSmrg		       screen->restore_x,
18833367019cSmrg		       screen->restore_y,
18843367019cSmrg		       screen->restore_width,
18853367019cSmrg		       screen->restore_height));
18863367019cSmrg
18873367019cSmrg		XMoveResizeWindow(screen->display,
18883367019cSmrg				  VShellWindow(xw),
18893367019cSmrg				  screen->restore_x,
18903367019cSmrg				  screen->restore_y,
18913367019cSmrg				  screen->restore_width,
18923367019cSmrg				  screen->restore_height);
18933367019cSmrg	    }
18943367019cSmrg	    break;
1895d522f475Smrg	}
1896d522f475Smrg    }
1897d522f475Smrg}
1898d522f475Smrg
1899d522f475Smrg/*ARGSUSED*/
1900d522f475Smrgvoid
1901b7c89284SsnjHandleMaximize(Widget w,
19029a64e1c5Smrg	       XEvent *event GCC_UNUSED,
1903fa3f02f3Smrg	       String *params GCC_UNUSED,
1904d522f475Smrg	       Cardinal *nparams GCC_UNUSED)
1905d522f475Smrg{
1906b7c89284Ssnj    XtermWidget xw;
1907b7c89284Ssnj
1908b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1909b7c89284Ssnj	RequestMaximize(xw, 1);
1910d522f475Smrg    }
1911d522f475Smrg}
1912d522f475Smrg
1913d522f475Smrg/*ARGSUSED*/
1914d522f475Smrgvoid
1915b7c89284SsnjHandleRestoreSize(Widget w,
19169a64e1c5Smrg		  XEvent *event GCC_UNUSED,
1917fa3f02f3Smrg		  String *params GCC_UNUSED,
1918d522f475Smrg		  Cardinal *nparams GCC_UNUSED)
1919d522f475Smrg{
1920b7c89284Ssnj    XtermWidget xw;
1921b7c89284Ssnj
1922b7c89284Ssnj    if ((xw = getXtermWidget(w)) != 0) {
1923b7c89284Ssnj	RequestMaximize(xw, 0);
1924d522f475Smrg    }
1925d522f475Smrg}
1926d522f475Smrg#endif /* OPT_MAXIMIZE */
1927d522f475Smrg
1928d522f475Smrgvoid
1929d522f475SmrgRedraw(void)
1930d522f475Smrg{
1931d4fba8b9Smrg    XtermWidget xw = term;
1932d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1933d522f475Smrg    XExposeEvent event;
1934d522f475Smrg
1935d522f475Smrg    TRACE(("Redraw\n"));
1936d522f475Smrg
1937d522f475Smrg    event.type = Expose;
1938d522f475Smrg    event.display = screen->display;
1939d522f475Smrg    event.x = 0;
1940d522f475Smrg    event.y = 0;
1941d522f475Smrg    event.count = 0;
1942d522f475Smrg
1943d522f475Smrg    if (VWindow(screen)) {
1944d522f475Smrg	event.window = VWindow(screen);
1945d4fba8b9Smrg	event.width = xw->core.width;
1946d4fba8b9Smrg	event.height = xw->core.height;
1947d4fba8b9Smrg	(*xw->core.widget_class->core_class.expose) ((Widget) xw,
1948d4fba8b9Smrg						     (XEvent *) &event,
1949d4fba8b9Smrg						     NULL);
1950d522f475Smrg	if (ScrollbarWidth(screen)) {
1951d522f475Smrg	    (screen->scrollWidget->core.widget_class->core_class.expose)
19529a64e1c5Smrg		(screen->scrollWidget, (XEvent *) &event, NULL);
1953d522f475Smrg	}
1954d522f475Smrg    }
1955d522f475Smrg#if OPT_TEK4014
1956d4fba8b9Smrg    if (TEK4014_SHOWN(xw)) {
1957cd3331d0Smrg	TekScreen *tekscr = TekScreenOf(tekWidget);
1958d522f475Smrg	event.window = TWindow(tekscr);
1959d522f475Smrg	event.width = tekWidget->core.width;
1960d522f475Smrg	event.height = tekWidget->core.height;
19619a64e1c5Smrg	TekExpose((Widget) tekWidget, (XEvent *) &event, NULL);
1962d522f475Smrg    }
1963d522f475Smrg#endif
1964d522f475Smrg}
1965d522f475Smrg
1966d522f475Smrg#ifdef VMS
1967d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d"
1968d522f475Smrg#else
1969d522f475Smrg#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d"
1970d522f475Smrg#endif
1971d522f475Smrg
1972d522f475Smrgvoid
1973d522f475Smrgtimestamp_filename(char *dst, const char *src)
1974d522f475Smrg{
1975d522f475Smrg    time_t tstamp;
1976d522f475Smrg    struct tm *tstruct;
1977d522f475Smrg
1978d522f475Smrg    tstamp = time((time_t *) 0);
1979d522f475Smrg    tstruct = localtime(&tstamp);
1980d522f475Smrg    sprintf(dst, TIMESTAMP_FMT,
1981d522f475Smrg	    src,
19823367019cSmrg	    (int) tstruct->tm_year + 1900,
1983d522f475Smrg	    tstruct->tm_mon + 1,
1984d522f475Smrg	    tstruct->tm_mday,
1985d522f475Smrg	    tstruct->tm_hour,
1986d522f475Smrg	    tstruct->tm_min,
1987d522f475Smrg	    tstruct->tm_sec);
1988d522f475Smrg}
1989d522f475Smrg
1990d4fba8b9SmrgFILE *
1991d4fba8b9Smrgcreate_printfile(XtermWidget xw, const char *suffix)
1992d4fba8b9Smrg{
1993d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
1994d4fba8b9Smrg    char fname[1024];
1995d4fba8b9Smrg    int fd;
1996d4fba8b9Smrg    FILE *fp;
1997d4fba8b9Smrg
1998d4fba8b9Smrg#ifdef VMS
1999d4fba8b9Smrg    sprintf(fname, "sys$scratch:xterm%s", suffix);
2000d4fba8b9Smrg#elif defined(HAVE_STRFTIME)
2001d4fba8b9Smrg    {
2002d4fba8b9Smrg	char format[1024];
2003d4fba8b9Smrg	time_t now;
2004d4fba8b9Smrg	struct tm *ltm;
2005d4fba8b9Smrg
2006d4fba8b9Smrg	now = time((time_t *) 0);
2007d4fba8b9Smrg	ltm = localtime(&now);
2008d4fba8b9Smrg
2009d4fba8b9Smrg	sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix);
2010d4fba8b9Smrg	if (strftime(fname, sizeof fname, format, ltm) == 0) {
2011d4fba8b9Smrg	    sprintf(fname, "xterm%s", suffix);
2012d4fba8b9Smrg	}
2013d4fba8b9Smrg    }
2014d4fba8b9Smrg#else
2015d4fba8b9Smrg    sprintf(fname, "xterm%s", suffix);
2016d4fba8b9Smrg#endif
2017d4fba8b9Smrg    fd = open_userfile(screen->uid, screen->gid, fname, False);
2018d4fba8b9Smrg    fp = (fd >= 0) ? fdopen(fd, "wb") : NULL;
2019d4fba8b9Smrg    return fp;
2020d4fba8b9Smrg}
2021d4fba8b9Smrg
2022d522f475Smrgint
2023d522f475Smrgopen_userfile(uid_t uid, gid_t gid, char *path, Bool append)
2024d522f475Smrg{
2025d522f475Smrg    int fd;
2026d522f475Smrg    struct stat sb;
2027d522f475Smrg
2028d522f475Smrg#ifdef VMS
2029d522f475Smrg    if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
2030d522f475Smrg	int the_error = errno;
20313367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
20323367019cSmrg		     path,
20333367019cSmrg		     the_error,
20343367019cSmrg		     SysErrorMsg(the_error));
2035d522f475Smrg	return -1;
2036d522f475Smrg    }
2037d522f475Smrg    chown(path, uid, gid);
2038d522f475Smrg#else
2039d522f475Smrg    if ((access(path, F_OK) != 0 && (errno != ENOENT))
2040d522f475Smrg	|| (creat_as(uid, gid, append, path, 0644) <= 0)
2041d522f475Smrg	|| ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) {
2042d522f475Smrg	int the_error = errno;
20433367019cSmrg	xtermWarning("cannot open %s: %d:%s\n",
20443367019cSmrg		     path,
20453367019cSmrg		     the_error,
20463367019cSmrg		     SysErrorMsg(the_error));
2047d522f475Smrg	return -1;
2048d522f475Smrg    }
2049d522f475Smrg#endif
2050d522f475Smrg
2051d522f475Smrg    /*
2052d522f475Smrg     * Doublecheck that the user really owns the file that we've opened before
2053d522f475Smrg     * we do any damage, and that it is not world-writable.
2054d522f475Smrg     */
2055d522f475Smrg    if (fstat(fd, &sb) < 0
2056d522f475Smrg	|| sb.st_uid != uid
2057d522f475Smrg	|| (sb.st_mode & 022) != 0) {
20583367019cSmrg	xtermWarning("you do not own %s\n", path);
2059d522f475Smrg	close(fd);
2060d522f475Smrg	return -1;
2061d522f475Smrg    }
2062d522f475Smrg    return fd;
2063d522f475Smrg}
2064d522f475Smrg
2065d522f475Smrg#ifndef VMS
2066d522f475Smrg/*
2067d522f475Smrg * Create a file only if we could with the permissions of the real user id.
2068d522f475Smrg * We could emulate this with careful use of access() and following
2069d522f475Smrg * symbolic links, but that is messy and has race conditions.
2070d522f475Smrg * Forking is messy, too, but we can't count on setreuid() or saved set-uids
2071d522f475Smrg * being available.
2072d522f475Smrg *
2073d522f475Smrg * Note: When called for user logging, we have ensured that the real and
2074d522f475Smrg * effective user ids are the same, so this remains as a convenience function
2075d522f475Smrg * for the debug logs.
2076d522f475Smrg *
2077d522f475Smrg * Returns
2078d522f475Smrg *	 1 if we can proceed to open the file in relative safety,
2079d522f475Smrg *	-1 on error, e.g., cannot fork
2080d522f475Smrg *	 0 otherwise.
2081d522f475Smrg */
2082d522f475Smrgint
2083712a7ff4Smrgcreat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode)
2084d522f475Smrg{
2085d522f475Smrg    int fd;
2086d522f475Smrg    pid_t pid;
2087d522f475Smrg    int retval = 0;
2088d522f475Smrg    int childstat = 0;
2089d522f475Smrg#ifndef HAVE_WAITPID
2090d522f475Smrg    int waited;
20913367019cSmrg    void (*chldfunc) (int);
2092d522f475Smrg
2093d522f475Smrg    chldfunc = signal(SIGCHLD, SIG_DFL);
2094d522f475Smrg#endif /* HAVE_WAITPID */
2095d522f475Smrg
2096d522f475Smrg    TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n",
2097d522f475Smrg	   (int) uid, (int) geteuid(),
2098d522f475Smrg	   (int) gid, (int) getegid(),
2099d522f475Smrg	   append,
2100d522f475Smrg	   pathname,
2101d522f475Smrg	   mode));
2102d522f475Smrg
2103d522f475Smrg    if (uid == geteuid() && gid == getegid()) {
2104d522f475Smrg	fd = open(pathname,
2105d522f475Smrg		  O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2106d522f475Smrg		  mode);
2107d522f475Smrg	if (fd >= 0)
2108d522f475Smrg	    close(fd);
2109d522f475Smrg	return (fd >= 0);
2110d522f475Smrg    }
2111d522f475Smrg
2112d522f475Smrg    pid = fork();
2113d522f475Smrg    switch (pid) {
2114d522f475Smrg    case 0:			/* child */
2115d522f475Smrg	if (setgid(gid) == -1
2116d522f475Smrg	    || setuid(uid) == -1) {
2117d522f475Smrg	    /* we cannot report an error here via stderr, just quit */
2118d522f475Smrg	    retval = 1;
2119d522f475Smrg	} else {
2120d522f475Smrg	    fd = open(pathname,
2121d522f475Smrg		      O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL),
2122d522f475Smrg		      mode);
2123d522f475Smrg	    if (fd >= 0) {
2124d522f475Smrg		close(fd);
2125d522f475Smrg		retval = 0;
2126d522f475Smrg	    } else {
2127d522f475Smrg		retval = 1;
2128d522f475Smrg	    }
2129d522f475Smrg	}
2130d522f475Smrg	_exit(retval);
2131d522f475Smrg	/* NOTREACHED */
2132d522f475Smrg    case -1:			/* error */
2133d522f475Smrg	return retval;
2134d522f475Smrg    default:			/* parent */
2135d522f475Smrg#ifdef HAVE_WAITPID
2136d522f475Smrg	while (waitpid(pid, &childstat, 0) < 0) {
2137d522f475Smrg#ifdef EINTR
2138d522f475Smrg	    if (errno == EINTR)
2139d522f475Smrg		continue;
2140d522f475Smrg#endif /* EINTR */
2141d522f475Smrg#ifdef ERESTARTSYS
2142d522f475Smrg	    if (errno == ERESTARTSYS)
2143d522f475Smrg		continue;
2144d522f475Smrg#endif /* ERESTARTSYS */
2145d522f475Smrg	    break;
2146d522f475Smrg	}
2147d522f475Smrg#else /* HAVE_WAITPID */
2148d522f475Smrg	waited = wait(&childstat);
2149d522f475Smrg	signal(SIGCHLD, chldfunc);
2150d522f475Smrg	/*
2151d522f475Smrg	   Since we had the signal handler uninstalled for a while,
2152d522f475Smrg	   we might have missed the termination of our screen child.
2153d522f475Smrg	   If we can check for this possibility without hanging, do so.
2154d522f475Smrg	 */
2155d522f475Smrg	do
2156cd3331d0Smrg	    if (waited == TScreenOf(term)->pid)
21573367019cSmrg		NormalExit();
2158d522f475Smrg	while ((waited = nonblocking_wait()) > 0) ;
2159d522f475Smrg#endif /* HAVE_WAITPID */
2160d522f475Smrg#ifndef WIFEXITED
2161d522f475Smrg#define WIFEXITED(status) ((status & 0xff) != 0)
2162d522f475Smrg#endif
2163d522f475Smrg	if (WIFEXITED(childstat))
2164d522f475Smrg	    retval = 1;
2165d522f475Smrg	return retval;
2166d522f475Smrg    }
2167d522f475Smrg}
2168d522f475Smrg#endif /* !VMS */
2169d522f475Smrg
2170d522f475Smrgint
2171fa3f02f3SmrgxtermResetIds(TScreen *screen)
2172d522f475Smrg{
2173d522f475Smrg    int result = 0;
2174d522f475Smrg    if (setgid(screen->gid) == -1) {
21753367019cSmrg	xtermWarning("unable to reset group-id\n");
2176d522f475Smrg	result = -1;
2177d522f475Smrg    }
2178d522f475Smrg    if (setuid(screen->uid) == -1) {
21793367019cSmrg	xtermWarning("unable to reset user-id\n");
2180d522f475Smrg	result = -1;
2181d522f475Smrg    }
2182d522f475Smrg    return result;
2183d522f475Smrg}
2184d522f475Smrg
2185d522f475Smrg#ifdef ALLOWLOGGING
2186d522f475Smrg
2187d522f475Smrg/*
2188d522f475Smrg * Logging is a security hole, since it allows a setuid program to write
2189d522f475Smrg * arbitrary data to an arbitrary file.  So it is disabled by default.
2190d522f475Smrg */
2191d522f475Smrg
2192d522f475Smrg#ifdef ALLOWLOGFILEEXEC
21933367019cSmrgstatic void
2194d4fba8b9Smrghandle_SIGPIPE(int sig GCC_UNUSED)
2195d522f475Smrg{
2196cd3331d0Smrg    XtermWidget xw = term;
2197cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2198d522f475Smrg
21993367019cSmrg    DEBUG_MSG("handle:logpipe\n");
2200d522f475Smrg#ifdef SYSV
2201d522f475Smrg    (void) signal(SIGPIPE, SIG_IGN);
2202d522f475Smrg#endif /* SYSV */
2203d522f475Smrg    if (screen->logging)
2204cd3331d0Smrg	CloseLog(xw);
2205d522f475Smrg}
2206d4fba8b9Smrg
2207d4fba8b9Smrg/*
2208d4fba8b9Smrg * Open a command to pipe log data to it.
2209d4fba8b9Smrg * Warning, enabling this "feature" allows arbitrary programs
2210d4fba8b9Smrg * to be run.  If ALLOWLOGFILECHANGES is enabled, this can be
2211d4fba8b9Smrg * done through escape sequences....  You have been warned.
2212d4fba8b9Smrg */
2213d4fba8b9Smrgstatic void
2214d4fba8b9SmrgStartLogExec(TScreen *screen)
2215d4fba8b9Smrg{
2216d4fba8b9Smrg    int pid;
2217d4fba8b9Smrg    int p[2];
2218d4fba8b9Smrg    static char *shell;
2219d4fba8b9Smrg    struct passwd pw;
2220d4fba8b9Smrg
2221d4fba8b9Smrg    if ((shell = x_getenv("SHELL")) == NULL) {
2222d4fba8b9Smrg
2223d4fba8b9Smrg	if (x_getpwuid(screen->uid, &pw)) {
2224d4fba8b9Smrg	    char *name = x_getlogin(screen->uid, &pw);
2225d4fba8b9Smrg	    if (*(pw.pw_shell)) {
2226d4fba8b9Smrg		shell = pw.pw_shell;
2227d4fba8b9Smrg	    }
2228d4fba8b9Smrg	    free(name);
2229d4fba8b9Smrg	}
2230d4fba8b9Smrg    }
2231d4fba8b9Smrg
2232d4fba8b9Smrg    if (shell == 0) {
2233d4fba8b9Smrg	static char dummy[] = "/bin/sh";
2234d4fba8b9Smrg	shell = dummy;
2235d4fba8b9Smrg    }
2236d4fba8b9Smrg
2237d4fba8b9Smrg    if (access(shell, X_OK) != 0) {
2238d4fba8b9Smrg	xtermPerror("Can't execute `%s'\n", shell);
2239d4fba8b9Smrg	return;
2240d4fba8b9Smrg    }
2241d4fba8b9Smrg
2242d4fba8b9Smrg    if (pipe(p) < 0) {
2243d4fba8b9Smrg	xtermPerror("Can't make a pipe connection\n");
2244d4fba8b9Smrg	return;
2245d4fba8b9Smrg    } else if ((pid = fork()) < 0) {
2246d4fba8b9Smrg	xtermPerror("Can't fork...\n");
2247d4fba8b9Smrg	return;
2248d4fba8b9Smrg    }
2249d4fba8b9Smrg    if (pid == 0) {		/* child */
2250d4fba8b9Smrg	/*
2251d4fba8b9Smrg	 * Close our output (we won't be talking back to the
2252d4fba8b9Smrg	 * parent), and redirect our child's output to the
2253d4fba8b9Smrg	 * original stderr.
2254d4fba8b9Smrg	 */
2255d4fba8b9Smrg	close(p[1]);
2256d4fba8b9Smrg	dup2(p[0], 0);
2257d4fba8b9Smrg	close(p[0]);
2258d4fba8b9Smrg	dup2(fileno(stderr), 1);
2259d4fba8b9Smrg	dup2(fileno(stderr), 2);
2260d4fba8b9Smrg
2261d4fba8b9Smrg	close(fileno(stderr));
2262d4fba8b9Smrg	close(ConnectionNumber(screen->display));
2263d4fba8b9Smrg	close(screen->respond);
2264d4fba8b9Smrg
2265d4fba8b9Smrg	signal(SIGHUP, SIG_DFL);
2266d4fba8b9Smrg	signal(SIGCHLD, SIG_DFL);
2267d4fba8b9Smrg
2268d4fba8b9Smrg	/* (this is redundant) */
2269d4fba8b9Smrg	if (xtermResetIds(screen) < 0)
2270d4fba8b9Smrg	    exit(ERROR_SETUID);
2271d4fba8b9Smrg
2272d4fba8b9Smrg	execl(shell, shell, "-c", &screen->logfile[1], (void *) 0);
2273d4fba8b9Smrg	xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]);
2274d4fba8b9Smrg	exit(ERROR_LOGEXEC);
2275d4fba8b9Smrg    }
2276d4fba8b9Smrg    close(p[0]);
2277d4fba8b9Smrg    screen->logfd = p[1];
2278d4fba8b9Smrg    signal(SIGPIPE, handle_SIGPIPE);
2279d4fba8b9Smrg}
2280d522f475Smrg#endif /* ALLOWLOGFILEEXEC */
2281d522f475Smrg
2282d4fba8b9Smrg/*
2283d4fba8b9Smrg * Generate a path for a logfile if no default path is given.
2284d4fba8b9Smrg */
2285d4fba8b9Smrgstatic char *
2286d4fba8b9SmrgGenerateLogPath(void)
2287d4fba8b9Smrg{
2288d4fba8b9Smrg    static char *log_default = NULL;
2289d4fba8b9Smrg
2290d4fba8b9Smrg    /* once opened we just reuse the same log name */
2291d4fba8b9Smrg    if (log_default)
2292d4fba8b9Smrg	return (log_default);
2293d4fba8b9Smrg
2294d4fba8b9Smrg#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME)
2295d4fba8b9Smrg    {
2296d4fba8b9Smrg#define LEN_HOSTNAME 255
2297d4fba8b9Smrg	/* Internet standard limit (RFC 1035):  ``To simplify implementations,
2298d4fba8b9Smrg	 * the total length of a domain name (i.e., label octets and label
2299d4fba8b9Smrg	 * length octets) is restricted to 255 octets or less.''
2300d4fba8b9Smrg	 */
2301d4fba8b9Smrg#define LEN_GETPID 9
2302d4fba8b9Smrg	/*
2303d4fba8b9Smrg	 * This is arbitrary...
2304d4fba8b9Smrg	 */
2305d4fba8b9Smrg	const char form[] = "Xterm.log.%s%s.%lu";
2306d4fba8b9Smrg	char where[LEN_HOSTNAME + 1];
2307d4fba8b9Smrg	char when[LEN_TIMESTAMP];
2308d4fba8b9Smrg	time_t now = time((time_t *) 0);
2309d4fba8b9Smrg	struct tm *ltm = (struct tm *) localtime(&now);
2310d4fba8b9Smrg
2311d4fba8b9Smrg	if ((gethostname(where, sizeof(where)) == 0) &&
2312d4fba8b9Smrg	    (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) &&
2313d4fba8b9Smrg	    ((log_default = (char *) malloc((sizeof(form)
2314d4fba8b9Smrg					     + strlen(where)
2315d4fba8b9Smrg					     + strlen(when)
2316d4fba8b9Smrg					     + LEN_GETPID))) != NULL)) {
2317d4fba8b9Smrg	    (void) sprintf(log_default,
2318d4fba8b9Smrg			   form,
2319d4fba8b9Smrg			   where, when,
2320d4fba8b9Smrg			   ((unsigned long) getpid()) % ((unsigned long) 1e10));
2321d4fba8b9Smrg	}
2322d4fba8b9Smrg    }
2323d4fba8b9Smrg#else
2324d4fba8b9Smrg    static const char log_def_name[] = "XtermLog.XXXXXX";
2325d4fba8b9Smrg    if ((log_default = x_strdup(log_def_name)) != NULL) {
2326d4fba8b9Smrg	mktemp(log_default);
2327d4fba8b9Smrg    }
2328d4fba8b9Smrg#endif
2329d4fba8b9Smrg
2330d4fba8b9Smrg    return (log_default);
2331d4fba8b9Smrg}
2332d4fba8b9Smrg
2333d522f475Smrgvoid
2334cd3331d0SmrgStartLog(XtermWidget xw)
2335d522f475Smrg{
2336cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2337d522f475Smrg
2338d522f475Smrg    if (screen->logging || (screen->inhibit & I_LOG))
2339d522f475Smrg	return;
2340d522f475Smrg#ifdef VMS			/* file name is fixed in VMS variant */
2341d522f475Smrg    screen->logfd = open(XTERM_VMS_LOGFILE,
2342d522f475Smrg			 O_CREAT | O_TRUNC | O_APPEND | O_RDWR,
2343d522f475Smrg			 0640);
2344d522f475Smrg    if (screen->logfd < 0)
2345d522f475Smrg	return;			/* open failed */
2346d522f475Smrg#else /*VMS */
23473367019cSmrg
2348d4fba8b9Smrg    /* if we weren't supplied with a logfile path, generate one */
2349d4fba8b9Smrg    if (IsEmpty(screen->logfile))
2350d4fba8b9Smrg	screen->logfile = GenerateLogPath();
23513367019cSmrg
2352d4fba8b9Smrg    /* give up if we were unable to allocate the filename */
2353d4fba8b9Smrg    if (!screen->logfile)
2354d4fba8b9Smrg	return;
2355d522f475Smrg
2356d4fba8b9Smrg    if (*screen->logfile == '|') {	/* exec command */
2357d4fba8b9Smrg#ifdef ALLOWLOGFILEEXEC
2358d4fba8b9Smrg	StartLogExec(screen);
2359d522f475Smrg#else
2360cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2361cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
2362d522f475Smrg	return;
2363d522f475Smrg#endif
2364d4fba8b9Smrg    } else if (strcmp(screen->logfile, "-") == 0) {
2365d4fba8b9Smrg	screen->logfd = STDOUT_FILENO;
2366d522f475Smrg    } else {
2367d522f475Smrg	if ((screen->logfd = open_userfile(screen->uid,
2368d522f475Smrg					   screen->gid,
2369d522f475Smrg					   screen->logfile,
2370d4fba8b9Smrg					   True)) < 0)
2371d522f475Smrg	    return;
2372d522f475Smrg    }
2373d522f475Smrg#endif /*VMS */
2374d522f475Smrg    screen->logstart = VTbuffer->next;
2375d522f475Smrg    screen->logging = True;
2376d522f475Smrg    update_logging();
2377d522f475Smrg}
2378d522f475Smrg
2379d522f475Smrgvoid
2380cd3331d0SmrgCloseLog(XtermWidget xw)
2381d522f475Smrg{
2382cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2383cd3331d0Smrg
2384d522f475Smrg    if (!screen->logging || (screen->inhibit & I_LOG))
2385d522f475Smrg	return;
2386cd3331d0Smrg    FlushLog(xw);
2387d522f475Smrg    close(screen->logfd);
2388d522f475Smrg    screen->logging = False;
2389d522f475Smrg    update_logging();
2390d522f475Smrg}
2391d522f475Smrg
2392d522f475Smrgvoid
2393cd3331d0SmrgFlushLog(XtermWidget xw)
2394d522f475Smrg{
2395cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
2396cd3331d0Smrg
2397d522f475Smrg    if (screen->logging && !(screen->inhibit & I_LOG)) {
2398d522f475Smrg	Char *cp;
2399d522f475Smrg	int i;
2400d522f475Smrg
2401d522f475Smrg#ifdef VMS			/* avoid logging output loops which otherwise occur sometimes
2402d522f475Smrg				   when there is no output and cp/screen->logstart are 1 apart */
2403d522f475Smrg	if (!tt_new_output)
2404d522f475Smrg	    return;
2405d522f475Smrg	tt_new_output = False;
2406d522f475Smrg#endif /* VMS */
2407d522f475Smrg	cp = VTbuffer->next;
2408d522f475Smrg	if (screen->logstart != 0
2409cd3331d0Smrg	    && (i = (int) (cp - screen->logstart)) > 0) {
2410cd3331d0Smrg	    IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i));
2411d522f475Smrg	}
2412d522f475Smrg	screen->logstart = VTbuffer->next;
2413d522f475Smrg    }
2414d522f475Smrg}
2415d522f475Smrg
2416d522f475Smrg#endif /* ALLOWLOGGING */
2417d522f475Smrg
2418d522f475Smrg/***====================================================================***/
2419d522f475Smrg
2420d4fba8b9Smrgstatic unsigned
2421d4fba8b9SmrgmaskToShift(unsigned long mask)
2422d4fba8b9Smrg{
2423d4fba8b9Smrg    unsigned result = 0;
2424d4fba8b9Smrg    if (mask != 0) {
2425d4fba8b9Smrg	while ((mask & 1) == 0) {
2426d4fba8b9Smrg	    mask >>= 1;
2427d4fba8b9Smrg	    ++result;
2428d4fba8b9Smrg	}
2429d4fba8b9Smrg    }
2430d4fba8b9Smrg    return result;
2431d4fba8b9Smrg}
2432d4fba8b9Smrg
2433d4fba8b9Smrgstatic unsigned
2434d4fba8b9SmrgmaskToWidth(unsigned long mask)
2435d4fba8b9Smrg{
2436d4fba8b9Smrg    unsigned result = 0;
2437d4fba8b9Smrg    while (mask != 0) {
2438d4fba8b9Smrg	if ((mask & 1) != 0)
2439d4fba8b9Smrg	    ++result;
2440d4fba8b9Smrg	mask >>= 1;
2441d4fba8b9Smrg    }
2442d4fba8b9Smrg    return result;
2443d4fba8b9Smrg}
2444d4fba8b9Smrg
2445fa3f02f3Smrgint
2446fa3f02f3SmrggetVisualInfo(XtermWidget xw)
2447fa3f02f3Smrg{
2448fa3f02f3Smrg#define MYFMT "getVisualInfo \
2449fa3f02f3Smrgdepth %d, \
2450fa3f02f3Smrgtype %d (%s), \
2451fa3f02f3Smrgsize %d \
2452fa3f02f3Smrgrgb masks (%04lx/%04lx/%04lx)\n"
2453fa3f02f3Smrg#define MYARG \
2454fa3f02f3Smrg       vi->depth,\
2455fa3f02f3Smrg       vi->class,\
2456fa3f02f3Smrg       ((vi->class & 1) ? "dynamic" : "static"),\
2457fa3f02f3Smrg       vi->colormap_size,\
2458fa3f02f3Smrg       vi->red_mask,\
2459fa3f02f3Smrg       vi->green_mask,\
2460fa3f02f3Smrg       vi->blue_mask
2461d522f475Smrg
2462fa3f02f3Smrg    TScreen *screen = TScreenOf(xw);
2463fa3f02f3Smrg    Display *dpy = screen->display;
2464fa3f02f3Smrg    XVisualInfo myTemplate;
2465fa3f02f3Smrg
2466fa3f02f3Smrg    if (xw->visInfo == 0 && xw->numVisuals == 0) {
2467fa3f02f3Smrg	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
2468fa3f02f3Smrg								XDefaultScreen(dpy)));
2469fa3f02f3Smrg	xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
2470fa3f02f3Smrg				     &myTemplate, &xw->numVisuals);
2471fa3f02f3Smrg
2472fa3f02f3Smrg	if ((xw->visInfo != 0) && (xw->numVisuals > 0)) {
2473fa3f02f3Smrg	    XVisualInfo *vi = xw->visInfo;
2474d4fba8b9Smrg	    xw->rgb_widths[0] = maskToWidth(vi->red_mask);
2475d4fba8b9Smrg	    xw->rgb_widths[1] = maskToWidth(vi->green_mask);
2476d4fba8b9Smrg	    xw->rgb_widths[2] = maskToWidth(vi->blue_mask);
2477d4fba8b9Smrg	    xw->rgb_shifts[0] = maskToShift(vi->red_mask);
2478d4fba8b9Smrg	    xw->rgb_shifts[1] = maskToShift(vi->green_mask);
2479d4fba8b9Smrg	    xw->rgb_shifts[2] = maskToShift(vi->blue_mask);
2480d4fba8b9Smrg
2481d4fba8b9Smrg	    xw->has_rgb = ((vi->red_mask != 0) &&
2482d4fba8b9Smrg			   (vi->green_mask != 0) &&
2483d4fba8b9Smrg			   (vi->blue_mask != 0) &&
2484d4fba8b9Smrg			   ((vi->red_mask & vi->green_mask) == 0) &&
2485d4fba8b9Smrg			   ((vi->green_mask & vi->blue_mask) == 0) &&
2486d4fba8b9Smrg			   ((vi->blue_mask & vi->red_mask) == 0));
2487d4fba8b9Smrg
2488fa3f02f3Smrg	    if (resource.reportColors) {
2489fa3f02f3Smrg		printf(MYFMT, MYARG);
2490fa3f02f3Smrg	    }
2491fa3f02f3Smrg	    TRACE((MYFMT, MYARG));
2492d4fba8b9Smrg	    TRACE(("...shifts %u/%u/%u\n",
2493d4fba8b9Smrg		   xw->rgb_shifts[0],
2494d4fba8b9Smrg		   xw->rgb_shifts[1],
2495d4fba8b9Smrg		   xw->rgb_shifts[2]));
2496fa3f02f3Smrg	}
2497fa3f02f3Smrg    }
2498fa3f02f3Smrg    return (xw->visInfo != 0) && (xw->numVisuals > 0);
2499fa3f02f3Smrg#undef MYFMT
2500fa3f02f3Smrg#undef MYARG
2501fa3f02f3Smrg}
25023367019cSmrg
25039a64e1c5Smrg#if OPT_ISO_COLORS
2504d4fba8b9Smrgstatic Bool
2505d4fba8b9SmrgReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final)
25069a64e1c5Smrg{
2507d4fba8b9Smrg    Bool result = False;
2508d4fba8b9Smrg
25099a64e1c5Smrg    if (AllowColorOps(xw, ecGetAnsiColor)) {
25109a64e1c5Smrg	XColor color;
25119a64e1c5Smrg	Colormap cmap = xw->core.colormap;
25129a64e1c5Smrg	char buffer[80];
25139a64e1c5Smrg
25149a64e1c5Smrg	TRACE(("ReportAnsiColorRequest %d\n", colornum));
25159a64e1c5Smrg	color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]);
25169a64e1c5Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
2517d4fba8b9Smrg	sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x",
2518d4fba8b9Smrg		opcode,
2519d4fba8b9Smrg		(opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum,
25209a64e1c5Smrg		color.red,
25219a64e1c5Smrg		color.green,
25229a64e1c5Smrg		color.blue);
25239a64e1c5Smrg	unparseputc1(xw, ANSI_OSC);
25249a64e1c5Smrg	unparseputs(xw, buffer);
25259a64e1c5Smrg	unparseputc1(xw, final);
2526d4fba8b9Smrg	result = True;
25279a64e1c5Smrg    }
2528d4fba8b9Smrg    return result;
25299a64e1c5Smrg}
25309a64e1c5Smrg
2531fa3f02f3Smrgstatic void
2532fa3f02f3SmrggetColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep)
2533fa3f02f3Smrg{
2534fa3f02f3Smrg    if (getVisualInfo(xw)) {
2535fa3f02f3Smrg	*typep = (unsigned) xw->visInfo->class;
2536fa3f02f3Smrg	*sizep = (unsigned) xw->visInfo->colormap_size;
2537fa3f02f3Smrg    } else {
2538fa3f02f3Smrg	*typep = 0;
2539fa3f02f3Smrg	*sizep = 0;
2540fa3f02f3Smrg    }
25413367019cSmrg}
25423367019cSmrg
25433367019cSmrg#define MAX_COLORTABLE 4096
25443367019cSmrg
25453367019cSmrg/*
25463367019cSmrg * Make only one call to XQueryColors(), since it can be slow.
25473367019cSmrg */
25483367019cSmrgstatic Boolean
25493367019cSmrgloadColorTable(XtermWidget xw, unsigned length)
25503367019cSmrg{
25513367019cSmrg    Colormap cmap = xw->core.colormap;
25523367019cSmrg    TScreen *screen = TScreenOf(xw);
2553fa3f02f3Smrg    Boolean result = (screen->cmap_data != 0);
25543367019cSmrg
2555fa3f02f3Smrg    if (!result
25563367019cSmrg	&& length != 0
25573367019cSmrg	&& length < MAX_COLORTABLE) {
25583367019cSmrg	screen->cmap_data = TypeMallocN(XColor, (size_t) length);
2559037a25ddSmrg
25603367019cSmrg	if (screen->cmap_data != 0) {
2561037a25ddSmrg	    unsigned i;
2562d4fba8b9Smrg	    unsigned shift;
2563d4fba8b9Smrg
2564d4fba8b9Smrg	    if (getVisualInfo(xw))
2565d4fba8b9Smrg		shift = xw->rgb_shifts[2];
2566d4fba8b9Smrg	    else
2567d4fba8b9Smrg		shift = 0;
2568037a25ddSmrg
25693367019cSmrg	    screen->cmap_size = length;
25703367019cSmrg
25713367019cSmrg	    for (i = 0; i < screen->cmap_size; i++) {
2572d4fba8b9Smrg		screen->cmap_data[i].pixel = (unsigned long) i << shift;
25733367019cSmrg	    }
25743367019cSmrg	    result = (Boolean) (XQueryColors(screen->display,
25753367019cSmrg					     cmap,
25763367019cSmrg					     screen->cmap_data,
25773367019cSmrg					     (int) screen->cmap_size) != 0);
25783367019cSmrg	}
25793367019cSmrg    }
2580d522f475Smrg    return result;
2581d522f475Smrg}
2582d522f475Smrg
2583d522f475Smrg/*
2584d522f475Smrg * Find closest color for "def" in "cmap".
2585d522f475Smrg * Set "def" to the resulting color.
2586d522f475Smrg *
2587d522f475Smrg * Based on Monish Shah's "find_closest_color()" for Vim 6.0,
2588d522f475Smrg * modified with ideas from David Tong's "noflash" library.
2589d522f475Smrg * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk.
2590d522f475Smrg *
2591d522f475Smrg * Return False if not able to find or allocate a color.
2592d522f475Smrg */
2593d522f475Smrgstatic Boolean
25949a64e1c5SmrgallocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def)
2595d522f475Smrg{
25963367019cSmrg    TScreen *screen = TScreenOf(xw);
2597d522f475Smrg    Boolean result = False;
25983367019cSmrg    unsigned cmap_type;
2599d522f475Smrg    unsigned cmap_size;
2600d522f475Smrg
2601fa3f02f3Smrg    getColormapInfo(xw, &cmap_type, &cmap_size);
2602d522f475Smrg
26033367019cSmrg    if ((cmap_type & 1) != 0) {
26043367019cSmrg
26053367019cSmrg	if (loadColorTable(xw, cmap_size)) {
2606037a25ddSmrg	    char *tried = TypeCallocN(char, (size_t) cmap_size);
2607d522f475Smrg
2608d522f475Smrg	    if (tried != 0) {
2609037a25ddSmrg		unsigned attempts;
2610d522f475Smrg
2611d522f475Smrg		/*
2612d522f475Smrg		 * Try (possibly each entry in the color map) to find the best
2613d522f475Smrg		 * approximation to the requested color.
2614d522f475Smrg		 */
2615d522f475Smrg		for (attempts = 0; attempts < cmap_size; attempts++) {
2616d522f475Smrg		    Boolean first = True;
2617037a25ddSmrg		    double bestRGB = 0.0;
2618037a25ddSmrg		    unsigned bestInx = 0;
2619037a25ddSmrg		    unsigned i;
2620d522f475Smrg
2621d522f475Smrg		    for (i = 0; i < cmap_size; i++) {
2622d522f475Smrg			if (!tried[bestInx]) {
2623037a25ddSmrg			    double diff, thisRGB = 0.0;
2624037a25ddSmrg
2625d522f475Smrg			    /*
2626d522f475Smrg			     * Look for the best match based on luminance.
2627d522f475Smrg			     * Measure this by the least-squares difference of
2628d522f475Smrg			     * the weighted R/G/B components from the color map
2629d522f475Smrg			     * versus the requested color.  Use the Y (luma)
2630d522f475Smrg			     * component of the YIQ color space model for
2631d522f475Smrg			     * weights that correspond to the luminance.
2632d522f475Smrg			     */
2633d522f475Smrg#define AddColorWeight(weight, color) \
26343367019cSmrg			    diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \
2635037a25ddSmrg			    thisRGB += diff * diff
2636d522f475Smrg
2637d522f475Smrg			    AddColorWeight(0.30, red);
2638d522f475Smrg			    AddColorWeight(0.61, green);
2639d522f475Smrg			    AddColorWeight(0.11, blue);
2640d522f475Smrg
2641d522f475Smrg			    if (first || (thisRGB < bestRGB)) {
2642d522f475Smrg				first = False;
2643d522f475Smrg				bestInx = i;
2644d522f475Smrg				bestRGB = thisRGB;
2645d522f475Smrg			    }
2646d522f475Smrg			}
2647d522f475Smrg		    }
26483367019cSmrg		    if (XAllocColor(screen->display, cmap,
26493367019cSmrg				    &screen->cmap_data[bestInx]) != 0) {
26503367019cSmrg			*def = screen->cmap_data[bestInx];
26513367019cSmrg			TRACE(("...closest %x/%x/%x\n", def->red,
26523367019cSmrg			       def->green, def->blue));
2653d522f475Smrg			result = True;
2654d522f475Smrg			break;
2655d522f475Smrg		    }
2656d522f475Smrg		    /*
2657d522f475Smrg		     * It failed - either the color map entry was readonly, or
2658d522f475Smrg		     * another client has allocated the entry.  Mark the entry
2659d522f475Smrg		     * so we will ignore it
2660d522f475Smrg		     */
2661d522f475Smrg		    tried[bestInx] = True;
2662d522f475Smrg		}
2663d522f475Smrg		free(tried);
2664d522f475Smrg	    }
2665d522f475Smrg	}
2666d522f475Smrg    }
2667d522f475Smrg    return result;
2668d522f475Smrg}
2669d522f475Smrg
26703367019cSmrg#ifndef ULONG_MAX
26713367019cSmrg#define ULONG_MAX (unsigned long)(~(0L))
26723367019cSmrg#endif
26733367019cSmrg
26743367019cSmrg#define CheckColor(result, value) \
26753367019cSmrg	    result = 0; \
26763367019cSmrg	    if (value.red) \
26773367019cSmrg		result |= 1; \
26783367019cSmrg	    if (value.green) \
26793367019cSmrg		result |= 2; \
26803367019cSmrg	    if (value.blue) \
26813367019cSmrg		result |= 4
26823367019cSmrg
26833367019cSmrg#define SelectColor(state, value, result) \
26843367019cSmrg	switch (state) { \
26853367019cSmrg	default: \
26863367019cSmrg	case 1: \
26873367019cSmrg	    result = value.red; \
26883367019cSmrg	    break; \
26893367019cSmrg	case 2: \
26903367019cSmrg	    result = value.green; \
26913367019cSmrg	    break; \
26923367019cSmrg	case 4: \
26933367019cSmrg	    result = value.blue; \
26943367019cSmrg	    break; \
26953367019cSmrg	}
26963367019cSmrg
26973367019cSmrg/*
26983367019cSmrg * Check if the color map consists of values in exactly one of the red, green
26993367019cSmrg * or blue columns.  If it is not, we do not know how to use it for the exact
27003367019cSmrg * match.
27013367019cSmrg */
27023367019cSmrgstatic int
27039a64e1c5SmrgsimpleColors(XColor *colortable, unsigned length)
27043367019cSmrg{
27053367019cSmrg    unsigned n;
2706fa3f02f3Smrg    int state = 0;
27073367019cSmrg    int check;
27083367019cSmrg
27093367019cSmrg    for (n = 0; n < length; ++n) {
27103367019cSmrg	if (state > 0) {
27113367019cSmrg	    CheckColor(check, colortable[n]);
27123367019cSmrg	    if (check > 0 && check != state) {
27133367019cSmrg		state = 0;
27143367019cSmrg		break;
27153367019cSmrg	    }
2716fa3f02f3Smrg	} else {
2717fa3f02f3Smrg	    CheckColor(state, colortable[n]);
27183367019cSmrg	}
27193367019cSmrg    }
27203367019cSmrg    switch (state) {
27213367019cSmrg    case 1:
27223367019cSmrg    case 2:
27233367019cSmrg    case 4:
27243367019cSmrg	break;
27253367019cSmrg    default:
27263367019cSmrg	state = 0;
27273367019cSmrg	break;
27283367019cSmrg    }
27293367019cSmrg    return state;
27303367019cSmrg}
27313367019cSmrg
2732fa3f02f3Smrg/*
2733fa3f02f3Smrg * Shift the mask left or right to put its most significant bit at the 16-bit
2734fa3f02f3Smrg * mark.
2735fa3f02f3Smrg */
2736fa3f02f3Smrgstatic unsigned
2737fa3f02f3SmrgnormalizeMask(unsigned mask)
2738fa3f02f3Smrg{
2739fa3f02f3Smrg    while (mask < 0x8000) {
2740fa3f02f3Smrg	mask <<= 1;
2741fa3f02f3Smrg    }
2742fa3f02f3Smrg    while (mask >= 0x10000) {
2743fa3f02f3Smrg	mask >>= 1;
2744fa3f02f3Smrg    }
2745fa3f02f3Smrg    return mask;
2746fa3f02f3Smrg}
2747fa3f02f3Smrg
27483367019cSmrgstatic unsigned
27499a64e1c5SmrgsearchColors(XColor *colortable, unsigned mask, unsigned length, unsigned
2750fa3f02f3Smrg	     color, int state)
27513367019cSmrg{
27523367019cSmrg    unsigned result = 0;
27533367019cSmrg    unsigned n;
27543367019cSmrg    unsigned long best = ULONG_MAX;
27553367019cSmrg    unsigned value;
27563367019cSmrg
2757fa3f02f3Smrg    mask = normalizeMask(mask);
27583367019cSmrg    for (n = 0; n < length; ++n) {
2759037a25ddSmrg	unsigned long diff;
2760037a25ddSmrg
27613367019cSmrg	SelectColor(state, colortable[n], value);
2762fa3f02f3Smrg	diff = ((color & mask) - (value & mask));
27633367019cSmrg	diff *= diff;
27643367019cSmrg	if (diff < best) {
27653367019cSmrg#if 0
27663367019cSmrg	    TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n",
27673367019cSmrg		   n, color,
27683367019cSmrg		   colortable[n].red,
27693367019cSmrg		   colortable[n].green,
27703367019cSmrg		   colortable[n].blue,
27713367019cSmrg		   diff));
27723367019cSmrg#endif
27733367019cSmrg	    result = n;
27743367019cSmrg	    best = diff;
27753367019cSmrg	}
27763367019cSmrg    }
27773367019cSmrg    SelectColor(state, colortable[result], value);
27783367019cSmrg    return value;
27793367019cSmrg}
27803367019cSmrg
27813367019cSmrg/*
27823367019cSmrg * This is a workaround for a longstanding defect in the X libraries.
27833367019cSmrg *
27843367019cSmrg * According to
27853367019cSmrg * http://www.unix.com/man-page/all/3x/XAllocColoA/
27863367019cSmrg *
27873367019cSmrg *     XAllocColor() acts differently on static and dynamic visuals.  On Pseu-
27883367019cSmrg *     doColor, DirectColor, and GrayScale  visuals,  XAllocColor()  fails  if
27893367019cSmrg *     there  are  no  unallocated  colorcells and no allocated read-only cell
27903367019cSmrg *     exactly matches the requested RGB values.  On  StaticColor,  TrueColor,
27913367019cSmrg *     and  StaticGray  visuals,  XAllocColor() returns the closest RGB values
27923367019cSmrg *     available in the colormap.  The colorcell_in_out structure returns  the
27933367019cSmrg *     actual RGB values allocated.
27943367019cSmrg *
27953367019cSmrg * That is, XAllocColor() should suffice unless the color map is full.  In that
2796fa3f02f3Smrg * case, allocateClosestRGB() is useful for the dynamic display classes such as
27973367019cSmrg * PseudoColor.  It is not useful for TrueColor, since XQueryColors() does not
27983367019cSmrg * return regular RGB triples (unless a different scheme was used for
27993367019cSmrg * specifying the pixel values); only the blue value is filled in.  However, it
28003367019cSmrg * is filled in with the colors that the server supports.
28013367019cSmrg *
28023367019cSmrg * Also (the reason for this function), XAllocColor() does not really work as
28033367019cSmrg * described.  For some TrueColor configurations it merely returns a close
28043367019cSmrg * approximation, but not the closest.
28053367019cSmrg */
28063367019cSmrgstatic Boolean
28079a64e1c5SmrgallocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def)
28083367019cSmrg{
28093367019cSmrg    XColor save = *def;
28103367019cSmrg    TScreen *screen = TScreenOf(xw);
28113367019cSmrg    Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0);
28123367019cSmrg
28133367019cSmrg    /*
2814fa3f02f3Smrg     * If this is a statically allocated display with too many items to store
2815fa3f02f3Smrg     * in our array, i.e., TrueColor, see if we can improve on the result by
2816fa3f02f3Smrg     * using the color values actually supported by the server.
28173367019cSmrg     */
28183367019cSmrg    if (result) {
28193367019cSmrg	unsigned cmap_type;
28203367019cSmrg	unsigned cmap_size;
28213367019cSmrg
2822fa3f02f3Smrg	getColormapInfo(xw, &cmap_type, &cmap_size);
28233367019cSmrg
2824fa3f02f3Smrg	if (cmap_type == TrueColor) {
28253367019cSmrg	    XColor temp = *def;
2826037a25ddSmrg	    int state;
28273367019cSmrg
28283367019cSmrg	    if (loadColorTable(xw, cmap_size)
28293367019cSmrg		&& (state = simpleColors(screen->cmap_data, cmap_size)) > 0) {
2830fa3f02f3Smrg#define SearchColors(which) \
2831fa3f02f3Smrg	temp.which = (unsigned short) searchColors(screen->cmap_data, \
2832fa3f02f3Smrg						   (unsigned) xw->visInfo->which##_mask,\
2833fa3f02f3Smrg						   cmap_size, \
2834fa3f02f3Smrg						   save.which, \
2835fa3f02f3Smrg						   state)
28363367019cSmrg		SearchColors(red);
28373367019cSmrg		SearchColors(green);
28383367019cSmrg		SearchColors(blue);
28393367019cSmrg		if (XAllocColor(screen->display, cmap, &temp) != 0) {
28403367019cSmrg#if OPT_TRACE
28413367019cSmrg		    if (temp.red != save.red
28423367019cSmrg			|| temp.green != save.green
28433367019cSmrg			|| temp.blue != save.blue) {
28443367019cSmrg			TRACE(("...improved %x/%x/%x ->%x/%x/%x\n",
28453367019cSmrg			       save.red, save.green, save.blue,
28463367019cSmrg			       temp.red, temp.green, temp.blue));
28473367019cSmrg		    } else {
28483367019cSmrg			TRACE(("...no improvement for %x/%x/%x\n",
28493367019cSmrg			       save.red, save.green, save.blue));
28503367019cSmrg		    }
28513367019cSmrg#endif
28523367019cSmrg		    *def = temp;
28533367019cSmrg		}
28543367019cSmrg	    }
28553367019cSmrg	}
28563367019cSmrg    }
28573367019cSmrg
28583367019cSmrg    return result;
28593367019cSmrg}
28603367019cSmrg
2861d522f475Smrg/*
2862d522f475Smrg * Allocate a color for the "ANSI" colors.  That actually includes colors up
2863d522f475Smrg * to 256.
2864d522f475Smrg *
2865d522f475Smrg * Returns
2866d522f475Smrg *	-1 on error
2867d522f475Smrg *	0 on no change
2868d522f475Smrg *	1 if a new color was allocated.
2869d522f475Smrg */
2870d522f475Smrgstatic int
2871d522f475SmrgAllocateAnsiColor(XtermWidget xw,
2872d522f475Smrg		  ColorRes * res,
2873cd3331d0Smrg		  const char *spec)
2874d522f475Smrg{
2875d522f475Smrg    int result;
2876d522f475Smrg    XColor def;
2877d522f475Smrg
28783367019cSmrg    if (xtermAllocColor(xw, &def, spec)) {
2879d522f475Smrg	if (
2880d522f475Smrg#if OPT_COLOR_RES
2881d522f475Smrg	       res->mode == True &&
2882d522f475Smrg#endif
2883d522f475Smrg	       EQL_COLOR_RES(res, def.pixel)) {
2884d522f475Smrg	    result = 0;
2885d522f475Smrg	} else {
2886d522f475Smrg	    result = 1;
2887d522f475Smrg	    SET_COLOR_RES(res, def.pixel);
28883367019cSmrg	    res->red = def.red;
28893367019cSmrg	    res->green = def.green;
28903367019cSmrg	    res->blue = def.blue;
28913367019cSmrg	    TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n",
28923367019cSmrg		   (int) (res - TScreenOf(xw)->Acolors), spec,
28933367019cSmrg		   def.red,
28943367019cSmrg		   def.green,
28953367019cSmrg		   def.blue,
28963367019cSmrg		   def.pixel));
2897d522f475Smrg#if OPT_COLOR_RES
2898d522f475Smrg	    if (!res->mode)
2899d522f475Smrg		result = 0;
2900d522f475Smrg	    res->mode = True;
2901d522f475Smrg#endif
2902d522f475Smrg	}
2903d522f475Smrg    } else {
2904d522f475Smrg	TRACE(("AllocateAnsiColor %s (failed)\n", spec));
2905d522f475Smrg	result = -1;
2906d522f475Smrg    }
2907d522f475Smrg    return (result);
2908d522f475Smrg}
2909d522f475Smrg
2910d522f475Smrg#if OPT_COLOR_RES
2911d522f475SmrgPixel
2912cd3331d0SmrgxtermGetColorRes(XtermWidget xw, ColorRes * res)
2913d522f475Smrg{
2914d522f475Smrg    Pixel result = 0;
2915d522f475Smrg
2916d522f475Smrg    if (res->mode) {
2917d522f475Smrg	result = res->value;
2918d522f475Smrg    } else {
2919d522f475Smrg	TRACE(("xtermGetColorRes for Acolors[%d]\n",
2920cd3331d0Smrg	       (int) (res - TScreenOf(xw)->Acolors)));
2921d522f475Smrg
2922cd3331d0Smrg	if (res >= TScreenOf(xw)->Acolors) {
2923cd3331d0Smrg	    assert(res - TScreenOf(xw)->Acolors < MAXCOLORS);
2924d522f475Smrg
2925cd3331d0Smrg	    if (AllocateAnsiColor(xw, res, res->resource) < 0) {
2926cd3331d0Smrg		res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value;
2927d522f475Smrg		res->mode = -True;
29283367019cSmrg		xtermWarning("Cannot allocate color \"%s\"\n",
29293367019cSmrg			     NonNull(res->resource));
2930d522f475Smrg	    }
2931d522f475Smrg	    result = res->value;
2932d522f475Smrg	} else {
2933d522f475Smrg	    result = 0;
2934d522f475Smrg	}
2935d522f475Smrg    }
2936d522f475Smrg    return result;
2937d522f475Smrg}
2938d522f475Smrg#endif
2939d522f475Smrg
2940cd3331d0Smrgstatic int
2941cd3331d0SmrgChangeOneAnsiColor(XtermWidget xw, int color, const char *name)
2942cd3331d0Smrg{
2943cd3331d0Smrg    int code;
2944cd3331d0Smrg
2945cd3331d0Smrg    if (color < 0 || color >= MAXCOLORS) {
2946cd3331d0Smrg	code = -1;
2947cd3331d0Smrg    } else {
2948cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color]);
2949cd3331d0Smrg
2950cd3331d0Smrg	TRACE(("ChangeAnsiColor for Acolors[%d]\n", color));
2951cd3331d0Smrg	code = AllocateAnsiColor(xw, res, name);
2952cd3331d0Smrg    }
2953cd3331d0Smrg    return code;
2954cd3331d0Smrg}
2955cd3331d0Smrg
2956cd3331d0Smrg/*
2957cd3331d0Smrg * Set or query entries in the Acolors[] array by parsing pairs of color/name
2958cd3331d0Smrg * values from the given buffer.
2959cd3331d0Smrg *
2960cd3331d0Smrg * The color can be any legal index into Acolors[], which consists of the
2961cd3331d0Smrg * 16/88/256 "ANSI" colors, followed by special color values for the various
2962cd3331d0Smrg * colorXX resources.  The indices for the special color values are not
2963cd3331d0Smrg * simple to work with, so an alternative is to use the calls which pass in
2964cd3331d0Smrg * 'first' set to the beginning of those indices.
2965cd3331d0Smrg *
2966cd3331d0Smrg * If the name is "?", report to the host the current value for the color.
2967cd3331d0Smrg */
2968d522f475Smrgstatic Bool
2969d522f475SmrgChangeAnsiColorRequest(XtermWidget xw,
2970d4fba8b9Smrg		       int opcode,
2971d522f475Smrg		       char *buf,
2972cd3331d0Smrg		       int first,
2973d522f475Smrg		       int final)
2974d522f475Smrg{
2975d522f475Smrg    int repaint = False;
2976d522f475Smrg    int code;
2977cd3331d0Smrg    int last = (MAXCOLORS - first);
2978d4fba8b9Smrg    int queried = 0;
2979d522f475Smrg
2980d522f475Smrg    TRACE(("ChangeAnsiColorRequest string='%s'\n", buf));
2981d522f475Smrg
2982d522f475Smrg    while (buf && *buf) {
2983037a25ddSmrg	int color;
2984037a25ddSmrg	char *name = strchr(buf, ';');
2985037a25ddSmrg
2986d522f475Smrg	if (name == NULL)
2987d522f475Smrg	    break;
2988d522f475Smrg	*name = '\0';
2989d522f475Smrg	name++;
2990d522f475Smrg	color = atoi(buf);
2991cd3331d0Smrg	if (color < 0 || color >= last)
2992cd3331d0Smrg	    break;		/* quit on any error */
2993d522f475Smrg	buf = strchr(name, ';');
2994d522f475Smrg	if (buf) {
2995d522f475Smrg	    *buf = '\0';
2996d522f475Smrg	    buf++;
2997d522f475Smrg	}
2998cd3331d0Smrg	if (!strcmp(name, "?")) {
2999d4fba8b9Smrg	    if (ReportAnsiColorRequest(xw, opcode, color + first, final))
3000d4fba8b9Smrg		++queried;
3001cd3331d0Smrg	} else {
3002cd3331d0Smrg	    code = ChangeOneAnsiColor(xw, color + first, name);
3003d522f475Smrg	    if (code < 0) {
3004d522f475Smrg		/* stop on any error */
3005d522f475Smrg		break;
3006d522f475Smrg	    } else if (code > 0) {
3007d522f475Smrg		repaint = True;
3008d522f475Smrg	    }
3009d522f475Smrg	    /* FIXME:  free old color somehow?  We aren't for the other color
3010d522f475Smrg	     * change style (dynamic colors).
3011d522f475Smrg	     */
3012d522f475Smrg	}
3013d522f475Smrg    }
3014d4fba8b9Smrg    if (queried)
3015d4fba8b9Smrg	unparse_end(xw);
3016d522f475Smrg
3017d522f475Smrg    return (repaint);
3018d522f475Smrg}
3019cd3331d0Smrg
3020cd3331d0Smrgstatic Bool
3021cd3331d0SmrgResetOneAnsiColor(XtermWidget xw, int color, int start)
3022cd3331d0Smrg{
3023cd3331d0Smrg    Bool repaint = False;
3024cd3331d0Smrg    int last = MAXCOLORS - start;
3025cd3331d0Smrg
3026cd3331d0Smrg    if (color >= 0 && color < last) {
3027cd3331d0Smrg	ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]);
3028cd3331d0Smrg
3029cd3331d0Smrg	if (res->mode) {
3030cd3331d0Smrg	    /* a color has been allocated for this slot - test further... */
3031cd3331d0Smrg	    if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) {
3032cd3331d0Smrg		repaint = True;
3033cd3331d0Smrg	    }
3034cd3331d0Smrg	}
3035cd3331d0Smrg    }
3036cd3331d0Smrg    return repaint;
3037cd3331d0Smrg}
3038cd3331d0Smrg
3039cd3331d0Smrgint
3040cd3331d0SmrgResetAnsiColorRequest(XtermWidget xw, char *buf, int start)
3041cd3331d0Smrg{
3042cd3331d0Smrg    int repaint = 0;
3043cd3331d0Smrg    int color;
3044cd3331d0Smrg
3045cd3331d0Smrg    TRACE(("ResetAnsiColorRequest(%s)\n", buf));
3046cd3331d0Smrg    if (*buf != '\0') {
3047cd3331d0Smrg	/* reset specific colors */
3048cd3331d0Smrg	while (!IsEmpty(buf)) {
3049cd3331d0Smrg	    char *next;
3050cd3331d0Smrg
3051037a25ddSmrg	    color = (int) (strtol) (buf, &next, 10);
3052037a25ddSmrg	    if (!PartS2L(buf, next) || (color < 0))
3053cd3331d0Smrg		break;		/* no number at all */
3054cd3331d0Smrg	    if (next != 0) {
3055cd3331d0Smrg		if (strchr(";", *next) == 0)
3056cd3331d0Smrg		    break;	/* unexpected delimiter */
3057cd3331d0Smrg		++next;
3058cd3331d0Smrg	    }
3059cd3331d0Smrg
3060cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3061cd3331d0Smrg		++repaint;
3062cd3331d0Smrg	    }
3063cd3331d0Smrg	    buf = next;
3064cd3331d0Smrg	}
3065cd3331d0Smrg    } else {
3066cd3331d0Smrg	TRACE(("...resetting all %d colors\n", MAXCOLORS));
3067cd3331d0Smrg	for (color = 0; color < MAXCOLORS; ++color) {
3068cd3331d0Smrg	    if (ResetOneAnsiColor(xw, color, start)) {
3069cd3331d0Smrg		++repaint;
3070cd3331d0Smrg	    }
3071cd3331d0Smrg	}
3072cd3331d0Smrg    }
3073cd3331d0Smrg    TRACE(("...ResetAnsiColorRequest ->%d\n", repaint));
3074cd3331d0Smrg    return repaint;
3075cd3331d0Smrg}
3076d522f475Smrg#else
30773367019cSmrg#define allocateClosestRGB(xw, cmap, def) 0
30783367019cSmrg#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def)
3079d522f475Smrg#endif /* OPT_ISO_COLORS */
3080d522f475Smrg
3081fa3f02f3SmrgBoolean
30829a64e1c5SmrgallocateBestRGB(XtermWidget xw, XColor *def)
3083fa3f02f3Smrg{
3084fa3f02f3Smrg    Colormap cmap = xw->core.colormap;
3085fa3f02f3Smrg
3086fa3f02f3Smrg    return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def);
3087fa3f02f3Smrg}
3088fa3f02f3Smrg
30893367019cSmrgstatic Boolean
30909a64e1c5SmrgxtermAllocColor(XtermWidget xw, XColor *def, const char *spec)
30913367019cSmrg{
30923367019cSmrg    Boolean result = False;
30933367019cSmrg    TScreen *screen = TScreenOf(xw);
30943367019cSmrg    Colormap cmap = xw->core.colormap;
30953367019cSmrg
3096fa3f02f3Smrg    if (XParseColor(screen->display, cmap, spec, def)) {
3097fa3f02f3Smrg	XColor save_def = *def;
3098fa3f02f3Smrg	if (resource.reportColors) {
3099fa3f02f3Smrg	    printf("color  %04x/%04x/%04x = \"%s\"\n",
3100fa3f02f3Smrg		   def->red, def->green, def->blue,
3101fa3f02f3Smrg		   spec);
3102fa3f02f3Smrg	}
3103fa3f02f3Smrg	if (allocateBestRGB(xw, def)) {
3104fa3f02f3Smrg	    if (resource.reportColors) {
3105fa3f02f3Smrg		if (def->red != save_def.red ||
3106fa3f02f3Smrg		    def->green != save_def.green ||
3107fa3f02f3Smrg		    def->blue != save_def.blue) {
3108fa3f02f3Smrg		    printf("color  %04x/%04x/%04x ~ \"%s\"\n",
3109fa3f02f3Smrg			   def->red, def->green, def->blue,
3110fa3f02f3Smrg			   spec);
3111fa3f02f3Smrg		}
3112fa3f02f3Smrg	    }
3113fa3f02f3Smrg	    TRACE(("xtermAllocColor -> %x/%x/%x\n",
3114fa3f02f3Smrg		   def->red, def->green, def->blue));
3115fa3f02f3Smrg	    result = True;
3116fa3f02f3Smrg	}
31173367019cSmrg    }
31183367019cSmrg    return result;
31193367019cSmrg}
31203367019cSmrg
31213367019cSmrg/*
31223367019cSmrg * This provides an approximation (the closest color from xterm's palette)
31233367019cSmrg * rather than the "exact" color (whatever the display could provide, actually)
31243367019cSmrg * because of the context in which it is used.
31253367019cSmrg */
31263367019cSmrg#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given))
31273367019cSmrgint
31283367019cSmrgxtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue)
31293367019cSmrg{
31303367019cSmrg    int result = -1;
31313367019cSmrg#if OPT_COLOR_RES && OPT_ISO_COLORS
31323367019cSmrg    int n;
31333367019cSmrg    int best_index = -1;
31343367019cSmrg    unsigned long best_value = 0;
31353367019cSmrg    unsigned long this_value;
31363367019cSmrg    long diff_red, diff_green, diff_blue;
31373367019cSmrg
31383367019cSmrg    TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue));
31393367019cSmrg
31403367019cSmrg    for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) {
31413367019cSmrg	ColorRes *res = &(TScreenOf(xw)->Acolors[n]);
31423367019cSmrg
31433367019cSmrg	/* ensure that we have a value for each of the colors */
31443367019cSmrg	if (!res->mode) {
31453367019cSmrg	    (void) AllocateAnsiColor(xw, res, res->resource);
31463367019cSmrg	}
31473367019cSmrg
31483367019cSmrg	/* find the closest match */
31493367019cSmrg	if (res->mode == True) {
31503367019cSmrg	    TRACE2(("...lookup %lx -> %x/%x/%x\n",
31513367019cSmrg		    res->value, res->red, res->green, res->blue));
31523367019cSmrg	    diff_red = ColorDiff(find_red, res->red);
31533367019cSmrg	    diff_green = ColorDiff(find_green, res->green);
31543367019cSmrg	    diff_blue = ColorDiff(find_blue, res->blue);
31553367019cSmrg	    this_value = (unsigned long) ((diff_red * diff_red)
31563367019cSmrg					  + (diff_green * diff_green)
31573367019cSmrg					  + (diff_blue * diff_blue));
31583367019cSmrg	    if (best_index < 0 || this_value < best_value) {
31593367019cSmrg		best_index = n;
31603367019cSmrg		best_value = this_value;
31613367019cSmrg	    }
31623367019cSmrg	}
31633367019cSmrg    }
31643367019cSmrg    TRACE(("...best match at %d with diff %lx\n", best_index, best_value));
31653367019cSmrg    result = best_index;
31663367019cSmrg#else
31673367019cSmrg    (void) xw;
31683367019cSmrg    (void) find_red;
31693367019cSmrg    (void) find_green;
31703367019cSmrg    (void) find_blue;
31713367019cSmrg#endif
31723367019cSmrg    return result;
31733367019cSmrg}
31743367019cSmrg
3175d4fba8b9Smrg#if OPT_DIRECT_COLOR
3176d4fba8b9Smrgint
3177d4fba8b9SmrggetDirectColor(XtermWidget xw, int red, int green, int blue)
3178d4fba8b9Smrg{
3179d4fba8b9Smrg#define nRGB(name,shift) \
3180d4fba8b9Smrg	((unsigned long)(name << xw->rgb_shifts[shift]) \
3181d4fba8b9Smrg		         & xw->visInfo->name ##_mask)
3182d4fba8b9Smrg    MyPixel result = (MyPixel) (nRGB(red, 0) | nRGB(green, 1) | nRGB(blue, 2));
3183d4fba8b9Smrg    return (int) result;
3184d4fba8b9Smrg}
3185d4fba8b9Smrg
3186d4fba8b9Smrgstatic void
3187d4fba8b9SmrgformatDirectColor(char *target, XtermWidget xw, unsigned value)
3188d4fba8b9Smrg{
3189d4fba8b9Smrg#define fRGB(name, shift) \
3190d4fba8b9Smrg	(value & xw->visInfo->name ## _mask) >> xw->rgb_shifts[shift]
3191d4fba8b9Smrg    sprintf(target, "%lu:%lu:%lu", fRGB(red, 0), fRGB(green, 1), fRGB(blue, 2));
3192d4fba8b9Smrg}
3193d4fba8b9Smrg#endif /* OPT_DIRECT_COLOR */
3194d4fba8b9Smrg
3195d4fba8b9Smrg#define fg2SGR(n) \
3196d4fba8b9Smrg		(n) >= 8 ? 9 : 3, \
3197d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3198d4fba8b9Smrg#define bg2SGR(n) \
3199d4fba8b9Smrg		(n) >= 8 ? 10 : 4, \
3200d4fba8b9Smrg		(n) >= 8 ? (n) - 8 : (n)
3201d4fba8b9Smrg
3202d4fba8b9Smrg#define EndOf(s) (s) + strlen(s)
3203d4fba8b9Smrg
3204d4fba8b9Smrgchar *
3205d4fba8b9SmrgxtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg)
3206d4fba8b9Smrg{
3207d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
3208d4fba8b9Smrg    char *msg = target;
3209d4fba8b9Smrg
3210d4fba8b9Smrg    strcpy(target, "0");
3211d4fba8b9Smrg    if (attr & BOLD)
3212d4fba8b9Smrg	strcat(msg, ";1");
3213d4fba8b9Smrg    if (attr & UNDERLINE)
3214d4fba8b9Smrg	strcat(msg, ";4");
3215d4fba8b9Smrg    if (attr & BLINK)
3216d4fba8b9Smrg	strcat(msg, ";5");
3217d4fba8b9Smrg    if (attr & INVERSE)
3218d4fba8b9Smrg	strcat(msg, ";7");
3219d4fba8b9Smrg    if (attr & INVISIBLE)
3220d4fba8b9Smrg	strcat(msg, ";8");
3221d4fba8b9Smrg#if OPT_WIDE_ATTRS
3222d4fba8b9Smrg    if (attr & ATR_FAINT)
3223d4fba8b9Smrg	strcat(msg, ";2");
3224d4fba8b9Smrg    if (attr & ATR_ITALIC)
3225d4fba8b9Smrg	strcat(msg, ";3");
3226d4fba8b9Smrg    if (attr & ATR_STRIKEOUT)
3227d4fba8b9Smrg	strcat(msg, ";9");
3228d4fba8b9Smrg    if (attr & ATR_DBL_UNDER)
3229d4fba8b9Smrg	strcat(msg, ";21");
3230d4fba8b9Smrg#endif
3231d4fba8b9Smrg#if OPT_256_COLORS || OPT_88_COLORS
3232d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3233d4fba8b9Smrg	if (attr & FG_COLOR) {
3234d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), {
3235d4fba8b9Smrg		strcat(msg, ";38:2::");
3236d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) fg);
3237d4fba8b9Smrg	    }) if (fg >= 16) {
3238d4fba8b9Smrg		sprintf(EndOf(msg), ";38:5:%d", fg);
3239d4fba8b9Smrg	    } else {
3240d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3241d4fba8b9Smrg	    }
3242d4fba8b9Smrg	}
3243d4fba8b9Smrg	if (attr & BG_COLOR) {
3244d4fba8b9Smrg	    if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), {
3245d4fba8b9Smrg		strcat(msg, ";48:2::");
3246d4fba8b9Smrg		formatDirectColor(EndOf(msg), xw, (unsigned) bg);
3247d4fba8b9Smrg	    }) if (bg >= 16) {
3248d4fba8b9Smrg		sprintf(EndOf(msg), ";48:5:%d", bg);
3249d4fba8b9Smrg	    } else {
3250d4fba8b9Smrg		sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3251d4fba8b9Smrg	    }
3252d4fba8b9Smrg	}
3253d4fba8b9Smrg    });
3254d4fba8b9Smrg#elif OPT_ISO_COLORS
3255d4fba8b9Smrg    if_OPT_ISO_COLORS(screen, {
3256d4fba8b9Smrg	if (attr & FG_COLOR) {
3257d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", fg2SGR(fg));
3258d4fba8b9Smrg	}
3259d4fba8b9Smrg	if (attr & BG_COLOR) {
3260d4fba8b9Smrg	    sprintf(EndOf(msg), ";%d%d", bg2SGR(bg));
3261d4fba8b9Smrg	}
3262d4fba8b9Smrg    });
3263d4fba8b9Smrg#else
3264d4fba8b9Smrg    (void) screen;
3265d4fba8b9Smrg    (void) fg;
3266d4fba8b9Smrg    (void) bg;
3267d4fba8b9Smrg#endif
3268d4fba8b9Smrg    return target;
3269d4fba8b9Smrg}
3270d4fba8b9Smrg
3271d522f475Smrg#if OPT_PASTE64
3272d522f475Smrgstatic void
3273fa3f02f3SmrgManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final)
3274d522f475Smrg{
3275d522f475Smrg#define PDATA(a,b) { a, #b }
3276d522f475Smrg    static struct {
3277d522f475Smrg	char given;
3278cd3331d0Smrg	String result;
3279d522f475Smrg    } table[] = {
3280d522f475Smrg	PDATA('s', SELECT),
3281d522f475Smrg	    PDATA('p', PRIMARY),
3282d4fba8b9Smrg	    PDATA('q', SECONDARY),
3283d522f475Smrg	    PDATA('c', CLIPBOARD),
3284d522f475Smrg	    PDATA('0', CUT_BUFFER0),
3285d522f475Smrg	    PDATA('1', CUT_BUFFER1),
3286d522f475Smrg	    PDATA('2', CUT_BUFFER2),
3287d522f475Smrg	    PDATA('3', CUT_BUFFER3),
3288d522f475Smrg	    PDATA('4', CUT_BUFFER4),
3289d522f475Smrg	    PDATA('5', CUT_BUFFER5),
3290d522f475Smrg	    PDATA('6', CUT_BUFFER6),
3291d522f475Smrg	    PDATA('7', CUT_BUFFER7),
3292d522f475Smrg    };
3293d522f475Smrg
3294cd3331d0Smrg    const char *base = buf;
3295d522f475Smrg    Cardinal j, n = 0;
3296d522f475Smrg
3297d522f475Smrg    TRACE(("Manipulate selection data\n"));
3298d522f475Smrg
3299d522f475Smrg    while (*buf != ';' && *buf != '\0') {
3300d522f475Smrg	++buf;
3301d522f475Smrg    }
3302d522f475Smrg
3303d522f475Smrg    if (*buf == ';') {
3304037a25ddSmrg	char *used;
3305037a25ddSmrg
3306d522f475Smrg	*buf++ = '\0';
3307d522f475Smrg
3308d522f475Smrg	if (*base == '\0')
3309d522f475Smrg	    base = "s0";
3310d522f475Smrg
33113367019cSmrg	if ((used = x_strdup(base)) != 0) {
3312037a25ddSmrg	    String *select_args;
3313037a25ddSmrg
33143367019cSmrg	    if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) {
33153367019cSmrg		while (*base != '\0') {
33163367019cSmrg		    for (j = 0; j < XtNumber(table); ++j) {
33173367019cSmrg			if (*base == table[j].given) {
33183367019cSmrg			    used[n] = *base;
33193367019cSmrg			    select_args[n++] = table[j].result;
33203367019cSmrg			    TRACE(("atom[%d] %s\n", n, table[j].result));
33213367019cSmrg			    break;
33223367019cSmrg			}
33233367019cSmrg		    }
33243367019cSmrg		    ++base;
33253367019cSmrg		}
33263367019cSmrg		used[n] = 0;
33273367019cSmrg
33283367019cSmrg		if (!strcmp(buf, "?")) {
33293367019cSmrg		    if (AllowWindowOps(xw, ewGetSelection)) {
33303367019cSmrg			TRACE(("Getting selection\n"));
33313367019cSmrg			unparseputc1(xw, ANSI_OSC);
33323367019cSmrg			unparseputs(xw, "52");
33333367019cSmrg			unparseputc(xw, ';');
33343367019cSmrg
33353367019cSmrg			unparseputs(xw, used);
33363367019cSmrg			unparseputc(xw, ';');
33373367019cSmrg
33383367019cSmrg			/* Tell xtermGetSelection data is base64 encoded */
33393367019cSmrg			screen->base64_paste = n;
33403367019cSmrg			screen->base64_final = final;
33413367019cSmrg
3342dfb07bc7Smrg			screen->selection_time =
3343dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3344dfb07bc7Smrg
33453367019cSmrg			/* terminator will be written in this call */
33463367019cSmrg			xtermGetSelection((Widget) xw,
3347dfb07bc7Smrg					  screen->selection_time,
33483367019cSmrg					  select_args, n,
33493367019cSmrg					  NULL);
335094644356Smrg			/*
335194644356Smrg			 * select_args is used via SelectionReceived, cannot
335294644356Smrg			 * free it here.
335394644356Smrg			 */
335494644356Smrg		    } else {
335594644356Smrg			free(select_args);
33563367019cSmrg		    }
33573367019cSmrg		} else {
33583367019cSmrg		    if (AllowWindowOps(xw, ewSetSelection)) {
3359d4fba8b9Smrg			char *old = buf;
3360d4fba8b9Smrg
3361d4fba8b9Smrg			TRACE(("Setting selection(%s) with %s\n", used, buf));
3362dfb07bc7Smrg			screen->selection_time =
3363dfb07bc7Smrg			    XtLastTimestampProcessed(TScreenOf(xw)->display);
3364d4fba8b9Smrg
3365d4fba8b9Smrg			for (j = 0; j < n; ++j) {
3366d4fba8b9Smrg			    buf = old;
3367d4fba8b9Smrg			    ClearSelectionBuffer(screen, select_args[j]);
3368d4fba8b9Smrg			    while (*buf != '\0') {
3369d4fba8b9Smrg				AppendToSelectionBuffer(screen,
3370d4fba8b9Smrg							CharOf(*buf++),
3371d4fba8b9Smrg							select_args[j]);
3372d4fba8b9Smrg			    }
3373d4fba8b9Smrg			}
33743367019cSmrg			CompleteSelection(xw, select_args, n);
33753367019cSmrg		    }
337694644356Smrg		    free(select_args);
33773367019cSmrg		}
3378cd3331d0Smrg	    }
33793367019cSmrg	    free(used);
3380d522f475Smrg	}
3381d522f475Smrg    }
3382d522f475Smrg}
3383d522f475Smrg#endif /* OPT_PASTE64 */
3384d522f475Smrg
3385d522f475Smrg/***====================================================================***/
3386d522f475Smrg
3387d4fba8b9Smrg#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \
3388d4fba8b9Smrg			 || (xw->screen.utf8_title) \
3389d4fba8b9Smrg			 || (xw->screen.c1_printable))
3390cd3331d0Smrg
3391d522f475Smrgstatic Bool
3392fa3f02f3SmrgxtermIsPrintable(XtermWidget xw, Char **bufp, Char *last)
3393d522f475Smrg{
3394cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3395d522f475Smrg    Bool result = False;
3396d522f475Smrg    Char *cp = *bufp;
3397d522f475Smrg    Char *next = cp;
3398d522f475Smrg
3399d522f475Smrg    (void) screen;
3400d522f475Smrg    (void) last;
3401d522f475Smrg
3402d522f475Smrg#if OPT_WIDE_CHARS
3403cd3331d0Smrg    if (xtermEnvUTF8() && IsSetUtf8Title(xw)) {
3404d522f475Smrg	PtyData data;
3405d522f475Smrg
34069a64e1c5Smrg	if (decodeUtf8(screen, fakePtyData(&data, cp, last))) {
3407d522f475Smrg	    if (data.utf_data != UCS_REPL
3408d522f475Smrg		&& (data.utf_data >= 128 ||
3409d522f475Smrg		    ansi_table[data.utf_data] == CASE_PRINT)) {
3410d522f475Smrg		next += (data.utf_size - 1);
3411d522f475Smrg		result = True;
3412d522f475Smrg	    } else {
3413d522f475Smrg		result = False;
3414d522f475Smrg	    }
3415d522f475Smrg	} else {
3416d522f475Smrg	    result = False;
3417d522f475Smrg	}
3418d522f475Smrg    } else
3419d522f475Smrg#endif
3420d522f475Smrg#if OPT_C1_PRINT
3421d522f475Smrg	if (screen->c1_printable
3422d522f475Smrg	    && (*cp >= 128 && *cp < 160)) {
3423d522f475Smrg	result = True;
3424d522f475Smrg    } else
3425d522f475Smrg#endif
3426d522f475Smrg    if (ansi_table[*cp] == CASE_PRINT) {
3427d522f475Smrg	result = True;
3428d522f475Smrg    }
3429d522f475Smrg    *bufp = next;
3430d522f475Smrg    return result;
3431d522f475Smrg}
3432d522f475Smrg
3433d522f475Smrg/***====================================================================***/
3434d522f475Smrg
3435d522f475Smrg/*
3436d522f475Smrg * Enum corresponding to the actual OSC codes rather than the internal
3437cd3331d0Smrg * array indices.  Compare with TermColors.
3438d522f475Smrg */
3439d522f475Smrgtypedef enum {
3440d522f475Smrg    OSC_TEXT_FG = 10
3441d522f475Smrg    ,OSC_TEXT_BG
3442d522f475Smrg    ,OSC_TEXT_CURSOR
3443d522f475Smrg    ,OSC_MOUSE_FG
3444d522f475Smrg    ,OSC_MOUSE_BG
3445d522f475Smrg#if OPT_TEK4014
3446d522f475Smrg    ,OSC_TEK_FG = 15
3447d522f475Smrg    ,OSC_TEK_BG
3448d522f475Smrg#endif
3449d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3450d522f475Smrg    ,OSC_HIGHLIGHT_BG = 17
3451d522f475Smrg#endif
3452d522f475Smrg#if OPT_TEK4014
3453d522f475Smrg    ,OSC_TEK_CURSOR = 18
3454d522f475Smrg#endif
3455d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3456d522f475Smrg    ,OSC_HIGHLIGHT_FG = 19
3457d522f475Smrg#endif
3458d522f475Smrg    ,OSC_NCOLORS
3459d522f475Smrg} OscTextColors;
3460d522f475Smrg
3461cd3331d0Smrg/*
3462cd3331d0Smrg * Map codes to OSC controls that can reset colors.
3463cd3331d0Smrg */
3464cd3331d0Smrg#define OSC_RESET 100
3465cd3331d0Smrg#define OSC_Reset(code) (code) + OSC_RESET
3466cd3331d0Smrg
3467d522f475Smrgstatic Bool
3468d522f475SmrgGetOldColors(XtermWidget xw)
3469d522f475Smrg{
34709a64e1c5Smrg    if (xw->work.oldColors == NULL) {
3471037a25ddSmrg	int i;
3472037a25ddSmrg
34739a64e1c5Smrg	xw->work.oldColors = TypeXtMalloc(ScrnColors);
34749a64e1c5Smrg	if (xw->work.oldColors == NULL) {
34753367019cSmrg	    xtermWarning("allocation failure in GetOldColors\n");
3476d522f475Smrg	    return (False);
3477d522f475Smrg	}
34789a64e1c5Smrg	xw->work.oldColors->which = 0;
3479d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
34809a64e1c5Smrg	    xw->work.oldColors->colors[i] = 0;
34819a64e1c5Smrg	    xw->work.oldColors->names[i] = NULL;
3482d522f475Smrg	}
34839a64e1c5Smrg	GetColors(xw, xw->work.oldColors);
3484d522f475Smrg    }
3485d522f475Smrg    return (True);
3486d522f475Smrg}
3487d522f475Smrg
3488d522f475Smrgstatic int
3489d4fba8b9SmrgoppositeColor(XtermWidget xw, int n)
3490d522f475Smrg{
3491d4fba8b9Smrg    Boolean reversed = (xw->misc.re_verse);
3492d4fba8b9Smrg
3493d522f475Smrg    switch (n) {
3494d522f475Smrg    case TEXT_FG:
3495d4fba8b9Smrg	n = reversed ? TEXT_FG : TEXT_BG;
3496d522f475Smrg	break;
3497d522f475Smrg    case TEXT_BG:
3498d4fba8b9Smrg	n = reversed ? TEXT_BG : TEXT_FG;
3499d522f475Smrg	break;
3500d522f475Smrg    case MOUSE_FG:
3501d522f475Smrg	n = MOUSE_BG;
3502d522f475Smrg	break;
3503d522f475Smrg    case MOUSE_BG:
3504d522f475Smrg	n = MOUSE_FG;
3505d522f475Smrg	break;
3506d522f475Smrg#if OPT_TEK4014
3507d522f475Smrg    case TEK_FG:
3508d4fba8b9Smrg	n = reversed ? TEK_FG : TEK_BG;
3509d522f475Smrg	break;
3510d522f475Smrg    case TEK_BG:
3511d4fba8b9Smrg	n = reversed ? TEK_BG : TEK_FG;
3512d522f475Smrg	break;
3513d522f475Smrg#endif
3514d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3515d522f475Smrg    case HIGHLIGHT_FG:
3516d522f475Smrg	n = HIGHLIGHT_BG;
3517d522f475Smrg	break;
3518d522f475Smrg    case HIGHLIGHT_BG:
3519d522f475Smrg	n = HIGHLIGHT_FG;
3520d522f475Smrg	break;
3521d522f475Smrg#endif
3522d522f475Smrg    default:
3523d522f475Smrg	break;
3524d522f475Smrg    }
3525d522f475Smrg    return n;
3526d522f475Smrg}
3527d522f475Smrg
3528d4fba8b9Smrgstatic Bool
3529d522f475SmrgReportColorRequest(XtermWidget xw, int ndx, int final)
3530d522f475Smrg{
3531d4fba8b9Smrg    Bool result = False;
3532d4fba8b9Smrg
3533cd3331d0Smrg    if (AllowColorOps(xw, ecGetColor)) {
3534cd3331d0Smrg	XColor color;
3535cd3331d0Smrg	Colormap cmap = xw->core.colormap;
3536cd3331d0Smrg	char buffer[80];
3537d522f475Smrg
3538cd3331d0Smrg	/*
3539cd3331d0Smrg	 * ChangeColorsRequest() has "always" chosen the opposite color when
3540cd3331d0Smrg	 * reverse-video is set.  Report this as the original color index, but
3541cd3331d0Smrg	 * reporting the opposite color which would be used.
3542cd3331d0Smrg	 */
3543d4fba8b9Smrg	int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx;
3544cd3331d0Smrg
3545cd3331d0Smrg	GetOldColors(xw);
35469a64e1c5Smrg	color.pixel = xw->work.oldColors->colors[ndx];
3547cd3331d0Smrg	XQueryColor(TScreenOf(xw)->display, cmap, &color);
3548cd3331d0Smrg	sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10,
3549cd3331d0Smrg		color.red,
3550cd3331d0Smrg		color.green,
3551cd3331d0Smrg		color.blue);
3552712a7ff4Smrg	TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n",
35539a64e1c5Smrg	       ndx, xw->work.oldColors->colors[ndx], buffer));
3554cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3555cd3331d0Smrg	unparseputs(xw, buffer);
3556cd3331d0Smrg	unparseputc1(xw, final);
3557d4fba8b9Smrg	result = True;
3558cd3331d0Smrg    }
3559d4fba8b9Smrg    return result;
3560d522f475Smrg}
3561d522f475Smrg
3562d522f475Smrgstatic Bool
3563d4fba8b9SmrgUpdateOldColors(XtermWidget xw, ScrnColors * pNew)
3564d522f475Smrg{
3565d522f475Smrg    int i;
3566d522f475Smrg
3567d522f475Smrg    /* if we were going to free old colors, this would be the place to
3568d522f475Smrg     * do it.   I've decided not to (for now), because it seems likely
3569d522f475Smrg     * that we'd have a small set of colors we use over and over, and that
3570d522f475Smrg     * we could save some overhead this way.   The only case in which this
3571d522f475Smrg     * (clearly) fails is if someone is trying a boatload of colors, in
3572d522f475Smrg     * which case they can restart xterm
3573d522f475Smrg     */
3574d522f475Smrg    for (i = 0; i < NCOLORS; i++) {
3575d522f475Smrg	if (COLOR_DEFINED(pNew, i)) {
35769a64e1c5Smrg	    if (xw->work.oldColors->names[i] != NULL) {
35779a64e1c5Smrg		XtFree(xw->work.oldColors->names[i]);
35789a64e1c5Smrg		xw->work.oldColors->names[i] = NULL;
3579d522f475Smrg	    }
3580d522f475Smrg	    if (pNew->names[i]) {
35819a64e1c5Smrg		xw->work.oldColors->names[i] = pNew->names[i];
3582d522f475Smrg	    }
35839a64e1c5Smrg	    xw->work.oldColors->colors[i] = pNew->colors[i];
3584d522f475Smrg	}
3585d522f475Smrg    }
3586d522f475Smrg    return (True);
3587d522f475Smrg}
3588d522f475Smrg
3589d522f475Smrg/*
3590d522f475Smrg * OSC codes are constant, but the indices for the color arrays depend on how
3591d522f475Smrg * xterm is compiled.
3592d522f475Smrg */
3593d522f475Smrgstatic int
3594d522f475SmrgOscToColorIndex(OscTextColors mode)
3595d522f475Smrg{
3596d522f475Smrg    int result = 0;
3597d522f475Smrg
3598d522f475Smrg#define CASE(name) case OSC_##name: result = name; break
3599d522f475Smrg    switch (mode) {
3600d522f475Smrg	CASE(TEXT_FG);
3601d522f475Smrg	CASE(TEXT_BG);
3602d522f475Smrg	CASE(TEXT_CURSOR);
3603d522f475Smrg	CASE(MOUSE_FG);
3604d522f475Smrg	CASE(MOUSE_BG);
3605d522f475Smrg#if OPT_TEK4014
3606d522f475Smrg	CASE(TEK_FG);
3607d522f475Smrg	CASE(TEK_BG);
3608d522f475Smrg#endif
3609d522f475Smrg#if OPT_HIGHLIGHT_COLOR
3610d522f475Smrg	CASE(HIGHLIGHT_BG);
3611d522f475Smrg	CASE(HIGHLIGHT_FG);
3612d522f475Smrg#endif
3613d522f475Smrg#if OPT_TEK4014
3614d522f475Smrg	CASE(TEK_CURSOR);
3615d522f475Smrg#endif
3616d522f475Smrg    case OSC_NCOLORS:
3617d522f475Smrg	break;
3618d522f475Smrg    }
3619d522f475Smrg    return result;
3620d522f475Smrg}
3621d522f475Smrg
3622d522f475Smrgstatic Bool
3623d522f475SmrgChangeColorsRequest(XtermWidget xw,
3624d522f475Smrg		    int start,
3625d522f475Smrg		    char *names,
3626d522f475Smrg		    int final)
3627d522f475Smrg{
3628d522f475Smrg    Bool result = False;
3629d522f475Smrg    ScrnColors newColors;
3630d522f475Smrg
3631d522f475Smrg    TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names));
3632d522f475Smrg
3633d522f475Smrg    if (GetOldColors(xw)) {
3634037a25ddSmrg	int i;
3635d4fba8b9Smrg	int queried = 0;
3636037a25ddSmrg
3637d522f475Smrg	newColors.which = 0;
3638d522f475Smrg	for (i = 0; i < NCOLORS; i++) {
3639d522f475Smrg	    newColors.names[i] = NULL;
3640d522f475Smrg	}
3641d522f475Smrg	for (i = start; i < OSC_NCOLORS; i++) {
3642037a25ddSmrg	    int ndx = OscToColorIndex((OscTextColors) i);
3643d522f475Smrg	    if (xw->misc.re_verse)
3644d4fba8b9Smrg		ndx = oppositeColor(xw, ndx);
3645d522f475Smrg
3646cd3331d0Smrg	    if (IsEmpty(names)) {
3647d522f475Smrg		newColors.names[ndx] = NULL;
3648d522f475Smrg	    } else {
3649037a25ddSmrg		char *thisName = ((names[0] == ';') ? NULL : names);
3650037a25ddSmrg
3651d522f475Smrg		names = strchr(names, ';');
3652d522f475Smrg		if (names != NULL) {
3653d522f475Smrg		    *names++ = '\0';
3654d522f475Smrg		}
3655fa3f02f3Smrg		if (thisName != 0) {
3656fa3f02f3Smrg		    if (!strcmp(thisName, "?")) {
3657d4fba8b9Smrg			if (ReportColorRequest(xw, ndx, final))
3658d4fba8b9Smrg			    ++queried;
36599a64e1c5Smrg		    } else if (!xw->work.oldColors->names[ndx]
36609a64e1c5Smrg			       || strcmp(thisName, xw->work.oldColors->names[ndx])) {
3661fa3f02f3Smrg			AllocateTermColor(xw, &newColors, ndx, thisName, False);
3662fa3f02f3Smrg		    }
3663d522f475Smrg		}
3664d522f475Smrg	    }
3665d522f475Smrg	}
3666d522f475Smrg
3667d522f475Smrg	if (newColors.which != 0) {
3668d522f475Smrg	    ChangeColors(xw, &newColors);
3669d522f475Smrg	    UpdateOldColors(xw, &newColors);
3670d4fba8b9Smrg	} else if (queried) {
3671d4fba8b9Smrg	    unparse_end(xw);
3672d522f475Smrg	}
3673d522f475Smrg	result = True;
3674d522f475Smrg    }
3675d522f475Smrg    return result;
3676d522f475Smrg}
3677d522f475Smrg
3678cd3331d0Smrgstatic Bool
3679cd3331d0SmrgResetColorsRequest(XtermWidget xw,
3680cd3331d0Smrg		   int code)
3681cd3331d0Smrg{
3682cd3331d0Smrg    Bool result = False;
3683cd3331d0Smrg
3684dfb07bc7Smrg    (void) xw;
3685dfb07bc7Smrg    (void) code;
3686dfb07bc7Smrg
3687cd3331d0Smrg    TRACE(("ResetColorsRequest code=%d\n", code));
3688cd3331d0Smrg
3689cd3331d0Smrg#if OPT_COLOR_RES
3690cd3331d0Smrg    if (GetOldColors(xw)) {
3691037a25ddSmrg	ScrnColors newColors;
3692037a25ddSmrg	const char *thisName;
3693037a25ddSmrg	int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET));
3694037a25ddSmrg
3695cd3331d0Smrg	if (xw->misc.re_verse)
3696d4fba8b9Smrg	    ndx = oppositeColor(xw, ndx);
3697cd3331d0Smrg
3698cd3331d0Smrg	thisName = xw->screen.Tcolors[ndx].resource;
3699cd3331d0Smrg
3700cd3331d0Smrg	newColors.which = 0;
3701cd3331d0Smrg	newColors.names[ndx] = NULL;
3702cd3331d0Smrg
3703cd3331d0Smrg	if (thisName != 0
37049a64e1c5Smrg	    && xw->work.oldColors->names[ndx] != 0
37059a64e1c5Smrg	    && strcmp(thisName, xw->work.oldColors->names[ndx])) {
3706cd3331d0Smrg	    AllocateTermColor(xw, &newColors, ndx, thisName, False);
3707cd3331d0Smrg
3708cd3331d0Smrg	    if (newColors.which != 0) {
3709cd3331d0Smrg		ChangeColors(xw, &newColors);
3710cd3331d0Smrg		UpdateOldColors(xw, &newColors);
3711cd3331d0Smrg	    }
3712cd3331d0Smrg	}
3713cd3331d0Smrg	result = True;
3714cd3331d0Smrg    }
3715cd3331d0Smrg#endif
3716cd3331d0Smrg    return result;
3717cd3331d0Smrg}
3718cd3331d0Smrg
3719cd3331d0Smrg#if OPT_SHIFT_FONTS
3720cd3331d0Smrg/*
3721cd3331d0Smrg * Initially, 'source' points to '#' or '?'.
3722cd3331d0Smrg *
3723cd3331d0Smrg * Look for an optional sign and optional number.  If those are found, lookup
3724cd3331d0Smrg * the corresponding menu font entry.
3725cd3331d0Smrg */
3726cd3331d0Smrgstatic int
3727fa3f02f3SmrgParseShiftedFont(XtermWidget xw, String source, String *target)
3728cd3331d0Smrg{
3729cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3730cd3331d0Smrg    int num = screen->menu_font_number;
3731cd3331d0Smrg    int rel = 0;
3732cd3331d0Smrg
3733cd3331d0Smrg    if (*++source == '+') {
3734cd3331d0Smrg	rel = 1;
3735cd3331d0Smrg	source++;
3736cd3331d0Smrg    } else if (*source == '-') {
3737cd3331d0Smrg	rel = -1;
3738cd3331d0Smrg	source++;
3739cd3331d0Smrg    }
3740cd3331d0Smrg
3741cd3331d0Smrg    if (isdigit(CharOf(*source))) {
3742cd3331d0Smrg	int val = atoi(source);
3743cd3331d0Smrg	if (rel > 0)
3744cd3331d0Smrg	    rel = val;
3745cd3331d0Smrg	else if (rel < 0)
3746cd3331d0Smrg	    rel = -val;
3747cd3331d0Smrg	else
3748cd3331d0Smrg	    num = val;
3749cd3331d0Smrg    }
3750cd3331d0Smrg
3751cd3331d0Smrg    if (rel != 0) {
3752cd3331d0Smrg	num = lookupRelativeFontSize(xw,
3753cd3331d0Smrg				     screen->menu_font_number, rel);
3754cd3331d0Smrg
3755cd3331d0Smrg    }
3756cd3331d0Smrg    TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source));
3757cd3331d0Smrg    *target = source;
3758cd3331d0Smrg    return num;
3759cd3331d0Smrg}
3760cd3331d0Smrg
3761cd3331d0Smrgstatic void
3762cb4a1343SmrgQueryFontRequest(XtermWidget xw, String buf, int final)
3763cd3331d0Smrg{
3764cd3331d0Smrg    if (AllowFontOps(xw, efGetFont)) {
3765cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3766cd3331d0Smrg	Bool success = True;
3767cd3331d0Smrg	int num;
3768cb4a1343Smrg	String base = buf + 1;
3769cd3331d0Smrg	const char *name = 0;
3770cd3331d0Smrg
3771cd3331d0Smrg	num = ParseShiftedFont(xw, buf, &buf);
3772cd3331d0Smrg	if (num < 0
3773cd3331d0Smrg	    || num > fontMenu_lastBuiltin) {
3774cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3775cd3331d0Smrg	    success = False;
3776cd3331d0Smrg	} else {
3777cd3331d0Smrg#if OPT_RENDERFONT
3778cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3779cd3331d0Smrg		name = getFaceName(xw, False);
3780cd3331d0Smrg	    } else
3781cd3331d0Smrg#endif
3782cd3331d0Smrg	    if ((name = screen->MenuFontName(num)) == 0) {
3783cd3331d0Smrg		success = False;
3784cd3331d0Smrg	    }
3785cd3331d0Smrg	}
3786cd3331d0Smrg
3787cd3331d0Smrg	unparseputc1(xw, ANSI_OSC);
3788cd3331d0Smrg	unparseputs(xw, "50");
3789cd3331d0Smrg
3790cd3331d0Smrg	if (success) {
3791cd3331d0Smrg	    unparseputc(xw, ';');
3792cd3331d0Smrg	    if (buf >= base) {
3793cd3331d0Smrg		/* identify the font-entry, unless it is the current one */
3794cd3331d0Smrg		if (*buf != '\0') {
3795037a25ddSmrg		    char temp[10];
3796037a25ddSmrg
3797cd3331d0Smrg		    unparseputc(xw, '#');
3798cd3331d0Smrg		    sprintf(temp, "%d", num);
3799cd3331d0Smrg		    unparseputs(xw, temp);
3800cd3331d0Smrg		    if (*name != '\0')
3801cd3331d0Smrg			unparseputc(xw, ' ');
3802cd3331d0Smrg		}
3803cd3331d0Smrg	    }
3804cd3331d0Smrg	    unparseputs(xw, name);
3805cd3331d0Smrg	}
3806cd3331d0Smrg
3807cd3331d0Smrg	unparseputc1(xw, final);
3808cd3331d0Smrg	unparse_end(xw);
3809cd3331d0Smrg    }
3810cd3331d0Smrg}
3811cd3331d0Smrg
3812cd3331d0Smrgstatic void
3813cb4a1343SmrgChangeFontRequest(XtermWidget xw, String buf)
3814cd3331d0Smrg{
3815cd3331d0Smrg    if (AllowFontOps(xw, efSetFont)) {
3816cd3331d0Smrg	TScreen *screen = TScreenOf(xw);
3817cd3331d0Smrg	Bool success = True;
3818cd3331d0Smrg	int num;
3819cd3331d0Smrg	VTFontNames fonts;
3820cd3331d0Smrg	char *name;
3821cd3331d0Smrg
3822cd3331d0Smrg	/*
3823cd3331d0Smrg	 * If the font specification is a "#", followed by an optional sign and
3824cd3331d0Smrg	 * optional number, lookup the corresponding menu font entry.
3825cd3331d0Smrg	 *
3826cd3331d0Smrg	 * Further, if the "#", etc., is followed by a font name, use that
3827cd3331d0Smrg	 * to load the font entry.
3828cd3331d0Smrg	 */
3829cd3331d0Smrg	if (*buf == '#') {
3830cd3331d0Smrg	    num = ParseShiftedFont(xw, buf, &buf);
3831cd3331d0Smrg
3832cd3331d0Smrg	    if (num < 0
3833cd3331d0Smrg		|| num > fontMenu_lastBuiltin) {
3834cd3331d0Smrg		Bell(xw, XkbBI_MinorError, 0);
3835cd3331d0Smrg		success = False;
3836cd3331d0Smrg	    } else {
3837cd3331d0Smrg		/*
3838cd3331d0Smrg		 * Skip past the optional number, and any whitespace to look
3839cd3331d0Smrg		 * for a font specification within the control.
3840cd3331d0Smrg		 */
3841cd3331d0Smrg		while (isdigit(CharOf(*buf))) {
3842cd3331d0Smrg		    ++buf;
3843cd3331d0Smrg		}
3844cd3331d0Smrg		while (isspace(CharOf(*buf))) {
3845cd3331d0Smrg		    ++buf;
3846cd3331d0Smrg		}
3847cd3331d0Smrg#if OPT_RENDERFONT
3848cd3331d0Smrg		if (UsingRenderFont(xw)) {
3849c219fbebSmrg		    /* EMPTY */
3850c219fbebSmrg		    /* there is only one font entry to load */
3851c219fbebSmrg		    ;
3852cd3331d0Smrg		} else
3853cd3331d0Smrg#endif
3854cd3331d0Smrg		{
3855cd3331d0Smrg		    /*
3856cd3331d0Smrg		     * Normally there is no font specified in the control.
3857cd3331d0Smrg		     * But if there is, simply overwrite the font entry.
3858cd3331d0Smrg		     */
3859cd3331d0Smrg		    if (*buf == '\0') {
3860cd3331d0Smrg			if ((buf = screen->MenuFontName(num)) == 0) {
3861cd3331d0Smrg			    success = False;
3862cd3331d0Smrg			}
3863cd3331d0Smrg		    }
3864cd3331d0Smrg		}
3865cd3331d0Smrg	    }
3866cd3331d0Smrg	} else {
3867cd3331d0Smrg	    num = screen->menu_font_number;
3868cd3331d0Smrg	}
3869cd3331d0Smrg	name = x_strtrim(buf);
387094644356Smrg	if (screen->EscapeFontName()) {
387194644356Smrg	    FREE_STRING(screen->EscapeFontName());
387294644356Smrg	    screen->EscapeFontName() = 0;
387394644356Smrg	}
3874cd3331d0Smrg	if (success && !IsEmpty(name)) {
3875cd3331d0Smrg#if OPT_RENDERFONT
3876cd3331d0Smrg	    if (UsingRenderFont(xw)) {
3877cd3331d0Smrg		setFaceName(xw, name);
3878cd3331d0Smrg		xtermUpdateFontInfo(xw, True);
3879cd3331d0Smrg	    } else
3880cd3331d0Smrg#endif
3881cd3331d0Smrg	    {
3882cd3331d0Smrg		memset(&fonts, 0, sizeof(fonts));
3883cd3331d0Smrg		fonts.f_n = name;
3884cd3331d0Smrg		SetVTFont(xw, num, True, &fonts);
388594644356Smrg		if (num == screen->menu_font_number &&
388694644356Smrg		    num != fontMenu_fontescape) {
388794644356Smrg		    screen->EscapeFontName() = x_strdup(name);
388894644356Smrg		}
3889cd3331d0Smrg	    }
3890cd3331d0Smrg	} else {
3891cd3331d0Smrg	    Bell(xw, XkbBI_MinorError, 0);
3892cd3331d0Smrg	}
389394644356Smrg	update_font_escape();
3894cd3331d0Smrg	free(name);
3895cd3331d0Smrg    }
3896cd3331d0Smrg}
3897cd3331d0Smrg#endif /* OPT_SHIFT_FONTS */
3898cd3331d0Smrg
3899d522f475Smrg/***====================================================================***/
3900d522f475Smrg
3901d522f475Smrgvoid
3902fa3f02f3Smrgdo_osc(XtermWidget xw, Char *oscbuf, size_t len, int final)
3903d522f475Smrg{
3904cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
3905d522f475Smrg    int mode;
3906d522f475Smrg    Char *cp;
3907d522f475Smrg    int state = 0;
3908d522f475Smrg    char *buf = 0;
3909cd3331d0Smrg    char temp[2];
3910cd3331d0Smrg#if OPT_ISO_COLORS
3911cd3331d0Smrg    int ansi_colors = 0;
3912cd3331d0Smrg#endif
3913cd3331d0Smrg    Bool need_data = True;
3914fa3f02f3Smrg    Bool optional_data = False;
3915d522f475Smrg
3916d522f475Smrg    TRACE(("do_osc %s\n", oscbuf));
3917d522f475Smrg
3918712a7ff4Smrg    (void) screen;
3919712a7ff4Smrg
3920d522f475Smrg    /*
3921d522f475Smrg     * Lines should be of the form <OSC> number ; string <ST>, however
3922d522f475Smrg     * older xterms can accept <BEL> as a final character.  We will respond
3923d522f475Smrg     * with the same final character as the application sends to make this
3924d522f475Smrg     * work better with shell scripts, which may have trouble reading an
3925d522f475Smrg     * <ESC><backslash>, which is the 7-bit equivalent to <ST>.
3926d522f475Smrg     */
3927d522f475Smrg    mode = 0;
3928d522f475Smrg    for (cp = oscbuf; *cp != '\0'; cp++) {
3929d522f475Smrg	switch (state) {
3930d522f475Smrg	case 0:
3931d522f475Smrg	    if (isdigit(*cp)) {
3932d522f475Smrg		mode = 10 * mode + (*cp - '0');
3933d522f475Smrg		if (mode > 65535) {
3934d522f475Smrg		    TRACE(("do_osc found unknown mode %d\n", mode));
3935d522f475Smrg		    return;
3936d522f475Smrg		}
3937d522f475Smrg		break;
3938d4fba8b9Smrg	    } else {
3939d4fba8b9Smrg		switch (*cp) {
3940d4fba8b9Smrg		case 'I':
3941d4fba8b9Smrg		    xtermLoadIcon(xw, (char *) ++cp);
3942d4fba8b9Smrg		    return;
3943d4fba8b9Smrg		case 'l':
3944d4fba8b9Smrg		    ChangeTitle(xw, (char *) ++cp);
3945d4fba8b9Smrg		    return;
3946d4fba8b9Smrg		case 'L':
3947d4fba8b9Smrg		    ChangeIconName(xw, (char *) ++cp);
3948d4fba8b9Smrg		    return;
3949d4fba8b9Smrg		}
3950d522f475Smrg	    }
3951d522f475Smrg	    /* FALLTHRU */
3952d522f475Smrg	case 1:
3953d522f475Smrg	    if (*cp != ';') {
3954cd3331d0Smrg		TRACE(("do_osc did not find semicolon offset %d\n",
3955cd3331d0Smrg		       (int) (cp - oscbuf)));
3956d522f475Smrg		return;
3957d522f475Smrg	    }
3958d522f475Smrg	    state = 2;
3959d522f475Smrg	    break;
3960d522f475Smrg	case 2:
3961d522f475Smrg	    buf = (char *) cp;
3962d522f475Smrg	    state = 3;
3963d522f475Smrg	    /* FALLTHRU */
3964d522f475Smrg	default:
3965cd3331d0Smrg	    if (!xtermIsPrintable(xw, &cp, oscbuf + len)) {
3966d522f475Smrg		switch (mode) {
3967d522f475Smrg		case 0:
3968d522f475Smrg		case 1:
3969d522f475Smrg		case 2:
3970d522f475Smrg		    break;
3971d522f475Smrg		default:
3972d522f475Smrg		    TRACE(("do_osc found nonprinting char %02X offset %d\n",
3973d522f475Smrg			   CharOf(*cp),
3974cd3331d0Smrg			   (int) (cp - oscbuf)));
3975d522f475Smrg		    return;
3976d522f475Smrg		}
3977d522f475Smrg	    }
3978d522f475Smrg	}
3979d522f475Smrg    }
3980cd3331d0Smrg
39813367019cSmrg    /*
39823367019cSmrg     * Check if the palette changed and there are no more immediate changes
39833367019cSmrg     * that could be deferred to the next repaint.
39843367019cSmrg     */
3985dfb07bc7Smrg    if (xw->work.palette_changed) {
39863367019cSmrg	switch (mode) {
3987d4fba8b9Smrg	case 03:		/* change X property */
39883367019cSmrg	case 30:		/* Konsole (unused) */
39893367019cSmrg	case 31:		/* Konsole (unused) */
39903367019cSmrg	case 50:		/* font operations */
39913367019cSmrg	case 51:		/* Emacs (unused) */
39923367019cSmrg#if OPT_PASTE64
39933367019cSmrg	case 52:		/* selection data */
39943367019cSmrg#endif
39953367019cSmrg	    TRACE(("forced repaint after palette changed\n"));
3996dfb07bc7Smrg	    xw->work.palette_changed = False;
39973367019cSmrg	    xtermRepaint(xw);
39983367019cSmrg	    break;
3999d4fba8b9Smrg	default:
4000d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
4001d4fba8b9Smrg	    break;
40023367019cSmrg	}
40033367019cSmrg    }
40043367019cSmrg
4005cd3331d0Smrg    /*
4006cd3331d0Smrg     * Most OSC controls other than resets require data.  Handle the others as
4007cd3331d0Smrg     * a special case.
4008cd3331d0Smrg     */
4009cd3331d0Smrg    switch (mode) {
401094644356Smrg    case 50:
4011cd3331d0Smrg#if OPT_ISO_COLORS
4012cd3331d0Smrg    case OSC_Reset(4):
4013cd3331d0Smrg    case OSC_Reset(5):
4014fa3f02f3Smrg	need_data = False;
4015fa3f02f3Smrg	optional_data = True;
4016fa3f02f3Smrg	break;
4017cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4018cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4019cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4020cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4021cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4022cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4023cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4024cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4025cd3331d0Smrg#endif
4026cd3331d0Smrg#if OPT_TEK4014
4027cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4028cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4029cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4030cd3331d0Smrg#endif
4031cd3331d0Smrg	need_data = False;
4032cd3331d0Smrg	break;
4033cd3331d0Smrg#endif
4034cd3331d0Smrg    default:
4035cd3331d0Smrg	break;
4036cd3331d0Smrg    }
4037cd3331d0Smrg
4038cd3331d0Smrg    /*
4039cd3331d0Smrg     * Check if we have data when we want, and not when we do not want it.
4040cd3331d0Smrg     * Either way, that is a malformed control sequence, and will be ignored.
4041cd3331d0Smrg     */
4042cd3331d0Smrg    if (IsEmpty(buf)) {
4043cd3331d0Smrg	if (need_data) {
4044cd3331d0Smrg	    TRACE(("do_osc found no data\n"));
4045cd3331d0Smrg	    return;
4046cd3331d0Smrg	}
4047cd3331d0Smrg	temp[0] = '\0';
4048cd3331d0Smrg	buf = temp;
4049fa3f02f3Smrg    } else if (!need_data && !optional_data) {
4050fa3f02f3Smrg	TRACE(("do_osc found unwanted data\n"));
4051d522f475Smrg	return;
40520d92cbfdSchristos    }
4053d522f475Smrg
4054d522f475Smrg    switch (mode) {
4055d522f475Smrg    case 0:			/* new icon name and title */
4056b7c89284Ssnj	ChangeIconName(xw, buf);
4057b7c89284Ssnj	ChangeTitle(xw, buf);
4058d522f475Smrg	break;
4059d522f475Smrg
4060d522f475Smrg    case 1:			/* new icon name only */
4061b7c89284Ssnj	ChangeIconName(xw, buf);
4062d522f475Smrg	break;
4063d522f475Smrg
4064d522f475Smrg    case 2:			/* new title only */
4065b7c89284Ssnj	ChangeTitle(xw, buf);
4066d522f475Smrg	break;
4067d522f475Smrg
406822d8e007Schristos#ifdef notdef
4069d522f475Smrg    case 3:			/* change X property */
4070cd3331d0Smrg	if (AllowWindowOps(xw, ewSetXprop))
40710d92cbfdSchristos	    ChangeXprop(buf);
4072d522f475Smrg	break;
407322d8e007Schristos#endif
4074d522f475Smrg#if OPT_ISO_COLORS
4075cd3331d0Smrg    case 5:
4076cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4077cd3331d0Smrg	/* FALLTHRU */
4078d522f475Smrg    case 4:
4079d4fba8b9Smrg	if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final))
4080dfb07bc7Smrg	    xw->work.palette_changed = True;
4081cd3331d0Smrg	break;
408294644356Smrg    case 6:
408394644356Smrg	/* FALLTHRU */
408494644356Smrg    case OSC_Reset(6):
408594644356Smrg	TRACE(("parse colorXXMode:%s\n", buf));
408694644356Smrg	while (*buf != '\0') {
408794644356Smrg	    long which = 0;
408894644356Smrg	    long value = 0;
408994644356Smrg	    char *next;
409094644356Smrg	    if (*buf == ';') {
409194644356Smrg		++buf;
409294644356Smrg	    } else {
409394644356Smrg		which = strtol(buf, &next, 10);
4094037a25ddSmrg		if (!PartS2L(buf, next) || (which < 0))
409594644356Smrg		    break;
409694644356Smrg		buf = next;
409794644356Smrg		if (*buf == ';')
409894644356Smrg		    ++buf;
409994644356Smrg	    }
410094644356Smrg	    if (*buf == ';') {
410194644356Smrg		++buf;
410294644356Smrg	    } else {
410394644356Smrg		value = strtol(buf, &next, 10);
4104dfb07bc7Smrg		if (!PartS2L(buf, next) || (value < 0))
410594644356Smrg		    break;
410694644356Smrg		buf = next;
410794644356Smrg		if (*buf == ';')
410894644356Smrg		    ++buf;
410994644356Smrg	    }
411094644356Smrg	    TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value));
411194644356Smrg	    switch (which) {
411294644356Smrg	    case 0:
411394644356Smrg		screen->colorBDMode = (value != 0);
411494644356Smrg		break;
411594644356Smrg	    case 1:
411694644356Smrg		screen->colorULMode = (value != 0);
411794644356Smrg		break;
411894644356Smrg	    case 2:
411994644356Smrg		screen->colorBLMode = (value != 0);
412094644356Smrg		break;
412194644356Smrg	    case 3:
412294644356Smrg		screen->colorRVMode = (value != 0);
412394644356Smrg		break;
412494644356Smrg#if OPT_WIDE_ATTRS
412594644356Smrg	    case 4:
412694644356Smrg		screen->colorITMode = (value != 0);
412794644356Smrg		break;
412894644356Smrg#endif
412994644356Smrg	    default:
413094644356Smrg		TRACE(("...unknown colorXXMode\n"));
413194644356Smrg		break;
413294644356Smrg	    }
413394644356Smrg	}
413494644356Smrg	break;
4135cd3331d0Smrg    case OSC_Reset(5):
4136cd3331d0Smrg	ansi_colors = NUM_ANSI_COLORS;
4137cd3331d0Smrg	/* FALLTHRU */
4138cd3331d0Smrg    case OSC_Reset(4):
4139cd3331d0Smrg	if (ResetAnsiColorRequest(xw, buf, ansi_colors))
4140dfb07bc7Smrg	    xw->work.palette_changed = True;
4141d522f475Smrg	break;
4142d522f475Smrg#endif
4143d522f475Smrg    case OSC_TEXT_FG:
4144d522f475Smrg    case OSC_TEXT_BG:
4145d522f475Smrg    case OSC_TEXT_CURSOR:
4146d522f475Smrg    case OSC_MOUSE_FG:
4147d522f475Smrg    case OSC_MOUSE_BG:
4148d522f475Smrg#if OPT_HIGHLIGHT_COLOR
4149d522f475Smrg    case OSC_HIGHLIGHT_BG:
4150cd3331d0Smrg    case OSC_HIGHLIGHT_FG:
4151d522f475Smrg#endif
4152d522f475Smrg#if OPT_TEK4014
4153d522f475Smrg    case OSC_TEK_FG:
4154d522f475Smrg    case OSC_TEK_BG:
4155d522f475Smrg    case OSC_TEK_CURSOR:
4156d522f475Smrg#endif
4157cd3331d0Smrg	if (xw->misc.dynamicColors) {
4158d522f475Smrg	    ChangeColorsRequest(xw, mode, buf, final);
4159cd3331d0Smrg	}
4160cd3331d0Smrg	break;
4161cd3331d0Smrg    case OSC_Reset(OSC_TEXT_FG):
4162cd3331d0Smrg    case OSC_Reset(OSC_TEXT_BG):
4163cd3331d0Smrg    case OSC_Reset(OSC_TEXT_CURSOR):
4164cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_FG):
4165cd3331d0Smrg    case OSC_Reset(OSC_MOUSE_BG):
4166cd3331d0Smrg#if OPT_HIGHLIGHT_COLOR
4167cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_BG):
4168cd3331d0Smrg    case OSC_Reset(OSC_HIGHLIGHT_FG):
4169cd3331d0Smrg#endif
4170cd3331d0Smrg#if OPT_TEK4014
4171cd3331d0Smrg    case OSC_Reset(OSC_TEK_FG):
4172cd3331d0Smrg    case OSC_Reset(OSC_TEK_BG):
4173cd3331d0Smrg    case OSC_Reset(OSC_TEK_CURSOR):
4174cd3331d0Smrg#endif
4175cd3331d0Smrg	if (xw->misc.dynamicColors) {
4176cd3331d0Smrg	    ResetColorsRequest(xw, mode);
4177cd3331d0Smrg	}
4178d522f475Smrg	break;
4179d522f475Smrg
4180d522f475Smrg    case 30:
4181d522f475Smrg    case 31:
4182d522f475Smrg	/* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */
4183d522f475Smrg	break;
4184d522f475Smrg
4185d522f475Smrg#ifdef ALLOWLOGGING
4186d522f475Smrg    case 46:			/* new log file */
4187d522f475Smrg#ifdef ALLOWLOGFILECHANGES
4188d522f475Smrg	/*
4189d522f475Smrg	 * Warning, enabling this feature allows people to overwrite
4190d522f475Smrg	 * arbitrary files accessible to the person running xterm.
4191d522f475Smrg	 */
4192037a25ddSmrg	if (strcmp(buf, "?")) {
4193037a25ddSmrg	    char *bp;
4194dfb07bc7Smrg	    if ((bp = x_strdup(buf)) != NULL) {
4195d4fba8b9Smrg		free(screen->logfile);
4196037a25ddSmrg		screen->logfile = bp;
4197037a25ddSmrg		break;
4198037a25ddSmrg	    }
4199d522f475Smrg	}
4200d522f475Smrg#endif
4201cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4202cd3331d0Smrg	Bell(xw, XkbBI_Info, 0);
4203d522f475Smrg	break;
4204d522f475Smrg#endif /* ALLOWLOGGING */
4205d522f475Smrg
4206d522f475Smrg    case 50:
4207d522f475Smrg#if OPT_SHIFT_FONTS
4208cd3331d0Smrg	if (*buf == '?') {
4209cd3331d0Smrg	    QueryFontRequest(xw, buf, final);
4210cd3331d0Smrg	} else if (xw->misc.shift_fonts) {
4211cd3331d0Smrg	    ChangeFontRequest(xw, buf);
4212d522f475Smrg	}
4213d522f475Smrg#endif /* OPT_SHIFT_FONTS */
4214d522f475Smrg	break;
4215d522f475Smrg    case 51:
4216d522f475Smrg	/* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */
4217d522f475Smrg	break;
4218d522f475Smrg
4219d522f475Smrg#if OPT_PASTE64
4220d522f475Smrg    case 52:
4221cd3331d0Smrg	ManipulateSelectionData(xw, screen, buf, final);
4222d522f475Smrg	break;
4223d522f475Smrg#endif
4224d522f475Smrg	/*
4225d522f475Smrg	 * One could write code to send back the display and host names,
4226d522f475Smrg	 * but that could potentially open a fairly nasty security hole.
4227d522f475Smrg	 */
4228cd3331d0Smrg    default:
4229cd3331d0Smrg	TRACE(("do_osc - unrecognized code\n"));
4230cd3331d0Smrg	break;
4231d522f475Smrg    }
4232d522f475Smrg    unparse_end(xw);
4233d522f475Smrg}
4234d522f475Smrg
4235d522f475Smrg/*
4236d522f475Smrg * Parse one nibble of a hex byte from the OSC string.  We have removed the
4237d522f475Smrg * string-terminator (replacing it with a null), so the only other delimiter
4238d522f475Smrg * that is expected is semicolon.  Ignore other characters (Ray Neuman says
4239d522f475Smrg * "real" terminals accept commas in the string definitions).
4240d522f475Smrg */
4241d522f475Smrgstatic int
4242cd3331d0Smrgudk_value(const char **cp)
4243d522f475Smrg{
4244cd3331d0Smrg    int result = -1;
4245d522f475Smrg
4246d522f475Smrg    for (;;) {
4247037a25ddSmrg	int c;
4248037a25ddSmrg
4249d522f475Smrg	if ((c = **cp) != '\0')
4250d522f475Smrg	    *cp = *cp + 1;
4251d522f475Smrg	if (c == ';' || c == '\0')
4252cd3331d0Smrg	    break;
4253cd3331d0Smrg	if ((result = x_hex2int(c)) >= 0)
4254cd3331d0Smrg	    break;
4255d522f475Smrg    }
4256cd3331d0Smrg
4257cd3331d0Smrg    return result;
4258d522f475Smrg}
4259d522f475Smrg
4260d522f475Smrgvoid
42619a64e1c5Smrgreset_decudk(XtermWidget xw)
4262d522f475Smrg{
4263d522f475Smrg    int n;
4264d522f475Smrg    for (n = 0; n < MAX_UDK; n++) {
4265d4fba8b9Smrg	FreeAndNull(xw->work.user_keys[n].str);
4266d4fba8b9Smrg	xw->work.user_keys[n].len = 0;
4267d522f475Smrg    }
4268d522f475Smrg}
4269d522f475Smrg
4270d522f475Smrg/*
4271d522f475Smrg * Parse the data for DECUDK (user-defined keys).
4272d522f475Smrg */
4273d522f475Smrgstatic void
42749a64e1c5Smrgparse_decudk(XtermWidget xw, const char *cp)
4275d522f475Smrg{
4276d522f475Smrg    while (*cp) {
4277cd3331d0Smrg	const char *base = cp;
4278d4fba8b9Smrg	char *str = malloc(strlen(cp) + 3);
4279d522f475Smrg	unsigned key = 0;
4280d522f475Smrg	int len = 0;
4281d522f475Smrg
428294644356Smrg	if (str == NULL)
428394644356Smrg	    break;
428494644356Smrg
4285d522f475Smrg	while (isdigit(CharOf(*cp)))
42860d92cbfdSchristos	    key = (key * 10) + (unsigned) (*cp++ - '0');
4287037a25ddSmrg
4288d522f475Smrg	if (*cp == '/') {
4289037a25ddSmrg	    int lo, hi;
4290037a25ddSmrg
4291d522f475Smrg	    cp++;
4292d522f475Smrg	    while ((hi = udk_value(&cp)) >= 0
4293d522f475Smrg		   && (lo = udk_value(&cp)) >= 0) {
42940d92cbfdSchristos		str[len++] = (char) ((hi << 4) | lo);
4295d522f475Smrg	    }
4296d522f475Smrg	}
4297d522f475Smrg	if (len > 0 && key < MAX_UDK) {
42983367019cSmrg	    str[len] = '\0';
4299d4fba8b9Smrg	    free(xw->work.user_keys[key].str);
43009a64e1c5Smrg	    xw->work.user_keys[key].str = str;
43019a64e1c5Smrg	    xw->work.user_keys[key].len = len;
4302d4fba8b9Smrg	    TRACE(("parse_decudk %d:%.*s\n", key, len, str));
4303d522f475Smrg	} else {
4304d522f475Smrg	    free(str);
4305d522f475Smrg	}
4306d522f475Smrg	if (*cp == ';')
4307d522f475Smrg	    cp++;
4308d522f475Smrg	if (cp == base)		/* badly-formed sequence - bail out */
4309d522f475Smrg	    break;
4310d522f475Smrg    }
4311d522f475Smrg}
4312d522f475Smrg
4313fa3f02f3Smrg/*
4314fa3f02f3Smrg * Parse numeric parameters.  Normally we use a state machine to simplify
4315fa3f02f3Smrg * interspersing with control characters, but have the string already.
4316fa3f02f3Smrg */
4317fa3f02f3Smrgstatic void
4318fa3f02f3Smrgparse_ansi_params(ANSI *params, const char **string)
4319fa3f02f3Smrg{
4320fa3f02f3Smrg    const char *cp = *string;
4321fa3f02f3Smrg    ParmType nparam = 0;
4322fa3f02f3Smrg    int last_empty = 1;
4323fa3f02f3Smrg
4324fa3f02f3Smrg    memset(params, 0, sizeof(*params));
4325fa3f02f3Smrg    while (*cp != '\0') {
4326fa3f02f3Smrg	Char ch = CharOf(*cp++);
4327fa3f02f3Smrg
4328fa3f02f3Smrg	if (isdigit(ch)) {
4329fa3f02f3Smrg	    last_empty = 0;
4330fa3f02f3Smrg	    if (nparam < NPARAM) {
4331fa3f02f3Smrg		params->a_param[nparam] =
4332fa3f02f3Smrg		    (ParmType) ((params->a_param[nparam] * 10)
4333fa3f02f3Smrg				+ (ch - '0'));
4334fa3f02f3Smrg	    }
4335fa3f02f3Smrg	} else if (ch == ';') {
4336fa3f02f3Smrg	    last_empty = 1;
4337fa3f02f3Smrg	    nparam++;
4338fa3f02f3Smrg	} else if (ch < 32) {
4339fa3f02f3Smrg	    /* EMPTY */ ;
4340fa3f02f3Smrg	} else {
4341fa3f02f3Smrg	    /* should be 0x30 to 0x7e */
4342fa3f02f3Smrg	    params->a_final = ch;
4343fa3f02f3Smrg	    break;
4344fa3f02f3Smrg	}
4345fa3f02f3Smrg    }
4346fa3f02f3Smrg
4347fa3f02f3Smrg    *string = cp;
4348fa3f02f3Smrg    if (!last_empty)
4349fa3f02f3Smrg	nparam++;
4350fa3f02f3Smrg    if (nparam > NPARAM)
4351fa3f02f3Smrg	params->a_nparam = NPARAM;
4352fa3f02f3Smrg    else
4353fa3f02f3Smrg	params->a_nparam = nparam;
4354fa3f02f3Smrg}
4355fa3f02f3Smrg
4356d522f475Smrg#if OPT_TRACE
4357d522f475Smrg#define SOFT_WIDE 10
4358d522f475Smrg#define SOFT_HIGH 20
4359d522f475Smrg
4360d522f475Smrgstatic void
4361fa3f02f3Smrgparse_decdld(ANSI *params, const char *string)
4362d522f475Smrg{
4363d522f475Smrg    char DscsName[8];
4364d522f475Smrg    int len;
4365d522f475Smrg    int Pfn = params->a_param[0];
4366d522f475Smrg    int Pcn = params->a_param[1];
4367d522f475Smrg    int Pe = params->a_param[2];
4368d522f475Smrg    int Pcmw = params->a_param[3];
4369d522f475Smrg    int Pw = params->a_param[4];
4370d522f475Smrg    int Pt = params->a_param[5];
4371d522f475Smrg    int Pcmh = params->a_param[6];
4372d522f475Smrg    int Pcss = params->a_param[7];
4373d522f475Smrg
4374d522f475Smrg    int start_char = Pcn + 0x20;
4375d522f475Smrg    int char_wide = ((Pcmw == 0)
4376d522f475Smrg		     ? (Pcss ? 6 : 10)
4377d522f475Smrg		     : (Pcmw > 4
4378d522f475Smrg			? Pcmw
4379d522f475Smrg			: (Pcmw + 3)));
4380d522f475Smrg    int char_high = ((Pcmh == 0)
43813367019cSmrg		     ? ((Pcmw >= 2 && Pcmw <= 4)
4382d522f475Smrg			? 10
4383d522f475Smrg			: 20)
4384d522f475Smrg		     : Pcmh);
4385d522f475Smrg    Char ch;
4386d522f475Smrg    Char bits[SOFT_HIGH][SOFT_WIDE];
4387d522f475Smrg    Bool first = True;
4388d522f475Smrg    Bool prior = False;
4389d522f475Smrg    int row = 0, col = 0;
4390d522f475Smrg
4391d522f475Smrg    TRACE(("Parsing DECDLD\n"));
4392d522f475Smrg    TRACE(("  font number   %d\n", Pfn));
4393d522f475Smrg    TRACE(("  starting char %d\n", Pcn));
4394d522f475Smrg    TRACE(("  erase control %d\n", Pe));
4395d522f475Smrg    TRACE(("  char-width    %d\n", Pcmw));
4396d522f475Smrg    TRACE(("  font-width    %d\n", Pw));
4397d522f475Smrg    TRACE(("  text/full     %d\n", Pt));
4398d522f475Smrg    TRACE(("  char-height   %d\n", Pcmh));
4399d522f475Smrg    TRACE(("  charset-size  %d\n", Pcss));
4400d522f475Smrg
4401d522f475Smrg    if (Pfn > 1
4402d522f475Smrg	|| Pcn > 95
4403d522f475Smrg	|| Pe > 2
4404d522f475Smrg	|| Pcmw > 10
4405d522f475Smrg	|| Pcmw == 1
4406d522f475Smrg	|| Pt > 2
4407d522f475Smrg	|| Pcmh > 20
4408d522f475Smrg	|| Pcss > 1
4409d522f475Smrg	|| char_wide > SOFT_WIDE
4410d522f475Smrg	|| char_high > SOFT_HIGH) {
4411d522f475Smrg	TRACE(("DECDLD illegal parameter\n"));
4412d522f475Smrg	return;
4413d522f475Smrg    }
4414d522f475Smrg
4415d522f475Smrg    len = 0;
4416d522f475Smrg    while (*string != '\0') {
4417d522f475Smrg	ch = CharOf(*string++);
4418d522f475Smrg	if (ch >= ANSI_SPA && ch <= 0x2f) {
4419d522f475Smrg	    if (len < 2)
4420b7c89284Ssnj		DscsName[len++] = (char) ch;
4421d522f475Smrg	} else if (ch >= 0x30 && ch <= 0x7e) {
4422b7c89284Ssnj	    DscsName[len++] = (char) ch;
4423d522f475Smrg	    break;
4424d522f475Smrg	}
4425d522f475Smrg    }
4426d522f475Smrg    DscsName[len] = 0;
4427d522f475Smrg    TRACE(("  Dscs name     '%s'\n", DscsName));
4428d522f475Smrg
4429d522f475Smrg    TRACE(("  character matrix %dx%d\n", char_high, char_wide));
4430d522f475Smrg    while (*string != '\0') {
4431d522f475Smrg	if (first) {
4432d522f475Smrg	    TRACE(("Char %d:\n", start_char));
4433d522f475Smrg	    if (prior) {
4434d522f475Smrg		for (row = 0; row < char_high; ++row) {
4435d522f475Smrg		    TRACE(("%.*s\n", char_wide, bits[row]));
4436d522f475Smrg		}
4437d522f475Smrg	    }
4438d522f475Smrg	    prior = False;
4439d522f475Smrg	    first = False;
4440d522f475Smrg	    for (row = 0; row < char_high; ++row) {
4441d522f475Smrg		for (col = 0; col < char_wide; ++col) {
4442d522f475Smrg		    bits[row][col] = '.';
4443d522f475Smrg		}
4444d522f475Smrg	    }
4445d522f475Smrg	    row = col = 0;
4446d522f475Smrg	}
4447d522f475Smrg	ch = CharOf(*string++);
4448d522f475Smrg	if (ch >= 0x3f && ch <= 0x7e) {
4449d522f475Smrg	    int n;
4450d522f475Smrg
4451b7c89284Ssnj	    ch = CharOf(ch - 0x3f);
4452d522f475Smrg	    for (n = 0; n < 6; ++n) {
4453b7c89284Ssnj		bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.');
4454d522f475Smrg	    }
4455d522f475Smrg	    col += 1;
4456d522f475Smrg	    prior = True;
4457d522f475Smrg	} else if (ch == '/') {
4458d522f475Smrg	    row += 6;
4459d522f475Smrg	    col = 0;
4460d522f475Smrg	} else if (ch == ';') {
4461d522f475Smrg	    first = True;
4462d522f475Smrg	    ++start_char;
4463d522f475Smrg	}
4464d522f475Smrg    }
4465d522f475Smrg}
4466d522f475Smrg#else
4467d522f475Smrg#define parse_decdld(p,q)	/* nothing */
4468d522f475Smrg#endif
4469d522f475Smrg
4470d4fba8b9Smrg#if OPT_DEC_RECTOPS
4471d4fba8b9Smrgstatic const char *
4472d4fba8b9Smrgskip_params(const char *cp)
4473d4fba8b9Smrg{
4474d4fba8b9Smrg    while (*cp == ';' || (*cp >= '0' && *cp <= '9'))
4475d4fba8b9Smrg	++cp;
4476d4fba8b9Smrg    return cp;
4477d4fba8b9Smrg}
4478d4fba8b9Smrg
4479d4fba8b9Smrgstatic int
4480d4fba8b9Smrgparse_int_param(const char **cp)
4481d4fba8b9Smrg{
4482d4fba8b9Smrg    int result = 0;
4483d4fba8b9Smrg    const char *s = *cp;
4484d4fba8b9Smrg    while (*s != '\0') {
4485d4fba8b9Smrg	if (*s == ';') {
4486d4fba8b9Smrg	    ++s;
4487d4fba8b9Smrg	    break;
4488d4fba8b9Smrg	} else if (*s >= '0' && *s <= '9') {
4489d4fba8b9Smrg	    result = (result * 10) + (*s++ - '0');
4490d4fba8b9Smrg	} else {
4491d4fba8b9Smrg	    s += strlen(s);
4492d4fba8b9Smrg	}
4493d4fba8b9Smrg    }
4494d4fba8b9Smrg    TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s));
4495d4fba8b9Smrg    *cp = s;
4496d4fba8b9Smrg    return result;
4497d4fba8b9Smrg}
4498d4fba8b9Smrg
4499d4fba8b9Smrgstatic int
4500d4fba8b9Smrgparse_chr_param(const char **cp)
4501d4fba8b9Smrg{
4502d4fba8b9Smrg    int result = 0;
4503d4fba8b9Smrg    const char *s = *cp;
4504d4fba8b9Smrg    if (*s != '\0') {
4505d4fba8b9Smrg	if ((result = CharOf(*s++)) != 0) {
4506d4fba8b9Smrg	    if (*s == ';') {
4507d4fba8b9Smrg		++s;
4508d4fba8b9Smrg	    } else if (*s != '\0') {
4509d4fba8b9Smrg		result = 0;
4510d4fba8b9Smrg	    }
4511d4fba8b9Smrg	}
4512d4fba8b9Smrg    }
4513d4fba8b9Smrg    TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s));
4514d4fba8b9Smrg    *cp = s;
4515d4fba8b9Smrg    return result;
4516d4fba8b9Smrg}
4517d4fba8b9Smrg
4518d4fba8b9Smrgstatic void
4519d4fba8b9Smrgrestore_DECCIR(XtermWidget xw, const char *cp)
4520d4fba8b9Smrg{
4521d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
4522d4fba8b9Smrg    int value;
4523d4fba8b9Smrg
4524d4fba8b9Smrg    /* row */
4525d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen))
4526d4fba8b9Smrg	return;
4527d4fba8b9Smrg    screen->cur_row = (value - 1);
4528d4fba8b9Smrg
4529d4fba8b9Smrg    /* column */
4530d4fba8b9Smrg    if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen))
4531d4fba8b9Smrg	return;
4532d4fba8b9Smrg    screen->cur_col = (value - 1);
4533d4fba8b9Smrg
4534d4fba8b9Smrg    /* page */
4535d4fba8b9Smrg    if (parse_int_param(&cp) != 1)
4536d4fba8b9Smrg	return;
4537d4fba8b9Smrg
4538d4fba8b9Smrg    /* rendition */
4539d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4540d4fba8b9Smrg	return;
4541d4fba8b9Smrg    UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD));
4542d4fba8b9Smrg    xw->flags |= (value & 8) ? INVERSE : 0;
4543d4fba8b9Smrg    xw->flags |= (value & 4) ? BLINK : 0;
4544d4fba8b9Smrg    xw->flags |= (value & 2) ? UNDERLINE : 0;
4545d4fba8b9Smrg    xw->flags |= (value & 1) ? BOLD : 0;
4546d4fba8b9Smrg
4547d4fba8b9Smrg    /* attributes */
4548d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40)
4549d4fba8b9Smrg	return;
4550d4fba8b9Smrg    screen->protected_mode &= ~DEC_PROTECT;
4551d4fba8b9Smrg    screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0;
4552d4fba8b9Smrg
4553d4fba8b9Smrg    /* flags */
4554d4fba8b9Smrg    if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40)
4555d4fba8b9Smrg	return;
4556d4fba8b9Smrg    screen->do_wrap = (value & 8) ? True : False;
4557d4fba8b9Smrg    screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0));
4558d4fba8b9Smrg    UIntClr(xw->flags, ORIGIN);
4559d4fba8b9Smrg    xw->flags |= (value & 1) ? ORIGIN : 0;
4560d4fba8b9Smrg
4561d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4562d4fba8b9Smrg	return;
4563d4fba8b9Smrg    screen->curgl = (Char) value;
4564d4fba8b9Smrg
4565d4fba8b9Smrg    if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS)
4566d4fba8b9Smrg	return;
4567d4fba8b9Smrg    screen->curgr = (Char) value;
4568d4fba8b9Smrg
4569d4fba8b9Smrg    /* character-set size */
4570d4fba8b9Smrg    if (parse_chr_param(&cp) != 0x4f)	/* works for xterm */
4571d4fba8b9Smrg	return;
4572d4fba8b9Smrg
4573d4fba8b9Smrg    /* SCS designators */
4574d4fba8b9Smrg    for (value = 0; value < NUM_GSETS; ++value) {
4575d4fba8b9Smrg	if (*cp == '%') {
4576d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '%', *++cp);
4577d4fba8b9Smrg	} else if (*cp != '\0') {
4578d4fba8b9Smrg	    xtermDecodeSCS(xw, value, 0, '\0', *cp);
4579d4fba8b9Smrg	} else {
4580d4fba8b9Smrg	    return;
4581d4fba8b9Smrg	}
4582d4fba8b9Smrg	cp++;
4583d4fba8b9Smrg    }
4584d4fba8b9Smrg
4585d4fba8b9Smrg    TRACE(("...done DECCIR\n"));
4586d4fba8b9Smrg}
4587d4fba8b9Smrg
4588d4fba8b9Smrgstatic void
4589d4fba8b9Smrgrestore_DECTABSR(XtermWidget xw, const char *cp)
4590d4fba8b9Smrg{
4591d4fba8b9Smrg    int stop = 0;
4592d4fba8b9Smrg    Bool fail = False;
4593d4fba8b9Smrg
4594d4fba8b9Smrg    TabZonk(xw->tabs);
4595d4fba8b9Smrg    while (*cp != '\0' && !fail) {
4596d4fba8b9Smrg	if ((*cp) >= '0' && (*cp) <= '9') {
4597d4fba8b9Smrg	    stop = (stop * 10) + ((*cp) - '0');
4598d4fba8b9Smrg	} else if (*cp == '/') {
4599d4fba8b9Smrg	    --stop;
4600d4fba8b9Smrg	    if (OkTAB(stop)) {
4601d4fba8b9Smrg		TabSet(xw->tabs, stop);
4602d4fba8b9Smrg		stop = 0;
4603d4fba8b9Smrg	    } else {
4604d4fba8b9Smrg		fail = True;
4605d4fba8b9Smrg	    }
4606d4fba8b9Smrg	} else {
4607d4fba8b9Smrg	    fail = True;
4608d4fba8b9Smrg	}
4609d4fba8b9Smrg	++cp;
4610d4fba8b9Smrg    }
4611d4fba8b9Smrg    --stop;
4612d4fba8b9Smrg    if (OkTAB(stop))
4613d4fba8b9Smrg	TabSet(xw->tabs, stop);
4614d4fba8b9Smrg
4615d4fba8b9Smrg    TRACE(("...done DECTABSR\n"));
4616d4fba8b9Smrg}
4617d4fba8b9Smrg#endif
4618d4fba8b9Smrg
4619d522f475Smrgvoid
4620fa3f02f3Smrgdo_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen)
4621d522f475Smrg{
4622cd3331d0Smrg    TScreen *screen = TScreenOf(xw);
4623d522f475Smrg    char reply[BUFSIZ];
4624cd3331d0Smrg    const char *cp = (const char *) dcsbuf;
4625d522f475Smrg    Bool okay;
4626d522f475Smrg    ANSI params;
4627d4fba8b9Smrg#if OPT_DEC_RECTOPS
4628d4fba8b9Smrg    char psarg = '0';
4629d4fba8b9Smrg#endif
4630d522f475Smrg
4631cd3331d0Smrg    TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen));
4632d522f475Smrg
4633d522f475Smrg    if (dcslen != strlen(cp))
4634d522f475Smrg	/* shouldn't have nulls in the string */
4635d522f475Smrg	return;
4636d522f475Smrg
4637d522f475Smrg    switch (*cp) {		/* intermediate character, or parameter */
4638d522f475Smrg    case '$':			/* DECRQSS */
4639d522f475Smrg	okay = True;
4640d522f475Smrg
4641d522f475Smrg	cp++;
4642d4fba8b9Smrg	if (*cp == 'q') {
4643d4fba8b9Smrg	    *reply = '\0';
4644d4fba8b9Smrg	    cp++;
4645d522f475Smrg	    if (!strcmp(cp, "\"q")) {	/* DECSCA */
4646d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCA\n"));
4647d522f475Smrg		sprintf(reply, "%d%s",
4648d522f475Smrg			(screen->protected_mode == DEC_PROTECT)
4649d522f475Smrg			&& (xw->flags & PROTECTED) ? 1 : 0,
4650d522f475Smrg			cp);
4651d522f475Smrg	    } else if (!strcmp(cp, "\"p")) {	/* DECSCL */
46523367019cSmrg		if (screen->vtXX_level < 2) {
46533367019cSmrg		    /* actually none of DECRQSS is valid for vt100's */
46543367019cSmrg		    break;
46553367019cSmrg		}
4656d4fba8b9Smrg		TRACE(("DECRQSS -> DECSCL\n"));
4657d522f475Smrg		sprintf(reply, "%d%s%s",
4658d522f475Smrg			(screen->vtXX_level ?
4659d522f475Smrg			 screen->vtXX_level : 1) + 60,
4660d522f475Smrg			(screen->vtXX_level >= 2)
4661d522f475Smrg			? (screen->control_eight_bits
4662d522f475Smrg			   ? ";0" : ";1")
4663d522f475Smrg			: "",
4664d522f475Smrg			cp);
4665d522f475Smrg	    } else if (!strcmp(cp, "r")) {	/* DECSTBM */
4666d4fba8b9Smrg		TRACE(("DECRQSS -> DECSTBM\n"));
4667d522f475Smrg		sprintf(reply, "%d;%dr",
4668d522f475Smrg			screen->top_marg + 1,
4669d522f475Smrg			screen->bot_marg + 1);
46703367019cSmrg	    } else if (!strcmp(cp, "s")) {	/* DECSLRM */
46713367019cSmrg		if (screen->vtXX_level >= 4) {	/* VT420 */
4672d4fba8b9Smrg		    TRACE(("DECRQSS -> DECSLRM\n"));
46733367019cSmrg		    sprintf(reply, "%d;%ds",
46743367019cSmrg			    screen->lft_marg + 1,
46753367019cSmrg			    screen->rgt_marg + 1);
4676037a25ddSmrg		} else {
4677037a25ddSmrg		    okay = False;
46783367019cSmrg		}
4679d522f475Smrg	    } else if (!strcmp(cp, "m")) {	/* SGR */
4680d4fba8b9Smrg		TRACE(("DECRQSS -> SGR\n"));
4681d4fba8b9Smrg		xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background);
4682d522f475Smrg		strcat(reply, "m");
4683712a7ff4Smrg	    } else if (!strcmp(cp, " q")) {	/* DECSCUSR */
46843367019cSmrg		int code = STEADY_BLOCK;
46853367019cSmrg		if (isCursorUnderline(screen))
46863367019cSmrg		    code = STEADY_UNDERLINE;
46873367019cSmrg		else if (isCursorBar(screen))
46883367019cSmrg		    code = STEADY_BAR;
46893367019cSmrg#if OPT_BLINK_CURS
469094644356Smrg		if (screen->cursor_blink_esc != 0)
46913367019cSmrg		    code -= 1;
46923367019cSmrg#endif
4693d4fba8b9Smrg		TRACE(("reply DECSCUSR\n"));
46943367019cSmrg		sprintf(reply, "%d%s", code, cp);
4695d4fba8b9Smrg	    } else if (!strcmp(cp, "t")) {	/* DECSLPP */
4696d4fba8b9Smrg		sprintf(reply, "%d%s",
4697d4fba8b9Smrg			((screen->max_row > 24) ? screen->max_row : 24),
4698d4fba8b9Smrg			cp);
4699d4fba8b9Smrg		TRACE(("reply DECSLPP\n"));
4700d4fba8b9Smrg	    } else if (!strcmp(cp, "$|")) {	/* DECSCPP */
4701d4fba8b9Smrg		TRACE(("reply DECSCPP\n"));
4702d4fba8b9Smrg		sprintf(reply, "%d%s",
4703d4fba8b9Smrg			((xw->flags & IN132COLUMNS) ? 132 : 80),
4704d4fba8b9Smrg			cp);
4705d4fba8b9Smrg	    } else if (!strcmp(cp, "*|")) {	/* DECSNLS */
4706d4fba8b9Smrg		TRACE(("reply DECSNLS\n"));
4707d4fba8b9Smrg		sprintf(reply, "%d%s",
4708d4fba8b9Smrg			screen->max_row + 1,
4709d4fba8b9Smrg			cp);
47100d92cbfdSchristos	    } else {
4711d4fba8b9Smrg		okay = False;
471222d8e007Schristos	    }
4713d4fba8b9Smrg
4714d4fba8b9Smrg	    unparseputc1(xw, ANSI_DCS);
4715d4fba8b9Smrg	    unparseputc(xw, okay ? '1' : '0');
4716d4fba8b9Smrg	    unparseputc(xw, '$');
4717d4fba8b9Smrg	    unparseputc(xw, 'r');
4718d4fba8b9Smrg	    cp = reply;
4719d4fba8b9Smrg	    unparseputs(xw, cp);
4720d4fba8b9Smrg	    unparseputc1(xw, ANSI_ST);
4721d522f475Smrg	} else {
4722d522f475Smrg	    unparseputc(xw, ANSI_CAN);
4723d522f475Smrg	}
4724d522f475Smrg	break;
4725d522f475Smrg    case '+':
4726d522f475Smrg	cp++;
4727cd3331d0Smrg	switch (*cp) {
4728d4fba8b9Smrg#if OPT_TCAP_QUERY
4729cd3331d0Smrg	case 'p':
4730cd3331d0Smrg	    if (AllowTcapOps(xw, etSetTcap)) {
4731cd3331d0Smrg		set_termcap(xw, cp + 1);
4732cd3331d0Smrg	    }
4733cd3331d0Smrg	    break;
4734cd3331d0Smrg	case 'q':
4735cd3331d0Smrg	    if (AllowTcapOps(xw, etGetTcap)) {
4736cd3331d0Smrg		Bool fkey;
4737cd3331d0Smrg		unsigned state;
4738cd3331d0Smrg		int code;
4739cd3331d0Smrg		const char *tmp;
4740cd3331d0Smrg		const char *parsed = ++cp;
4741d522f475Smrg
4742cd3331d0Smrg		code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4743d522f475Smrg
4744cd3331d0Smrg		unparseputc1(xw, ANSI_DCS);
4745b7c89284Ssnj
4746cd3331d0Smrg		unparseputc(xw, code >= 0 ? '1' : '0');
4747d522f475Smrg
4748cd3331d0Smrg		unparseputc(xw, '+');
4749cd3331d0Smrg		unparseputc(xw, 'r');
4750d522f475Smrg
4751cd3331d0Smrg		while (*cp != 0 && (code >= -1)) {
4752cd3331d0Smrg		    if (cp == parsed)
4753cd3331d0Smrg			break;	/* no data found, error */
4754d522f475Smrg
4755cd3331d0Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4756cd3331d0Smrg			unparseputc(xw, *tmp);
4757d522f475Smrg
4758cd3331d0Smrg		    if (code >= 0) {
4759cd3331d0Smrg			unparseputc(xw, '=');
4760cd3331d0Smrg			screen->tc_query_code = code;
4761cd3331d0Smrg			screen->tc_query_fkey = fkey;
4762d522f475Smrg#if OPT_ISO_COLORS
4763cd3331d0Smrg			/* XK_COLORS is a fake code for the "Co" entry (maximum
4764cd3331d0Smrg			 * number of colors) */
4765cd3331d0Smrg			if (code == XK_COLORS) {
4766d4fba8b9Smrg			    unparseputn(xw, (unsigned) NUM_ANSI_COLORS);
4767d4fba8b9Smrg			} else
4768d4fba8b9Smrg#if OPT_DIRECT_COLOR
4769d4fba8b9Smrg			if (code == XK_RGB) {
4770d4fba8b9Smrg			    if (TScreenOf(xw)->direct_color && xw->has_rgb) {
4771d4fba8b9Smrg				if (xw->rgb_widths[0] == xw->rgb_widths[1] &&
4772d4fba8b9Smrg				    xw->rgb_widths[1] == xw->rgb_widths[2]) {
4773d4fba8b9Smrg				    unparseputn(xw, xw->rgb_widths[0]);
4774d4fba8b9Smrg				} else {
4775d4fba8b9Smrg				    char temp[1024];
4776d4fba8b9Smrg				    sprintf(temp, "%d/%d/%d",
4777d4fba8b9Smrg					    xw->rgb_widths[0],
4778d4fba8b9Smrg					    xw->rgb_widths[1],
4779d4fba8b9Smrg					    xw->rgb_widths[2]);
4780d4fba8b9Smrg				    unparseputs(xw, temp);
4781d4fba8b9Smrg				}
4782d4fba8b9Smrg			    } else {
4783d4fba8b9Smrg				unparseputs(xw, "-1");
4784d4fba8b9Smrg			    }
4785cd3331d0Smrg			} else
4786d4fba8b9Smrg#endif
4787cd3331d0Smrg#endif
4788cd3331d0Smrg			if (code == XK_TCAPNAME) {
4789c219fbebSmrg			    unparseputs(xw, resource.term_name);
4790cd3331d0Smrg			} else {
4791cd3331d0Smrg			    XKeyEvent event;
4792d4fba8b9Smrg			    memset(&event, 0, sizeof(event));
4793cd3331d0Smrg			    event.state = state;
4794cd3331d0Smrg			    Input(xw, &event, False);
4795cd3331d0Smrg			}
4796cd3331d0Smrg			screen->tc_query_code = -1;
4797cd3331d0Smrg		    } else {
4798cd3331d0Smrg			break;	/* no match found, error */
4799d522f475Smrg		    }
4800d522f475Smrg
4801d522f475Smrg		    cp = parsed;
4802cd3331d0Smrg		    if (*parsed == ';') {
4803cd3331d0Smrg			unparseputc(xw, *parsed++);
4804cd3331d0Smrg			cp = parsed;
4805cd3331d0Smrg			code = xtermcapKeycode(xw, &parsed, &state, &fkey);
4806cd3331d0Smrg		    }
4807d522f475Smrg		}
4808cd3331d0Smrg		unparseputc1(xw, ANSI_ST);
4809d522f475Smrg	    }
4810cd3331d0Smrg	    break;
4811d4fba8b9Smrg#endif
4812d4fba8b9Smrg#if OPT_XRES_QUERY
4813d4fba8b9Smrg	case 'Q':
4814d4fba8b9Smrg	    ++cp;
4815d4fba8b9Smrg	    if (AllowXResOps(xw)) {
4816d4fba8b9Smrg		Boolean first = True;
4817d4fba8b9Smrg		while (*cp != '\0') {
4818d4fba8b9Smrg		    const char *parsed = 0;
4819d4fba8b9Smrg		    const char *tmp;
4820d4fba8b9Smrg		    char *name = x_decode_hex(cp, &parsed);
4821d4fba8b9Smrg		    char *value;
4822d4fba8b9Smrg		    char *result;
4823d4fba8b9Smrg		    if (cp == parsed || name == NULL) {
4824d4fba8b9Smrg			free(name);
4825d4fba8b9Smrg			break;	/* no data found, error */
4826d4fba8b9Smrg		    }
4827d4fba8b9Smrg		    TRACE(("query-feature '%s'\n", name));
4828d4fba8b9Smrg		    if ((value = vt100ResourceToString(xw, name)) != 0) {
4829d4fba8b9Smrg			okay = True;	/* valid */
4830d4fba8b9Smrg		    } else {
4831d4fba8b9Smrg			okay = False;	/* invalid */
4832d4fba8b9Smrg		    }
4833d4fba8b9Smrg		    if (first) {
4834d4fba8b9Smrg			unparseputc1(xw, ANSI_DCS);
4835d4fba8b9Smrg			unparseputc(xw, okay ? '1' : '0');
4836d4fba8b9Smrg			unparseputc(xw, '+');
4837d4fba8b9Smrg			unparseputc(xw, 'R');
4838d4fba8b9Smrg			first = False;
4839d4fba8b9Smrg		    }
4840d4fba8b9Smrg
4841d4fba8b9Smrg		    for (tmp = cp; tmp != parsed; ++tmp)
4842d4fba8b9Smrg			unparseputc(xw, *tmp);
4843d4fba8b9Smrg
4844d4fba8b9Smrg		    if (value != 0) {
4845d4fba8b9Smrg			unparseputc1(xw, '=');
4846d4fba8b9Smrg			result = x_encode_hex(value);
4847d4fba8b9Smrg			unparseputs(xw, result);
4848d4fba8b9Smrg		    } else {
4849d4fba8b9Smrg			result = NULL;
4850d4fba8b9Smrg		    }
4851d4fba8b9Smrg
4852d4fba8b9Smrg		    free(name);
4853d4fba8b9Smrg		    free(value);
4854d4fba8b9Smrg		    free(result);
4855d4fba8b9Smrg
4856d4fba8b9Smrg		    cp = parsed;
4857d4fba8b9Smrg		    if (*parsed == ';') {
4858d4fba8b9Smrg			unparseputc(xw, *parsed++);
4859d4fba8b9Smrg			cp = parsed;
4860d4fba8b9Smrg		    }
4861d4fba8b9Smrg		}
4862d4fba8b9Smrg		if (!first)
4863d4fba8b9Smrg		    unparseputc1(xw, ANSI_ST);
4864d4fba8b9Smrg	    }
4865d4fba8b9Smrg	    break;
4866d4fba8b9Smrg#endif
4867d522f475Smrg	}
4868d522f475Smrg	break;
4869d4fba8b9Smrg#if OPT_DEC_RECTOPS
4870d4fba8b9Smrg    case '1':
4871d4fba8b9Smrg	/* FALLTHRU */
4872d4fba8b9Smrg    case '2':
4873d4fba8b9Smrg	if (*skip_params(cp) == '$') {
4874d4fba8b9Smrg	    psarg = *cp++;
4875d4fba8b9Smrg	    if ((*cp++ == '$')
4876d4fba8b9Smrg		&& (*cp++ == 't')
4877d4fba8b9Smrg		&& (screen->vtXX_level >= 3)) {
4878d4fba8b9Smrg		switch (psarg) {
4879d4fba8b9Smrg		case '1':
4880d4fba8b9Smrg		    TRACE(("DECRSPS (DECCIR)\n"));
4881d4fba8b9Smrg		    restore_DECCIR(xw, cp);
4882d4fba8b9Smrg		    break;
4883d4fba8b9Smrg		case '2':
4884d4fba8b9Smrg		    TRACE(("DECRSPS (DECTABSR)\n"));
4885d4fba8b9Smrg		    restore_DECTABSR(xw, cp);
4886d4fba8b9Smrg		    break;
4887d4fba8b9Smrg		}
4888d4fba8b9Smrg	    }
4889d4fba8b9Smrg	    break;
4890d4fba8b9Smrg	}
4891d522f475Smrg#endif
4892d4fba8b9Smrg	/* FALLTHRU */
4893d522f475Smrg    default:
4894d4fba8b9Smrg	if (optRegisGraphics(screen) ||
4895d4fba8b9Smrg	    optSixelGraphics(screen) ||
4896fa3f02f3Smrg	    screen->vtXX_level >= 2) {	/* VT220 */
48970d92cbfdSchristos	    parse_ansi_params(&params, &cp);
48980d92cbfdSchristos	    switch (params.a_final) {
4899d4fba8b9Smrg	    case 'p':		/* ReGIS */
49009a64e1c5Smrg#if OPT_REGIS_GRAPHICS
4901d4fba8b9Smrg		if (optRegisGraphics(screen)) {
4902fa3f02f3Smrg		    parse_regis(xw, &params, cp);
4903fa3f02f3Smrg		}
49049a64e1c5Smrg#else
49059a64e1c5Smrg		TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n"));
49069a64e1c5Smrg#endif
4907fa3f02f3Smrg		break;
4908d4fba8b9Smrg	    case 'q':		/* sixel */
49099a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
4910d4fba8b9Smrg		if (optSixelGraphics(screen)) {
4911037a25ddSmrg		    (void) parse_sixel(xw, &params, cp);
4912fa3f02f3Smrg		}
49139a64e1c5Smrg#else
49149a64e1c5Smrg		TRACE(("ignoring sixel graphic (compilation flag not enabled)\n"));
4915fa3f02f3Smrg#endif
49169a64e1c5Smrg		break;
49170d92cbfdSchristos	    case '|':		/* DECUDK */
49189a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
49199a64e1c5Smrg		    if (params.a_param[0] == 0)
49209a64e1c5Smrg			reset_decudk(xw);
49219a64e1c5Smrg		    parse_decudk(xw, cp);
49229a64e1c5Smrg		}
49230d92cbfdSchristos		break;
492494644356Smrg	    case L_CURL:	/* DECDLD */
49259a64e1c5Smrg		if (screen->vtXX_level >= 2) {	/* VT220 */
49269a64e1c5Smrg		    parse_decdld(&params, cp);
49279a64e1c5Smrg		}
49280d92cbfdSchristos		break;
49290d92cbfdSchristos	    }
4930d522f475Smrg	}
4931d522f475Smrg	break;
4932d522f475Smrg    }
4933d522f475Smrg    unparse_end(xw);
4934d522f475Smrg}
4935d522f475Smrg
4936cb4a1343Smrg#if OPT_DEC_RECTOPS
4937cb4a1343Smrgenum {
4938cb4a1343Smrg    mdUnknown = 0,
4939cb4a1343Smrg    mdMaybeSet = 1,
4940cb4a1343Smrg    mdMaybeReset = 2,
4941cb4a1343Smrg    mdAlwaysSet = 3,
4942cb4a1343Smrg    mdAlwaysReset = 4
4943cb4a1343Smrg};
4944cb4a1343Smrg
4945cb4a1343Smrg#define MdBool(bool)      ((bool) ? mdMaybeSet : mdMaybeReset)
49463367019cSmrg#define MdFlag(mode,flag) MdBool((mode) & (flag))
4947cb4a1343Smrg
4948cb4a1343Smrg/*
4949cb4a1343Smrg * Reply is the same format as the query, with pair of mode/value:
4950cb4a1343Smrg * 0 - not recognized
4951cb4a1343Smrg * 1 - set
4952cb4a1343Smrg * 2 - reset
4953cb4a1343Smrg * 3 - permanently set
4954cb4a1343Smrg * 4 - permanently reset
4955cb4a1343Smrg * Only one mode can be reported at a time.
4956cb4a1343Smrg */
4957cb4a1343Smrgvoid
4958d4fba8b9Smrgdo_ansi_rqm(XtermWidget xw, int nparams, int *params)
4959cb4a1343Smrg{
4960cb4a1343Smrg    ANSI reply;
4961cb4a1343Smrg    int count = 0;
4962cb4a1343Smrg
4963d4fba8b9Smrg    TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0]));
4964cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
4965037a25ddSmrg
4966cb4a1343Smrg    if (nparams >= 1) {
4967d4fba8b9Smrg	int result = mdUnknown;
4968037a25ddSmrg
4969d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
4970cb4a1343Smrg	switch (params[0]) {
4971cb4a1343Smrg	case 1:		/* GATM */
4972cb4a1343Smrg	    result = mdAlwaysReset;
4973cb4a1343Smrg	    break;
4974cb4a1343Smrg	case 2:
4975cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_KAM);
4976cb4a1343Smrg	    break;
4977cb4a1343Smrg	case 3:		/* CRM */
4978cb4a1343Smrg	    result = mdMaybeReset;
4979cb4a1343Smrg	    break;
4980cb4a1343Smrg	case 4:
4981cb4a1343Smrg	    result = MdFlag(xw->flags, INSERT);
4982cb4a1343Smrg	    break;
4983cb4a1343Smrg	case 5:		/* SRTM */
4984cb4a1343Smrg	case 7:		/* VEM */
4985cb4a1343Smrg	case 10:		/* HEM */
4986cb4a1343Smrg	case 11:		/* PUM */
4987cb4a1343Smrg	    result = mdAlwaysReset;
4988cb4a1343Smrg	    break;
4989cb4a1343Smrg	case 12:
4990cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_SRM);
4991cb4a1343Smrg	    break;
4992cb4a1343Smrg	case 13:		/* FEAM */
4993cb4a1343Smrg	case 14:		/* FETM */
4994cb4a1343Smrg	case 15:		/* MATM */
4995cb4a1343Smrg	case 16:		/* TTM */
4996cb4a1343Smrg	case 17:		/* SATM */
4997cb4a1343Smrg	case 18:		/* TSM */
4998cb4a1343Smrg	case 19:		/* EBM */
4999cb4a1343Smrg	    result = mdAlwaysReset;
5000cb4a1343Smrg	    break;
5001cb4a1343Smrg	case 20:
5002cb4a1343Smrg	    result = MdFlag(xw->flags, LINEFEED);
5003cb4a1343Smrg	    break;
5004cb4a1343Smrg	}
5005cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5006cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5007cb4a1343Smrg    }
5008cb4a1343Smrg    reply.a_type = ANSI_CSI;
5009cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5010cb4a1343Smrg    reply.a_inters = '$';
5011cb4a1343Smrg    reply.a_final = 'y';
5012cb4a1343Smrg    unparseseq(xw, &reply);
5013cb4a1343Smrg}
5014cb4a1343Smrg
5015cb4a1343Smrgvoid
5016d4fba8b9Smrgdo_dec_rqm(XtermWidget xw, int nparams, int *params)
5017cb4a1343Smrg{
5018cb4a1343Smrg    ANSI reply;
5019cb4a1343Smrg    int count = 0;
5020cb4a1343Smrg
5021d4fba8b9Smrg    TRACE(("do_dec_rqm %d:%d\n", nparams, params[0]));
5022cb4a1343Smrg    memset(&reply, 0, sizeof(reply));
5023037a25ddSmrg
5024cb4a1343Smrg    if (nparams >= 1) {
5025cb4a1343Smrg	TScreen *screen = TScreenOf(xw);
5026d4fba8b9Smrg	int result = mdUnknown;
5027cb4a1343Smrg
5028d4fba8b9Smrg	/* DECRQM can only ask about one mode at a time */
5029d4fba8b9Smrg	switch ((DECSET_codes) params[0]) {
5030fa3f02f3Smrg	case srm_DECCKM:
5031cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECCKM);
5032cb4a1343Smrg	    break;
5033fa3f02f3Smrg	case srm_DECANM:	/* ANSI/VT52 mode      */
5034cb4a1343Smrg#if OPT_VT52_MODE
50353367019cSmrg	    result = MdBool(screen->vtXX_level >= 1);
5036cb4a1343Smrg#else
5037cb4a1343Smrg	    result = mdMaybeSet;
5038cb4a1343Smrg#endif
5039cb4a1343Smrg	    break;
5040fa3f02f3Smrg	case srm_DECCOLM:
5041cb4a1343Smrg	    result = MdFlag(xw->flags, IN132COLUMNS);
5042cb4a1343Smrg	    break;
5043fa3f02f3Smrg	case srm_DECSCLM:	/* (slow scroll)        */
5044cb4a1343Smrg	    result = MdFlag(xw->flags, SMOOTHSCROLL);
5045cb4a1343Smrg	    break;
5046fa3f02f3Smrg	case srm_DECSCNM:
5047cb4a1343Smrg	    result = MdFlag(xw->flags, REVERSE_VIDEO);
5048cb4a1343Smrg	    break;
5049fa3f02f3Smrg	case srm_DECOM:
5050cb4a1343Smrg	    result = MdFlag(xw->flags, ORIGIN);
5051cb4a1343Smrg	    break;
5052fa3f02f3Smrg	case srm_DECAWM:
5053cb4a1343Smrg	    result = MdFlag(xw->flags, WRAPAROUND);
5054cb4a1343Smrg	    break;
5055fa3f02f3Smrg	case srm_DECARM:
5056cb4a1343Smrg	    result = mdAlwaysReset;
5057cb4a1343Smrg	    break;
5058fa3f02f3Smrg	case srm_X10_MOUSE:	/* X10 mouse                    */
5059cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == X10_MOUSE);
5060cb4a1343Smrg	    break;
5061cb4a1343Smrg#if OPT_TOOLBAR
5062fa3f02f3Smrg	case srm_RXVT_TOOLBAR:
5063cb4a1343Smrg	    result = MdBool(resource.toolBar);
5064cb4a1343Smrg	    break;
5065cb4a1343Smrg#endif
5066cb4a1343Smrg#if OPT_BLINK_CURS
5067d4fba8b9Smrg	case srm_ATT610_BLINK:	/* AT&T 610: Start/stop blinking cursor */
5068d4fba8b9Smrg	    result = MdBool(screen->cursor_blink_esc);
5069d4fba8b9Smrg	    break;
5070d4fba8b9Smrg	case srm_CURSOR_BLINK_OPS:
5071d4fba8b9Smrg	    switch (screen->cursor_blink) {
5072d4fba8b9Smrg	    case cbTrue:
5073d4fba8b9Smrg		result = mdMaybeSet;
5074d4fba8b9Smrg		break;
5075d4fba8b9Smrg	    case cbFalse:
5076d4fba8b9Smrg		result = mdMaybeReset;
5077d4fba8b9Smrg		break;
5078d4fba8b9Smrg	    case cbAlways:
5079d4fba8b9Smrg		result = mdAlwaysSet;
5080d4fba8b9Smrg		break;
5081d4fba8b9Smrg	    case cbLAST:
5082d4fba8b9Smrg		/* FALLTHRU */
5083d4fba8b9Smrg	    case cbNever:
5084d4fba8b9Smrg		result = mdAlwaysReset;
5085d4fba8b9Smrg		break;
5086d4fba8b9Smrg	    }
5087d4fba8b9Smrg	    break;
5088d4fba8b9Smrg	case srm_XOR_CURSOR_BLINKS:
5089d4fba8b9Smrg	    result = (screen->cursor_blink_xor
5090d4fba8b9Smrg		      ? mdAlwaysSet
5091d4fba8b9Smrg		      : mdAlwaysReset);
5092cb4a1343Smrg	    break;
5093cb4a1343Smrg#endif
5094fa3f02f3Smrg	case srm_DECPFF:	/* print form feed */
5095712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_formfeed);
5096cb4a1343Smrg	    break;
5097fa3f02f3Smrg	case srm_DECPEX:	/* print extent */
5098712a7ff4Smrg	    result = MdBool(PrinterOf(screen).printer_extent);
5099cb4a1343Smrg	    break;
5100fa3f02f3Smrg	case srm_DECTCEM:	/* Show/hide cursor (VT200) */
5101cb4a1343Smrg	    result = MdBool(screen->cursor_set);
5102cb4a1343Smrg	    break;
5103fa3f02f3Smrg	case srm_RXVT_SCROLLBAR:
5104cb4a1343Smrg	    result = MdBool(screen->fullVwin.sb_info.width != OFF);
5105cb4a1343Smrg	    break;
5106cb4a1343Smrg#if OPT_SHIFT_FONTS
5107fa3f02f3Smrg	case srm_RXVT_FONTSIZE:
5108cb4a1343Smrg	    result = MdBool(xw->misc.shift_fonts);
5109cb4a1343Smrg	    break;
5110cb4a1343Smrg#endif
5111cb4a1343Smrg#if OPT_TEK4014
5112fa3f02f3Smrg	case srm_DECTEK:
5113cb4a1343Smrg	    result = MdBool(TEK4014_ACTIVE(xw));
5114cb4a1343Smrg	    break;
5115cb4a1343Smrg#endif
5116fa3f02f3Smrg	case srm_132COLS:
5117cb4a1343Smrg	    result = MdBool(screen->c132);
5118cb4a1343Smrg	    break;
5119fa3f02f3Smrg	case srm_CURSES_HACK:
5120cb4a1343Smrg	    result = MdBool(screen->curses);
5121cb4a1343Smrg	    break;
5122fa3f02f3Smrg	case srm_DECNRCM:	/* national charset (VT220) */
5123d4fba8b9Smrg	    if (screen->vtXX_level >= 2) {
5124d4fba8b9Smrg		result = MdFlag(xw->flags, NATIONAL);
5125d4fba8b9Smrg	    } else {
5126d4fba8b9Smrg		result = 0;
5127d4fba8b9Smrg	    }
5128cb4a1343Smrg	    break;
5129fa3f02f3Smrg	case srm_MARGIN_BELL:	/* margin bell                  */
5130cb4a1343Smrg	    result = MdBool(screen->marginbell);
5131cb4a1343Smrg	    break;
5132d4fba8b9Smrg#if OPT_PRINT_GRAPHICS
5133d4fba8b9Smrg	case srm_DECGEPM:	/* Graphics Expanded Print Mode */
5134d4fba8b9Smrg	    result = MdBool(screen->graphics_expanded_print_mode);
5135d4fba8b9Smrg	    break;
5136d4fba8b9Smrg#endif
5137fa3f02f3Smrg	case srm_REVERSEWRAP:	/* reverse wraparound   */
5138d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax))
5139d4fba8b9Smrg		result = MdFlag(xw->flags, REVERSEWRAP);
5140cb4a1343Smrg	    break;
5141d4fba8b9Smrg#if defined(ALLOWLOGGING)
5142fa3f02f3Smrg	case srm_ALLOWLOGGING:	/* logging              */
5143d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5144d4fba8b9Smrg#if defined(ALLOWLOGFILEONOFF)
5145d4fba8b9Smrg		result = MdBool(screen->logging);
5146d4fba8b9Smrg#else
5147d4fba8b9Smrg		result = ((MdBool(screen->logging) == mdMaybeSet)
5148d4fba8b9Smrg			  ? mdAlwaysSet
5149d4fba8b9Smrg			  : mdAlwaysReset);
5150d4fba8b9Smrg#endif
5151d4fba8b9Smrg	    break;
5152d4fba8b9Smrg#elif OPT_PRINT_GRAPHICS
5153d4fba8b9Smrg	case srm_DECGPBM:	/* Graphics Print Background Mode */
5154d4fba8b9Smrg	    result = MdBool(screen->graphics_print_background_mode);
5155cb4a1343Smrg	    break;
5156cb4a1343Smrg#endif
5157fa3f02f3Smrg	case srm_OPT_ALTBUF_CURSOR:	/* alternate buffer & cursor */
5158cb4a1343Smrg	    /* FALLTHRU */
5159fa3f02f3Smrg	case srm_OPT_ALTBUF:
5160cb4a1343Smrg	    result = MdBool(screen->whichBuf);
5161cb4a1343Smrg	    break;
5162d4fba8b9Smrg	case srm_ALTBUF:
5163d4fba8b9Smrg	    if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode))
5164d4fba8b9Smrg		result = MdBool(screen->whichBuf);
5165d4fba8b9Smrg	    break;
5166fa3f02f3Smrg	case srm_DECNKM:
5167cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECKPAM);
5168cb4a1343Smrg	    break;
5169fa3f02f3Smrg	case srm_DECBKM:
5170cb4a1343Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECBKM);
5171cb4a1343Smrg	    break;
5172fa3f02f3Smrg	case srm_DECLRMM:
5173d4fba8b9Smrg	    if (screen->vtXX_level >= 4) {	/* VT420 */
5174d4fba8b9Smrg		result = MdFlag(xw->flags, LEFT_RIGHT);
5175d4fba8b9Smrg	    } else {
5176d4fba8b9Smrg		result = 0;
5177d4fba8b9Smrg	    }
51783367019cSmrg	    break;
5179fa3f02f3Smrg#if OPT_SIXEL_GRAPHICS
5180fa3f02f3Smrg	case srm_DECSDM:
5181fa3f02f3Smrg	    result = MdFlag(xw->keyboard.flags, MODE_DECSDM);
5182fa3f02f3Smrg	    break;
5183fa3f02f3Smrg#endif
5184fa3f02f3Smrg	case srm_DECNCSM:
5185d4fba8b9Smrg	    if (screen->vtXX_level >= 5) {	/* VT510 */
5186d4fba8b9Smrg		result = MdFlag(xw->flags, NOCLEAR_COLM);
5187d4fba8b9Smrg	    } else {
5188d4fba8b9Smrg		result = 0;
5189d4fba8b9Smrg	    }
51903367019cSmrg	    break;
5191d4fba8b9Smrg	case srm_VT200_MOUSE:	/* xterm bogus sequence */
5192cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_MOUSE);
5193cb4a1343Smrg	    break;
5194fa3f02f3Smrg	case srm_VT200_HIGHLIGHT_MOUSE:	/* xterm sequence w/hilite tracking */
5195cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE);
5196cb4a1343Smrg	    break;
5197fa3f02f3Smrg	case srm_BTN_EVENT_MOUSE:
5198cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE);
5199cb4a1343Smrg	    break;
5200fa3f02f3Smrg	case srm_ANY_EVENT_MOUSE:
5201cb4a1343Smrg	    result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE);
5202cb4a1343Smrg	    break;
5203cb4a1343Smrg#if OPT_FOCUS_EVENT
5204fa3f02f3Smrg	case srm_FOCUS_EVENT_MOUSE:
5205cb4a1343Smrg	    result = MdBool(screen->send_focus_pos);
5206cb4a1343Smrg	    break;
5207cb4a1343Smrg#endif
5208fa3f02f3Smrg	case srm_EXT_MODE_MOUSE:
52093367019cSmrg	    /* FALLTHRU */
5210fa3f02f3Smrg	case srm_SGR_EXT_MODE_MOUSE:
52113367019cSmrg	    /* FALLTHRU */
5212fa3f02f3Smrg	case srm_URXVT_EXT_MODE_MOUSE:
5213d4fba8b9Smrg	    /* FALLTHRU */
5214d4fba8b9Smrg	case srm_PIXEL_POSITION_MOUSE:
52153367019cSmrg	    result = MdBool(screen->extend_coords == params[0]);
52163367019cSmrg	    break;
5217fa3f02f3Smrg	case srm_ALTERNATE_SCROLL:
52183367019cSmrg	    result = MdBool(screen->alternateScroll);
5219cb4a1343Smrg	    break;
5220fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_OUTPUT:
5221cb4a1343Smrg	    result = MdBool(screen->scrollttyoutput);
5222cb4a1343Smrg	    break;
5223fa3f02f3Smrg	case srm_RXVT_SCROLL_TTY_KEYPRESS:
5224cb4a1343Smrg	    result = MdBool(screen->scrollkey);
5225cb4a1343Smrg	    break;
5226fa3f02f3Smrg	case srm_EIGHT_BIT_META:
52273367019cSmrg	    result = MdBool(screen->eight_bit_meta);
5228cb4a1343Smrg	    break;
5229cb4a1343Smrg#if OPT_NUM_LOCK
5230fa3f02f3Smrg	case srm_REAL_NUMLOCK:
5231cb4a1343Smrg	    result = MdBool(xw->misc.real_NumLock);
5232cb4a1343Smrg	    break;
5233fa3f02f3Smrg	case srm_META_SENDS_ESC:
5234cb4a1343Smrg	    result = MdBool(screen->meta_sends_esc);
5235cb4a1343Smrg	    break;
5236cb4a1343Smrg#endif
5237fa3f02f3Smrg	case srm_DELETE_IS_DEL:
5238d4fba8b9Smrg	    result = MdBool(xtermDeleteIsDEL(xw));
5239cb4a1343Smrg	    break;
5240cb4a1343Smrg#if OPT_NUM_LOCK
5241fa3f02f3Smrg	case srm_ALT_SENDS_ESC:
5242cb4a1343Smrg	    result = MdBool(screen->alt_sends_esc);
5243cb4a1343Smrg	    break;
5244cb4a1343Smrg#endif
5245fa3f02f3Smrg	case srm_KEEP_SELECTION:
5246cb4a1343Smrg	    result = MdBool(screen->keepSelection);
5247cb4a1343Smrg	    break;
5248fa3f02f3Smrg	case srm_SELECT_TO_CLIPBOARD:
5249cb4a1343Smrg	    result = MdBool(screen->selectToClipboard);
5250cb4a1343Smrg	    break;
5251fa3f02f3Smrg	case srm_BELL_IS_URGENT:
5252cb4a1343Smrg	    result = MdBool(screen->bellIsUrgent);
5253cb4a1343Smrg	    break;
5254fa3f02f3Smrg	case srm_POP_ON_BELL:
5255cb4a1343Smrg	    result = MdBool(screen->poponbell);
5256cb4a1343Smrg	    break;
5257d4fba8b9Smrg	case srm_KEEP_CLIPBOARD:
5258d4fba8b9Smrg	    result = MdBool(screen->keepClipboard);
5259d4fba8b9Smrg	    break;
5260d4fba8b9Smrg	case srm_ALLOW_ALTBUF:
5261d4fba8b9Smrg	    result = MdBool(xw->misc.titeInhibit);
5262d4fba8b9Smrg	    break;
5263d4fba8b9Smrg	case srm_SAVE_CURSOR:
5264cb4a1343Smrg	    result = MdBool(screen->sc[screen->whichBuf].saved);
5265cb4a1343Smrg	    break;
5266cb4a1343Smrg#if OPT_TCAP_FKEYS
5267fa3f02f3Smrg	case srm_TCAP_FKEYS:
5268cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsTermcap);
5269cb4a1343Smrg	    break;
5270cb4a1343Smrg#endif
5271cb4a1343Smrg#if OPT_SUN_FUNC_KEYS
5272fa3f02f3Smrg	case srm_SUN_FKEYS:
5273cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSun);
5274cb4a1343Smrg	    break;
5275cb4a1343Smrg#endif
5276cb4a1343Smrg#if OPT_HP_FUNC_KEYS
5277fa3f02f3Smrg	case srm_HP_FKEYS:
5278cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsHP);
5279cb4a1343Smrg	    break;
5280cb4a1343Smrg#endif
5281cb4a1343Smrg#if OPT_SCO_FUNC_KEYS
5282fa3f02f3Smrg	case srm_SCO_FKEYS:
5283cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsSCO);
5284cb4a1343Smrg	    break;
5285cb4a1343Smrg#endif
5286fa3f02f3Smrg	case srm_LEGACY_FKEYS:
5287cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsLegacy);
5288cb4a1343Smrg	    break;
5289cb4a1343Smrg#if OPT_SUNPC_KBD
5290fa3f02f3Smrg	case srm_VT220_FKEYS:
5291cb4a1343Smrg	    result = MdBool(xw->keyboard.type == keyboardIsVT220);
5292cb4a1343Smrg	    break;
5293cb4a1343Smrg#endif
5294d4fba8b9Smrg#if OPT_PASTE64 || OPT_READLINE
5295d4fba8b9Smrg	case srm_PASTE_IN_BRACKET:
5296d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_brackets));
5297d4fba8b9Smrg	    break;
5298d4fba8b9Smrg#endif
5299cb4a1343Smrg#if OPT_READLINE
5300fa3f02f3Smrg	case srm_BUTTON1_MOVE_POINT:
5301d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, click1_moves));
5302cb4a1343Smrg	    break;
5303fa3f02f3Smrg	case srm_BUTTON2_MOVE_POINT:
5304d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_moves));
5305cb4a1343Smrg	    break;
5306fa3f02f3Smrg	case srm_DBUTTON3_DELETE:
5307d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, dclick3_deletes));
5308cb4a1343Smrg	    break;
5309fa3f02f3Smrg	case srm_PASTE_QUOTE:
5310d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_quotes));
5311cb4a1343Smrg	    break;
5312fa3f02f3Smrg	case srm_PASTE_LITERAL_NL:
5313d4fba8b9Smrg	    result = MdBool(SCREEN_FLAG(screen, paste_literal_nl));
5314cb4a1343Smrg	    break;
5315cb4a1343Smrg#endif /* OPT_READLINE */
5316d4fba8b9Smrg#if OPT_GRAPHICS
53179a64e1c5Smrg	case srm_PRIVATE_COLOR_REGISTERS:
53189a64e1c5Smrg	    result = MdBool(screen->privatecolorregisters);
53199a64e1c5Smrg	    break;
53209a64e1c5Smrg#endif
53219a64e1c5Smrg#if OPT_SIXEL_GRAPHICS
53229a64e1c5Smrg	case srm_SIXEL_SCROLLS_RIGHT:
53239a64e1c5Smrg	    result = MdBool(screen->sixel_scrolls_right);
53249a64e1c5Smrg	    break;
53259a64e1c5Smrg#endif
53269a64e1c5Smrg	default:
53279a64e1c5Smrg	    TRACE(("DATA_ERROR: requested report for unknown private mode %d\n",
53289a64e1c5Smrg		   params[0]));
5329cb4a1343Smrg	}
5330cb4a1343Smrg	reply.a_param[count++] = (ParmType) params[0];
5331cb4a1343Smrg	reply.a_param[count++] = (ParmType) result;
5332d4fba8b9Smrg	TRACE(("DECRPM(%d) = %d\n", params[0], result));
5333cb4a1343Smrg    }
5334cb4a1343Smrg    reply.a_type = ANSI_CSI;
5335cb4a1343Smrg    reply.a_pintro = '?';
5336cb4a1343Smrg    reply.a_nparam = (ParmType) count;
5337cb4a1343Smrg    reply.a_inters = '$';
5338cb4a1343Smrg    reply.a_final = 'y';
5339cb4a1343Smrg    unparseseq(xw, &reply);
5340cb4a1343Smrg}
5341cb4a1343Smrg#endif /* OPT_DEC_RECTOPS */
5342cb4a1343Smrg
5343d522f475Smrgchar *
53449a64e1c5Smrgudk_lookup(XtermWidget xw, int keycode, int *len)
5345d522f475Smrg{
5346d4fba8b9Smrg    char *result = NULL;
5347d522f475Smrg    if (keycode >= 0 && keycode < MAX_UDK) {
53489a64e1c5Smrg	*len = xw->work.user_keys[keycode].len;
5349d4fba8b9Smrg	result = xw->work.user_keys[keycode].str;
5350d4fba8b9Smrg	TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result));
5351d4fba8b9Smrg    } else {
5352d4fba8b9Smrg	TRACE(("udk_lookup(%d) = <null>\n", keycode));
5353d4fba8b9Smrg    }
5354d4fba8b9Smrg    return result;
5355d4fba8b9Smrg}
5356d4fba8b9Smrg
5357d4fba8b9Smrg#if OPT_REPORT_ICONS
5358d4fba8b9Smrgvoid
5359d4fba8b9Smrgreport_icons(const char *fmt, ...)
5360d4fba8b9Smrg{
5361d4fba8b9Smrg    if (resource.reportIcons) {
5362d4fba8b9Smrg	va_list ap;
5363d4fba8b9Smrg	va_start(ap, fmt);
5364d4fba8b9Smrg	vfprintf(stdout, fmt, ap);
5365d4fba8b9Smrg	va_end(ap);
5366d4fba8b9Smrg#if OPT_TRACE
5367d4fba8b9Smrg	va_start(ap, fmt);
5368d4fba8b9Smrg	TraceVA(fmt, ap);
5369d4fba8b9Smrg	va_end(ap);
5370d4fba8b9Smrg#endif
5371d522f475Smrg    }
5372d522f475Smrg}
5373d4fba8b9Smrg#endif
5374d522f475Smrg
53753367019cSmrg#ifdef HAVE_LIBXPM
53763367019cSmrg
53773367019cSmrg#ifndef PIXMAP_ROOTDIR
53783367019cSmrg#define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
53793367019cSmrg#endif
53803367019cSmrg
53813367019cSmrgtypedef struct {
53823367019cSmrg    const char *name;
53833367019cSmrg    const char *const *data;
53843367019cSmrg} XPM_DATA;
53853367019cSmrg
53863367019cSmrgstatic char *
5387d4fba8b9Smrgx_find_icon(char **work, int *state, const char *filename, const char *suffix)
53883367019cSmrg{
53893367019cSmrg    const char *prefix = PIXMAP_ROOTDIR;
53903367019cSmrg    const char *larger = "_48x48";
53913367019cSmrg    char *result = 0;
53923367019cSmrg
53933367019cSmrg    if (*state >= 0) {
53943367019cSmrg	if ((*state & 1) == 0)
53953367019cSmrg	    suffix = "";
53963367019cSmrg	if ((*state & 2) == 0)
53973367019cSmrg	    larger = "";
53983367019cSmrg	if ((*state & 4) == 0) {
53993367019cSmrg	    prefix = "";
54003367019cSmrg	} else if (!strncmp(filename, "/", (size_t) 1) ||
54013367019cSmrg		   !strncmp(filename, "./", (size_t) 2) ||
54023367019cSmrg		   !strncmp(filename, "../", (size_t) 3)) {
54033367019cSmrg	    *state = -1;
54043367019cSmrg	} else if (*state >= 8) {
54053367019cSmrg	    *state = -1;
54063367019cSmrg	}
54073367019cSmrg    }
54083367019cSmrg
54093367019cSmrg    if (*state >= 0) {
5410037a25ddSmrg	size_t length;
5411037a25ddSmrg
5412d4fba8b9Smrg	FreeAndNull(*work);
54133367019cSmrg	length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) +
54143367019cSmrg	    strlen(suffix);
54153367019cSmrg	if ((result = malloc(length)) != 0) {
54163367019cSmrg	    sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix);
54173367019cSmrg	    *work = result;
54183367019cSmrg	}
54193367019cSmrg	*state += 1;
54203367019cSmrg    }
5421d4fba8b9Smrg    TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result)));
54223367019cSmrg    return result;
54233367019cSmrg}
54243367019cSmrg
54253367019cSmrg#if OPT_BUILTIN_XPMS
5426d4fba8b9Smrg
54273367019cSmrgstatic const XPM_DATA *
5428d4fba8b9Smrgbuilt_in_xpm(const XPM_DATA * table, Cardinal length, const char *find)
54293367019cSmrg{
54303367019cSmrg    const XPM_DATA *result = 0;
54313367019cSmrg    if (!IsEmpty(find)) {
54323367019cSmrg	Cardinal n;
54333367019cSmrg	for (n = 0; n < length; ++n) {
54343367019cSmrg	    if (!x_strcasecmp(find, table[n].name)) {
54353367019cSmrg		result = table + n;
5436d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[n].name));
54373367019cSmrg		break;
54383367019cSmrg	    }
54393367019cSmrg	}
54403367019cSmrg
54413367019cSmrg	/*
54423367019cSmrg	 * As a fallback, check if the icon name matches without the lengths,
54433367019cSmrg	 * which are all _HHxWW format.
54443367019cSmrg	 */
54453367019cSmrg	if (result == 0) {
54463367019cSmrg	    const char *base = table[0].name;
54473367019cSmrg	    const char *last = strchr(base, '_');
54483367019cSmrg	    if (last != 0
54493367019cSmrg		&& !x_strncasecmp(find, base, (unsigned) (last - base))) {
54503367019cSmrg		result = table + length - 1;
5451d4fba8b9Smrg		ReportIcons(("use builtin-icon %s\n", table[0].name));
54523367019cSmrg	    }
54533367019cSmrg	}
54543367019cSmrg    }
54553367019cSmrg    return result;
54563367019cSmrg}
5457d4fba8b9Smrg#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint)
54583367019cSmrg#endif /* OPT_BUILTIN_XPMS */
54593367019cSmrg
54603367019cSmrgtypedef enum {
54613367019cSmrg    eHintDefault = 0		/* use the largest builtin-icon */
54623367019cSmrg    ,eHintNone
54633367019cSmrg    ,eHintSearch
54643367019cSmrg} ICON_HINT;
54653367019cSmrg#endif /* HAVE_LIBXPM */
54663367019cSmrg
54673367019cSmrgint
54683367019cSmrggetVisualDepth(XtermWidget xw)
54693367019cSmrg{
54703367019cSmrg    int result = 0;
54713367019cSmrg
5472fa3f02f3Smrg    if (getVisualInfo(xw)) {
5473fa3f02f3Smrg	result = xw->visInfo->depth;
54743367019cSmrg    }
54753367019cSmrg    return result;
54763367019cSmrg}
54773367019cSmrg
54783367019cSmrg/*
54793367019cSmrg * WM_ICON_SIZE should be honored if possible.
54803367019cSmrg */
54813367019cSmrgvoid
5482d4fba8b9SmrgxtermLoadIcon(XtermWidget xw, const char *icon_hint)
54833367019cSmrg{
54843367019cSmrg#ifdef HAVE_LIBXPM
54853367019cSmrg    Display *dpy = XtDisplay(xw);
54863367019cSmrg    Pixmap myIcon = 0;
54873367019cSmrg    Pixmap myMask = 0;
54883367019cSmrg    char *workname = 0;
5489d4fba8b9Smrg    ICON_HINT hint = eHintDefault;
5490fa3f02f3Smrg#include <builtin_icons.h>
54913367019cSmrg
5492d4fba8b9Smrg    ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint)));
5493d4fba8b9Smrg    if (!IsEmpty(icon_hint)) {
5494d4fba8b9Smrg	if (!x_strcasecmp(icon_hint, "none")) {
5495d4fba8b9Smrg	    hint = eHintNone;
5496d4fba8b9Smrg	} else {
5497d4fba8b9Smrg	    hint = eHintSearch;
5498d4fba8b9Smrg	}
5499d4fba8b9Smrg    }
55003367019cSmrg
55013367019cSmrg    if (hint == eHintSearch) {
55023367019cSmrg	int state = 0;
5503d4fba8b9Smrg	while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) {
55043367019cSmrg	    Pixmap resIcon = 0;
55053367019cSmrg	    Pixmap shapemask = 0;
55063367019cSmrg	    XpmAttributes attributes;
5507d4fba8b9Smrg	    struct stat sb;
55083367019cSmrg
55093367019cSmrg	    attributes.depth = (unsigned) getVisualDepth(xw);
55103367019cSmrg	    attributes.valuemask = XpmDepth;
55113367019cSmrg
5512d4fba8b9Smrg	    if (IsEmpty(workname)
5513d4fba8b9Smrg		|| lstat(workname, &sb) != 0
5514d4fba8b9Smrg		|| !S_ISREG(sb.st_mode)) {
5515d4fba8b9Smrg		TRACE(("...failure (no such file)\n"));
5516d4fba8b9Smrg	    } else {
5517d4fba8b9Smrg		int rc = XpmReadFileToPixmap(dpy,
5518d4fba8b9Smrg					     DefaultRootWindow(dpy),
5519d4fba8b9Smrg					     workname,
5520d4fba8b9Smrg					     &resIcon,
5521d4fba8b9Smrg					     &shapemask,
5522d4fba8b9Smrg					     &attributes);
5523d4fba8b9Smrg		if (rc == XpmSuccess) {
5524d4fba8b9Smrg		    myIcon = resIcon;
5525d4fba8b9Smrg		    myMask = shapemask;
5526d4fba8b9Smrg		    TRACE(("...success\n"));
5527d4fba8b9Smrg		    ReportIcons(("found/loaded icon-file %s\n", workname));
5528d4fba8b9Smrg		    break;
5529d4fba8b9Smrg		} else {
5530d4fba8b9Smrg		    TRACE(("...failure (%s)\n", XpmGetErrorString(rc)));
5531d4fba8b9Smrg		}
55323367019cSmrg	    }
55333367019cSmrg	}
55343367019cSmrg    }
55353367019cSmrg
55363367019cSmrg    /*
55373367019cSmrg     * If no external file was found, look for the name in the built-in table.
55383367019cSmrg     * If that fails, just use the biggest mini-icon.
55393367019cSmrg     */
55403367019cSmrg    if (myIcon == 0 && hint != eHintNone) {
55413367019cSmrg	char **data;
55423367019cSmrg#if OPT_BUILTIN_XPMS
55433367019cSmrg	const XPM_DATA *myData = 0;
5544d4fba8b9Smrg	myData = BuiltInXPM(mini_xterm_xpms);
55453367019cSmrg	if (myData == 0)
5546d4fba8b9Smrg	    myData = BuiltInXPM(filled_xterm_xpms);
55473367019cSmrg	if (myData == 0)
5548d4fba8b9Smrg	    myData = BuiltInXPM(xterm_color_xpms);
55493367019cSmrg	if (myData == 0)
5550d4fba8b9Smrg	    myData = BuiltInXPM(xterm_xpms);
55513367019cSmrg	if (myData == 0)
55523367019cSmrg	    myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1];
555394644356Smrg	data = (char **) myData->data;
55543367019cSmrg#else
55553367019cSmrg	data = (char **) &mini_xterm_48x48_xpm;
55563367019cSmrg#endif
55573367019cSmrg	if (XpmCreatePixmapFromData(dpy,
55583367019cSmrg				    DefaultRootWindow(dpy),
55593367019cSmrg				    data,
5560d4fba8b9Smrg				    &myIcon, &myMask, 0) == 0) {
5561d4fba8b9Smrg	    ReportIcons(("loaded built-in pixmap icon\n"));
5562d4fba8b9Smrg	} else {
55633367019cSmrg	    myIcon = 0;
55643367019cSmrg	    myMask = 0;
55653367019cSmrg	}
55663367019cSmrg    }
55673367019cSmrg
55683367019cSmrg    if (myIcon != 0) {
55693367019cSmrg	XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw));
55703367019cSmrg	if (!hints)
55713367019cSmrg	    hints = XAllocWMHints();
55723367019cSmrg
55733367019cSmrg	if (hints) {
55743367019cSmrg	    hints->flags |= IconPixmapHint;
55753367019cSmrg	    hints->icon_pixmap = myIcon;
55763367019cSmrg	    if (myMask) {
55773367019cSmrg		hints->flags |= IconMaskHint;
55783367019cSmrg		hints->icon_mask = myMask;
55793367019cSmrg	    }
55803367019cSmrg
55813367019cSmrg	    XSetWMHints(dpy, VShellWindow(xw), hints);
55823367019cSmrg	    XFree(hints);
5583d4fba8b9Smrg	    ReportIcons(("updated window-manager hints\n"));
55843367019cSmrg	}
55853367019cSmrg    }
55863367019cSmrg
5587d4fba8b9Smrg    free(workname);
55883367019cSmrg
55893367019cSmrg#else
55903367019cSmrg    (void) xw;
5591d4fba8b9Smrg    (void) icon_hint;
55923367019cSmrg#endif
55933367019cSmrg}
55943367019cSmrg
55953367019cSmrgvoid
5596cd3331d0SmrgChangeGroup(XtermWidget xw, const char *attribute, char *value)
5597d522f475Smrg{
5598d522f475Smrg    Arg args[1];
5599cd3331d0Smrg    Boolean changed = True;
5600d522f475Smrg    Widget w = CURRENT_EMU();
5601d522f475Smrg    Widget top = SHELL_OF(w);
5602d522f475Smrg
5603d4fba8b9Smrg    char *my_attr = NULL;
5604d4fba8b9Smrg    char *old_value = value;
5605d4fba8b9Smrg#if OPT_WIDE_CHARS
5606d4fba8b9Smrg    Boolean titleIsUTF8;
5607d4fba8b9Smrg#endif
5608d522f475Smrg
5609b7c89284Ssnj    if (!AllowTitleOps(xw))
5610d522f475Smrg	return;
5611d522f475Smrg
5612d4fba8b9Smrg    /*
5613d4fba8b9Smrg     * Ignore empty or too-long requests.
5614d4fba8b9Smrg     */
5615d4fba8b9Smrg    if (value == 0 || strlen(value) > 1000)
5616d4fba8b9Smrg	return;
5617d4fba8b9Smrg
5618cd3331d0Smrg    if (IsTitleMode(xw, tmSetBase16)) {
5619cd3331d0Smrg	const char *temp;
5620cd3331d0Smrg	char *test;
5621cd3331d0Smrg
5622d4fba8b9Smrg	/* this allocates a new string, if no error is detected */
5623cd3331d0Smrg	value = x_decode_hex(value, &temp);
5624d4fba8b9Smrg	if (value == 0 || *temp != '\0') {
56253367019cSmrg	    free(value);
5626cd3331d0Smrg	    return;
56273367019cSmrg	}
5628cd3331d0Smrg	for (test = value; *test != '\0'; ++test) {
5629cd3331d0Smrg	    if (CharOf(*test) < 32) {
5630cd3331d0Smrg		*test = '\0';
5631cd3331d0Smrg		break;
5632cd3331d0Smrg	    }
5633cd3331d0Smrg	}
5634cd3331d0Smrg    }
5635d4fba8b9Smrg#if OPT_WIDE_CHARS
5636d522f475Smrg    /*
5637d4fba8b9Smrg     * By design, xterm uses the XtNtitle resource of the X Toolkit for setting
5638d4fba8b9Smrg     * the WM_NAME property, rather than doing this directly.  That relies on
5639d4fba8b9Smrg     * the application to tell it if the format should be something other than
5640d4fba8b9Smrg     * STRING, i.e., by setting the XtNtitleEncoding resource.
5641d4fba8b9Smrg     *
5642d4fba8b9Smrg     * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted).  In X11R6,
5643d4fba8b9Smrg     * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86
5644d4fba8b9Smrg     * added UTF8_STRING (the documentation for that was discarded by an Xorg
5645d4fba8b9Smrg     * developer, although the source-code provides this feature).
5646d4fba8b9Smrg     *
5647d4fba8b9Smrg     * Since X11R5, if the X11 library fails to store a text property as
5648d4fba8b9Smrg     * STRING, it falls back to COMPOUND_TEXT.  For best interoperability, we
5649d4fba8b9Smrg     * prefer to use STRING if the data fits, or COMPOUND_TEXT.  In either
5650d4fba8b9Smrg     * case, limit the resulting characters to the printable ISO-8859-1 set.
5651d522f475Smrg     */
5652d4fba8b9Smrg    titleIsUTF8 = isValidUTF8((Char *) value);
5653d4fba8b9Smrg    if (IsSetUtf8Title(xw) && titleIsUTF8) {
5654d4fba8b9Smrg	char *testc = malloc(strlen(value) + 1);
5655d4fba8b9Smrg	Char *nextc = (Char *) value;
5656d4fba8b9Smrg	Boolean ok8bit = True;
5657d522f475Smrg
5658d4fba8b9Smrg	if (testc != NULL) {
5659d4fba8b9Smrg	    /*
5660d4fba8b9Smrg	     * Check if the data fits in STRING.  Along the way, replace
5661d4fba8b9Smrg	     * control characters.
5662d4fba8b9Smrg	     */
5663d4fba8b9Smrg	    Char *lastc = (Char *) testc;
5664d4fba8b9Smrg	    while (*nextc != '\0') {
5665d4fba8b9Smrg		unsigned ch;
5666d4fba8b9Smrg		nextc = convertFromUTF8(nextc, &ch);
5667d4fba8b9Smrg		if (ch > 255) {
5668d4fba8b9Smrg		    ok8bit = False;
5669d4fba8b9Smrg		} else if (!IsLatin1(ch)) {
5670d4fba8b9Smrg		    ch = OnlyLatin1(ch);
5671d4fba8b9Smrg		}
5672d4fba8b9Smrg		*lastc++ = (Char) ch;
5673d4fba8b9Smrg	    }
5674d4fba8b9Smrg	    *lastc = '\0';
5675d4fba8b9Smrg	    if (ok8bit) {
5676d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n"));
5677d4fba8b9Smrg		if (value != old_value)
5678d4fba8b9Smrg		    free(value);
5679d4fba8b9Smrg		value = testc;
5680d4fba8b9Smrg		titleIsUTF8 = False;
5681d4fba8b9Smrg	    } else {
5682d4fba8b9Smrg		TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n"
5683d4fba8b9Smrg		       "\t%s\n", value));
5684d4fba8b9Smrg		free(testc);
5685d4fba8b9Smrg		nextc = (Char *) value;
5686d4fba8b9Smrg		while (*nextc != '\0') {
5687d4fba8b9Smrg		    unsigned ch;
5688d4fba8b9Smrg		    Char *skip = convertFromUTF8(nextc, &ch);
5689d4fba8b9Smrg		    if (iswcntrl((wint_t) ch)) {
5690d4fba8b9Smrg			memset(nextc, BAD_ASCII, (size_t) (skip - nextc));
5691d4fba8b9Smrg		    }
5692d4fba8b9Smrg		    nextc = skip;
5693d4fba8b9Smrg		}
5694cd3331d0Smrg	    }
5695d522f475Smrg	}
5696d4fba8b9Smrg    } else
5697d4fba8b9Smrg#endif
5698d4fba8b9Smrg    {
5699d4fba8b9Smrg	Char *c1 = (Char *) value;
5700d4fba8b9Smrg
5701d4fba8b9Smrg	TRACE(("ChangeGroup: assume ISO-8859-1\n"));
5702d4fba8b9Smrg	for (c1 = (Char *) value; *c1 != '\0'; ++c1) {
5703d4fba8b9Smrg	    *c1 = (Char) OnlyLatin1(*c1);
5704d4fba8b9Smrg	}
5705d4fba8b9Smrg    }
5706d4fba8b9Smrg
5707d4fba8b9Smrg    my_attr = x_strdup(attribute);
5708d4fba8b9Smrg
5709d4fba8b9Smrg    ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value));
5710d522f475Smrg
5711d522f475Smrg#if OPT_WIDE_CHARS
5712d4fba8b9Smrg    /*
5713d4fba8b9Smrg     * If we're running in UTF-8 mode, and have not been told that the
5714d4fba8b9Smrg     * title string is in UTF-8, it is likely that non-ASCII text in the
5715d4fba8b9Smrg     * string will be rejected because it is not printable in the current
5716d4fba8b9Smrg     * locale.  So we convert it to UTF-8, allowing the X library to
5717d4fba8b9Smrg     * convert it back.
5718d4fba8b9Smrg     */
5719d4fba8b9Smrg    TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT "));
5720d4fba8b9Smrg    if (xtermEnvUTF8() && !titleIsUTF8) {
5721d4fba8b9Smrg	size_t limit = strlen(value);
5722d4fba8b9Smrg	Char *c1 = (Char *) value;
5723d4fba8b9Smrg	int n;
5724cd3331d0Smrg
5725d4fba8b9Smrg	for (n = 0; c1[n] != '\0'; ++n) {
5726d4fba8b9Smrg	    if (c1[n] > 127) {
5727d4fba8b9Smrg		Char *converted;
5728d4fba8b9Smrg		if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) {
5729d4fba8b9Smrg		    Char *temp = converted;
5730d4fba8b9Smrg		    while (*c1 != 0) {
5731d4fba8b9Smrg			temp = convertToUTF8(temp, *c1++);
5732d522f475Smrg		    }
5733d4fba8b9Smrg		    *temp = 0;
5734d4fba8b9Smrg		    if (value != old_value)
5735d4fba8b9Smrg			free(value);
5736d4fba8b9Smrg		    value = (char *) converted;
5737d4fba8b9Smrg		    ReportIcons(("...converted{%s}\n", value));
5738d522f475Smrg		}
5739d4fba8b9Smrg		break;
5740d522f475Smrg	    }
5741d522f475Smrg	}
5742d4fba8b9Smrg    }
5743d522f475Smrg#endif
5744d522f475Smrg
5745d522f475Smrg#if OPT_SAME_NAME
5746d4fba8b9Smrg    /* If the attribute isn't going to change, then don't bother... */
5747d4fba8b9Smrg    if (resource.sameName) {
5748d4fba8b9Smrg	char *buf = 0;
5749d4fba8b9Smrg	XtSetArg(args[0], my_attr, &buf);
5750d4fba8b9Smrg	XtGetValues(top, args, 1);
5751d4fba8b9Smrg	TRACE(("...comparing{%s}\n", NonNull(buf)));
5752d4fba8b9Smrg	if (buf != 0 && strcmp(value, buf) == 0)
5753d4fba8b9Smrg	    changed = False;
5754d4fba8b9Smrg    }
5755d522f475Smrg#endif /* OPT_SAME_NAME */
5756d522f475Smrg
5757d4fba8b9Smrg    if (changed) {
5758d4fba8b9Smrg	ReportIcons(("...updating %s\n", my_attr));
5759d4fba8b9Smrg	ReportIcons(("...value is %s\n", value));
5760d4fba8b9Smrg	XtSetArg(args[0], my_attr, value);
5761d4fba8b9Smrg	XtSetValues(top, args, 1);
5762d4fba8b9Smrg    }
5763d522f475Smrg#if OPT_WIDE_CHARS
5764d4fba8b9Smrg    if (xtermEnvUTF8()) {
5765d4fba8b9Smrg	Display *dpy = XtDisplay(xw);
5766d4fba8b9Smrg	const char *propname = (!strcmp(my_attr, XtNtitle)
5767d4fba8b9Smrg				? "_NET_WM_NAME"
5768d4fba8b9Smrg				: "_NET_WM_ICON_NAME");
5769d4fba8b9Smrg	Atom my_atom = XInternAtom(dpy, propname, False);
5770d4fba8b9Smrg
5771d4fba8b9Smrg	if (my_atom != None) {
5772d4fba8b9Smrg	    changed = True;
5773d4fba8b9Smrg
5774d4fba8b9Smrg	    if (IsSetUtf8Title(xw)) {
5775d4fba8b9Smrg#if OPT_SAME_NAME
5776d4fba8b9Smrg		if (resource.sameName) {
5777d4fba8b9Smrg		    Atom actual_type;
5778d4fba8b9Smrg		    Atom requested_type = XA_UTF8_STRING(dpy);
5779d4fba8b9Smrg		    int actual_format = 0;
5780d4fba8b9Smrg		    long long_length = 1024;
5781d4fba8b9Smrg		    unsigned long nitems = 0;
5782d4fba8b9Smrg		    unsigned long bytes_after = 0;
5783d4fba8b9Smrg		    unsigned char *prop = 0;
5784d4fba8b9Smrg
5785d4fba8b9Smrg		    if (xtermGetWinProp(dpy,
5786d4fba8b9Smrg					VShellWindow(xw),
5787d4fba8b9Smrg					my_atom,
5788d4fba8b9Smrg					0L,
5789d4fba8b9Smrg					long_length,
5790d4fba8b9Smrg					requested_type,
5791d4fba8b9Smrg					&actual_type,
5792d4fba8b9Smrg					&actual_format,
5793d4fba8b9Smrg					&nitems,
5794d4fba8b9Smrg					&bytes_after,
5795d4fba8b9Smrg					&prop)
5796d4fba8b9Smrg			&& actual_type == requested_type
5797d4fba8b9Smrg			&& actual_format == 8
5798d4fba8b9Smrg			&& prop != 0
5799d4fba8b9Smrg			&& nitems == strlen(value)
5800d4fba8b9Smrg			&& memcmp(value, prop, nitems) == 0) {
5801d4fba8b9Smrg			changed = False;
5802cd3331d0Smrg		    }
5803cd3331d0Smrg		}
5804d4fba8b9Smrg#endif /* OPT_SAME_NAME */
5805d4fba8b9Smrg		if (changed) {
5806d4fba8b9Smrg		    ReportIcons(("...updating %s\n", propname));
5807d4fba8b9Smrg		    ReportIcons(("...value is %s\n", value));
5808d4fba8b9Smrg		    XChangeProperty(dpy, VShellWindow(xw), my_atom,
5809d4fba8b9Smrg				    XA_UTF8_STRING(dpy), 8,
5810d4fba8b9Smrg				    PropModeReplace,
5811d4fba8b9Smrg				    (Char *) value,
5812d4fba8b9Smrg				    (int) strlen(value));
5813d4fba8b9Smrg		}
5814d4fba8b9Smrg	    } else {
5815d4fba8b9Smrg		ReportIcons(("...deleting %s\n", propname));
5816d4fba8b9Smrg		XDeleteProperty(dpy, VShellWindow(xw), my_atom);
5817d522f475Smrg	    }
5818d522f475Smrg	}
5819d522f475Smrg    }
5820d4fba8b9Smrg#endif
5821d4fba8b9Smrg    if (value != old_value) {
58223367019cSmrg	free(value);
58233367019cSmrg    }
58243367019cSmrg    free(my_attr);
58253367019cSmrg
5826cd3331d0Smrg    return;
5827d522f475Smrg}
5828d522f475Smrg
5829d522f475Smrgvoid
5830b7c89284SsnjChangeIconName(XtermWidget xw, char *name)
5831d522f475Smrg{
5832cd3331d0Smrg    if (name == 0) {
58333367019cSmrg	name = emptyString;
58343367019cSmrg    }
58353367019cSmrg    if (!showZIconBeep(xw, name))
5836b7c89284Ssnj	ChangeGroup(xw, XtNiconName, name);
5837d522f475Smrg}
5838d522f475Smrg
5839d522f475Smrgvoid
5840b7c89284SsnjChangeTitle(XtermWidget xw, char *name)
5841d522f475Smrg{
5842b7c89284Ssnj    ChangeGroup(xw, XtNtitle, name);
5843d522f475Smrg}
5844d522f475Smrg
5845712a7ff4Smrg#define Strlen(s) strlen((const char *)(s))
5846d522f475Smrg
5847d522f475Smrgvoid
5848d522f475SmrgChangeXprop(char *buf)
5849d522f475Smrg{
5850d522f475Smrg    Display *dpy = XtDisplay(toplevel);
5851d522f475Smrg    Window w = XtWindow(toplevel);
5852d522f475Smrg    XTextProperty text_prop;
5853d522f475Smrg    Atom aprop;
5854d522f475Smrg    Char *pchEndPropName = (Char *) strchr(buf, '=');
5855d522f475Smrg
5856d522f475Smrg    if (pchEndPropName)
5857d522f475Smrg	*pchEndPropName = '\0';
5858d522f475Smrg    aprop = XInternAtom(dpy, buf, False);
5859d522f475Smrg    if (pchEndPropName == NULL) {
5860d522f475Smrg	/* no "=value" given, so delete the property */
5861d522f475Smrg	XDeleteProperty(dpy, w, aprop);
5862d522f475Smrg    } else {
5863d522f475Smrg	text_prop.value = pchEndPropName + 1;
5864d522f475Smrg	text_prop.encoding = XA_STRING;
5865d522f475Smrg	text_prop.format = 8;
5866d522f475Smrg	text_prop.nitems = Strlen(text_prop.value);
5867d522f475Smrg	XSetTextProperty(dpy, w, &text_prop, aprop);
5868d522f475Smrg    }
5869d522f475Smrg}
5870d522f475Smrg
5871d522f475Smrg/***====================================================================***/
5872d522f475Smrg
5873d522f475Smrg/*
5874d522f475Smrg * This is part of ReverseVideo().  It reverses the data stored for the old
5875d522f475Smrg * "dynamic" colors that might have been retrieved using OSC 10-18.
5876d522f475Smrg */
5877d522f475Smrgvoid
58789a64e1c5SmrgReverseOldColors(XtermWidget xw)
5879d522f475Smrg{
58809a64e1c5Smrg    ScrnColors *pOld = xw->work.oldColors;
5881d522f475Smrg    Pixel tmpPix;
5882d522f475Smrg    char *tmpName;
5883d522f475Smrg
5884d522f475Smrg    if (pOld) {
5885d4fba8b9Smrg	/* change text cursor, if necessary */
5886d522f475Smrg	if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) {
5887d522f475Smrg	    pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG];
5888d522f475Smrg	    if (pOld->names[TEXT_CURSOR]) {
58899a64e1c5Smrg		XtFree(xw->work.oldColors->names[TEXT_CURSOR]);
5890d522f475Smrg		pOld->names[TEXT_CURSOR] = NULL;
5891d522f475Smrg	    }
5892d522f475Smrg	    if (pOld->names[TEXT_BG]) {
5893d522f475Smrg		if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) {
5894d522f475Smrg		    pOld->names[TEXT_CURSOR] = tmpName;
5895d522f475Smrg		}
5896d522f475Smrg	    }
5897d522f475Smrg	}
5898d522f475Smrg
5899d522f475Smrg	EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix);
5900d522f475Smrg	EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName);
5901d522f475Smrg
5902d522f475Smrg	EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix);
5903d522f475Smrg	EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName);
5904d522f475Smrg
5905d522f475Smrg#if OPT_TEK4014
5906d522f475Smrg	EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix);
5907d522f475Smrg	EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName);
5908d522f475Smrg#endif
5909d4fba8b9Smrg	FreeMarkGCs(xw);
5910d522f475Smrg    }
5911d522f475Smrg    return;
5912d522f475Smrg}
5913d522f475Smrg
5914d522f475SmrgBool
5915d522f475SmrgAllocateTermColor(XtermWidget xw,
5916d522f475Smrg		  ScrnColors * pNew,
5917d522f475Smrg		  int ndx,
5918cd3331d0Smrg		  const char *name,
5919cd3331d0Smrg		  Bool always)
5920d522f475Smrg{
5921cd3331d0Smrg    Bool result = False;
5922d522f475Smrg
5923cd3331d0Smrg    if (always || AllowColorOps(xw, ecSetColor)) {
5924cd3331d0Smrg	XColor def;
5925cd3331d0Smrg	char *newName;
5926cd3331d0Smrg
5927712a7ff4Smrg	result = True;
5928712a7ff4Smrg	if (!x_strcasecmp(name, XtDefaultForeground)) {
5929712a7ff4Smrg	    def.pixel = xw->old_foreground;
5930712a7ff4Smrg	} else if (!x_strcasecmp(name, XtDefaultBackground)) {
5931712a7ff4Smrg	    def.pixel = xw->old_background;
59323367019cSmrg	} else if (!xtermAllocColor(xw, &def, name)) {
5933712a7ff4Smrg	    result = False;
5934712a7ff4Smrg	}
5935712a7ff4Smrg
5936712a7ff4Smrg	if (result
5937cd3331d0Smrg	    && (newName = x_strdup(name)) != 0) {
5938712a7ff4Smrg	    if (COLOR_DEFINED(pNew, ndx)) {
5939cd3331d0Smrg		free(pNew->names[ndx]);
5940712a7ff4Smrg	    }
5941cd3331d0Smrg	    SET_COLOR_VALUE(pNew, ndx, def.pixel);
5942cd3331d0Smrg	    SET_COLOR_NAME(pNew, ndx, newName);
5943712a7ff4Smrg	    TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n",
5944712a7ff4Smrg		   ndx, newName, def.pixel));
5945cd3331d0Smrg	} else {
5946cd3331d0Smrg	    TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name));
5947712a7ff4Smrg	    result = False;
5948cd3331d0Smrg	}
5949cd3331d0Smrg    }
5950cd3331d0Smrg    return result;
5951d522f475Smrg}
5952d522f475Smrg/***====================================================================***/
5953d522f475Smrg
5954d522f475Smrg/* ARGSUSED */
5955d522f475Smrgvoid
5956cd3331d0SmrgPanic(const char *s GCC_UNUSED, int a GCC_UNUSED)
5957d522f475Smrg{
59583367019cSmrg    if_DEBUG({
59593367019cSmrg	xtermWarning(s, a);
59603367019cSmrg    });
5961d522f475Smrg}
5962d522f475Smrg
5963d522f475Smrgconst char *
5964d522f475SmrgSysErrorMsg(int code)
5965d522f475Smrg{
596694644356Smrg    static const char unknown[] = "unknown error";
5967d4fba8b9Smrg    const char *s = strerror(code);
5968d522f475Smrg    return s ? s : unknown;
5969d522f475Smrg}
5970d522f475Smrg
5971d522f475Smrgconst char *
5972d522f475SmrgSysReasonMsg(int code)
5973d522f475Smrg{
5974d522f475Smrg    /* *INDENT-OFF* */
5975d522f475Smrg    static const struct {
5976d522f475Smrg	int code;
5977d522f475Smrg	const char *name;
5978d522f475Smrg    } table[] = {
5979d522f475Smrg	{ ERROR_FIONBIO,	"main:  ioctl() failed on FIONBIO" },
5980d522f475Smrg	{ ERROR_F_GETFL,	"main: ioctl() failed on F_GETFL" },
5981d522f475Smrg	{ ERROR_F_SETFL,	"main: ioctl() failed on F_SETFL", },
5982d522f475Smrg	{ ERROR_OPDEVTTY,	"spawn: open() failed on /dev/tty", },
5983d522f475Smrg	{ ERROR_TIOCGETP,	"spawn: ioctl() failed on TIOCGETP", },
5984d522f475Smrg	{ ERROR_PTSNAME,	"spawn: ptsname() failed", },
5985d522f475Smrg	{ ERROR_OPPTSNAME,	"spawn: open() failed on ptsname", },
5986d522f475Smrg	{ ERROR_PTEM,		"spawn: ioctl() failed on I_PUSH/\"ptem\"" },
5987d522f475Smrg	{ ERROR_CONSEM,		"spawn: ioctl() failed on I_PUSH/\"consem\"" },
5988d522f475Smrg	{ ERROR_LDTERM,		"spawn: ioctl() failed on I_PUSH/\"ldterm\"" },
5989d522f475Smrg	{ ERROR_TTCOMPAT,	"spawn: ioctl() failed on I_PUSH/\"ttcompat\"" },
5990d522f475Smrg	{ ERROR_TIOCSETP,	"spawn: ioctl() failed on TIOCSETP" },
5991d522f475Smrg	{ ERROR_TIOCSETC,	"spawn: ioctl() failed on TIOCSETC" },
5992d522f475Smrg	{ ERROR_TIOCSETD,	"spawn: ioctl() failed on TIOCSETD" },
5993d522f475Smrg	{ ERROR_TIOCSLTC,	"spawn: ioctl() failed on TIOCSLTC" },
5994d522f475Smrg	{ ERROR_TIOCLSET,	"spawn: ioctl() failed on TIOCLSET" },
5995d522f475Smrg	{ ERROR_INIGROUPS,	"spawn: initgroups() failed" },
5996d522f475Smrg	{ ERROR_FORK,		"spawn: fork() failed" },
5997d522f475Smrg	{ ERROR_EXEC,		"spawn: exec() failed" },
5998d522f475Smrg	{ ERROR_PTYS,		"get_pty: not enough ptys" },
5999d522f475Smrg	{ ERROR_PTY_EXEC,	"waiting for initial map" },
6000d522f475Smrg	{ ERROR_SETUID,		"spawn: setuid() failed" },
6001d522f475Smrg	{ ERROR_INIT,		"spawn: can't initialize window" },
6002d522f475Smrg	{ ERROR_TIOCKSET,	"spawn: ioctl() failed on TIOCKSET" },
6003d522f475Smrg	{ ERROR_TIOCKSETC,	"spawn: ioctl() failed on TIOCKSETC" },
6004d522f475Smrg	{ ERROR_LUMALLOC,	"luit: command-line malloc failed" },
6005d522f475Smrg	{ ERROR_SELECT,		"in_put: select() failed" },
6006d522f475Smrg	{ ERROR_VINIT,		"VTInit: can't initialize window" },
6007d522f475Smrg	{ ERROR_KMMALLOC1,	"HandleKeymapChange: malloc failed" },
6008d522f475Smrg	{ ERROR_TSELECT,	"Tinput: select() failed" },
6009d522f475Smrg	{ ERROR_TINIT,		"TekInit: can't initialize window" },
6010d522f475Smrg	{ ERROR_BMALLOC2,	"SaltTextAway: malloc() failed" },
6011d522f475Smrg	{ ERROR_LOGEXEC,	"StartLog: exec() failed" },
6012d522f475Smrg	{ ERROR_XERROR,		"xerror: XError event" },
6013d522f475Smrg	{ ERROR_XIOERROR,	"xioerror: X I/O error" },
6014d522f475Smrg	{ ERROR_SCALLOC,	"Alloc: calloc() failed on base" },
6015d522f475Smrg	{ ERROR_SCALLOC2,	"Alloc: calloc() failed on rows" },
6016d522f475Smrg	{ ERROR_SAVE_PTR,	"ScrnPointers: malloc/realloc() failed" },
6017d522f475Smrg    };
6018d522f475Smrg    /* *INDENT-ON* */
6019d522f475Smrg
6020d522f475Smrg    Cardinal n;
6021d522f475Smrg    const char *result = "?";
6022d522f475Smrg
6023d522f475Smrg    for (n = 0; n < XtNumber(table); ++n) {
6024d522f475Smrg	if (code == table[n].code) {
6025d522f475Smrg	    result = table[n].name;
6026d522f475Smrg	    break;
6027d522f475Smrg	}
6028d522f475Smrg    }
6029d522f475Smrg    return result;
6030d522f475Smrg}
6031d522f475Smrg
6032d522f475Smrgvoid
6033d522f475SmrgSysError(int code)
6034d522f475Smrg{
6035d522f475Smrg    int oerrno = errno;
6036d522f475Smrg
6037c219fbebSmrg    fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno);
6038d522f475Smrg    fprintf(stderr, "%s\n", SysErrorMsg(oerrno));
6039d522f475Smrg    fprintf(stderr, "Reason: %s\n", SysReasonMsg(code));
6040d522f475Smrg
6041d522f475Smrg    Cleanup(code);
6042d522f475Smrg}
6043d522f475Smrg
6044d522f475Smrgvoid
60453367019cSmrgNormalExit(void)
6046d522f475Smrg{
6047d522f475Smrg    static Bool cleaning;
6048d522f475Smrg
6049d522f475Smrg    /*
6050d522f475Smrg     * Process "-hold" and session cleanup only for a normal exit.
6051d522f475Smrg     */
60523367019cSmrg    if (cleaning) {
60533367019cSmrg	hold_screen = 0;
60543367019cSmrg	return;
60553367019cSmrg    }
6056d522f475Smrg
60573367019cSmrg    cleaning = True;
60583367019cSmrg    need_cleanup = False;
6059d522f475Smrg
60603367019cSmrg    if (hold_screen) {
60613367019cSmrg	hold_screen = 2;
60623367019cSmrg	while (hold_screen) {
6063d4fba8b9Smrg	    xtermFlushDbe(term);
6064d4fba8b9Smrg	    xevents(term);
6065d4fba8b9Smrg	    Sleep(EVENT_DELAY);
6066d522f475Smrg	}
60673367019cSmrg    }
6068d522f475Smrg#if OPT_SESSION_MGT
60693367019cSmrg    if (resource.sessionMgt) {
60703367019cSmrg	XtVaSetValues(toplevel,
60713367019cSmrg		      XtNjoinSession, False,
60723367019cSmrg		      (void *) 0);
6073d522f475Smrg    }
60743367019cSmrg#endif
60753367019cSmrg    Cleanup(0);
60763367019cSmrg}
60773367019cSmrg
6078d4fba8b9Smrg#if USE_DOUBLE_BUFFER
6079d4fba8b9Smrgvoid
6080d4fba8b9SmrgxtermFlushDbe(XtermWidget xw)
6081d4fba8b9Smrg{
6082d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
6083d4fba8b9Smrg    if (resource.buffered && screen->needSwap) {
6084d4fba8b9Smrg	XdbeSwapInfo swap;
6085d4fba8b9Smrg	swap.swap_window = VWindow(screen);
6086d4fba8b9Smrg	swap.swap_action = XdbeCopied;
6087d4fba8b9Smrg	XdbeSwapBuffers(XtDisplay(xw), &swap, 1);
6088d4fba8b9Smrg	XFlush(XtDisplay(xw));
6089d4fba8b9Smrg	screen->needSwap = 0;
6090d4fba8b9Smrg	ScrollBarDrawThumb(xw, 2);
6091d4fba8b9Smrg	X_GETTIMEOFDAY(&screen->buffered_at);
6092d4fba8b9Smrg    }
6093d4fba8b9Smrg}
6094d4fba8b9Smrg
6095d4fba8b9Smrgvoid
6096d4fba8b9SmrgxtermTimedDbe(XtermWidget xw)
6097d4fba8b9Smrg{
6098d4fba8b9Smrg    if (resource.buffered) {
6099d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
6100d4fba8b9Smrg	struct timeval now;
6101d4fba8b9Smrg	long elapsed;
6102d4fba8b9Smrg	long limit = DbeMsecs(xw);
6103d4fba8b9Smrg
6104d4fba8b9Smrg	X_GETTIMEOFDAY(&now);
6105d4fba8b9Smrg	if (screen->buffered_at.tv_sec) {
6106d4fba8b9Smrg	    elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec)
6107d4fba8b9Smrg		       + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L);
6108d4fba8b9Smrg	} else {
6109d4fba8b9Smrg	    elapsed = limit;
6110d4fba8b9Smrg	}
6111d4fba8b9Smrg	if (elapsed >= limit) {
6112d4fba8b9Smrg	    xtermNeedSwap(xw, 1);
6113d4fba8b9Smrg	    xtermFlushDbe(xw);
6114d4fba8b9Smrg	}
6115d4fba8b9Smrg    }
6116d4fba8b9Smrg}
6117d4fba8b9Smrg#endif
6118d4fba8b9Smrg
61193367019cSmrg/*
61203367019cSmrg * cleanup by sending SIGHUP to client processes
61213367019cSmrg */
61223367019cSmrgvoid
61233367019cSmrgCleanup(int code)
61243367019cSmrg{
61253367019cSmrg    TScreen *screen = TScreenOf(term);
61263367019cSmrg
61273367019cSmrg    TRACE(("Cleanup %d\n", code));
6128d522f475Smrg
6129d522f475Smrg    if (screen->pid > 1) {
6130d522f475Smrg	(void) kill_process_group(screen->pid, SIGHUP);
6131d522f475Smrg    }
6132d522f475Smrg    Exit(code);
6133d522f475Smrg}
6134d522f475Smrg
6135fa3f02f3Smrg#ifndef S_IXOTH
6136fa3f02f3Smrg#define S_IXOTH 1
6137fa3f02f3Smrg#endif
6138fa3f02f3Smrg
6139fa3f02f3SmrgBoolean
6140fa3f02f3SmrgvalidProgram(const char *pathname)
6141fa3f02f3Smrg{
6142fa3f02f3Smrg    Boolean result = False;
6143fa3f02f3Smrg    struct stat sb;
6144fa3f02f3Smrg
6145fa3f02f3Smrg    if (!IsEmpty(pathname)
6146fa3f02f3Smrg	&& *pathname == '/'
6147fa3f02f3Smrg	&& strstr(pathname, "/..") == 0
6148fa3f02f3Smrg	&& stat(pathname, &sb) == 0
6149fa3f02f3Smrg	&& (sb.st_mode & S_IFMT) == S_IFREG
6150fa3f02f3Smrg	&& (sb.st_mode & S_IXOTH) != 0) {
6151fa3f02f3Smrg	result = True;
6152fa3f02f3Smrg    }
6153fa3f02f3Smrg    return result;
6154fa3f02f3Smrg}
6155fa3f02f3Smrg
6156d522f475Smrg#ifndef VMS
61573367019cSmrg#ifndef PATH_MAX
61583367019cSmrg#define PATH_MAX 512		/* ... is not defined consistently in Xos.h */
61593367019cSmrg#endif
6160d522f475Smrgchar *
6161d522f475SmrgxtermFindShell(char *leaf, Bool warning)
6162d522f475Smrg{
61633367019cSmrg    char *s0;
6164d522f475Smrg    char *s;
6165d522f475Smrg    char *d;
6166d522f475Smrg    char *tmp;
6167d522f475Smrg    char *result = leaf;
61683367019cSmrg    Bool allocated = False;
6169d522f475Smrg
6170d522f475Smrg    TRACE(("xtermFindShell(%s)\n", leaf));
61713367019cSmrg
61723367019cSmrg    if (!strncmp("./", result, (size_t) 2)
61733367019cSmrg	|| !strncmp("../", result, (size_t) 3)) {
61743367019cSmrg	size_t need = PATH_MAX;
61753367019cSmrg	size_t used = strlen(result) + 2;
61763367019cSmrg	char *buffer = malloc(used + need);
61773367019cSmrg	if (buffer != 0) {
61783367019cSmrg	    if (getcwd(buffer, need) != 0) {
61793367019cSmrg		sprintf(buffer + strlen(buffer), "/%s", result);
61803367019cSmrg		result = buffer;
61813367019cSmrg		allocated = True;
61823367019cSmrg	    } else {
61833367019cSmrg		free(buffer);
61843367019cSmrg	    }
61853367019cSmrg	}
61863367019cSmrg    } else if (*result != '\0' && strchr("+/-", *result) == 0) {
6187d522f475Smrg	/* find it in $PATH */
61883367019cSmrg	if ((s = s0 = x_getenv("PATH")) != 0) {
61890d92cbfdSchristos	    if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) {
6190d522f475Smrg		Bool found = False;
6191d522f475Smrg		while (*s != '\0') {
6192d522f475Smrg		    strcpy(tmp, s);
6193d522f475Smrg		    for (d = tmp;; ++d) {
6194d522f475Smrg			if (*d == ':' || *d == '\0') {
6195d522f475Smrg			    int skip = (*d != '\0');
6196d522f475Smrg			    *d = '/';
6197d522f475Smrg			    strcpy(d + 1, leaf);
6198d522f475Smrg			    if (skip)
6199d522f475Smrg				++d;
6200d522f475Smrg			    s += (d - tmp);
6201fa3f02f3Smrg			    if (validProgram(tmp)) {
6202d522f475Smrg				result = x_strdup(tmp);
6203d522f475Smrg				found = True;
62043367019cSmrg				allocated = True;
6205d522f475Smrg			    }
6206d522f475Smrg			    break;
6207d522f475Smrg			}
6208d522f475Smrg		    }
6209d522f475Smrg		    if (found)
6210d522f475Smrg			break;
6211d522f475Smrg		}
6212d522f475Smrg		free(tmp);
6213d522f475Smrg	    }
62143367019cSmrg	    free(s0);
6215d522f475Smrg	}
6216d522f475Smrg    }
6217d522f475Smrg    TRACE(("...xtermFindShell(%s)\n", result));
6218fa3f02f3Smrg    if (!validProgram(result)) {
6219d522f475Smrg	if (warning)
62203367019cSmrg	    xtermWarning("No absolute path found for shell: %s\n", result);
62213367019cSmrg	if (allocated)
62223367019cSmrg	    free(result);
6223d522f475Smrg	result = 0;
6224d522f475Smrg    }
62253367019cSmrg    /* be consistent, so that caller can always free the result */
62263367019cSmrg    if (result != 0 && !allocated)
62273367019cSmrg	result = x_strdup(result);
6228d522f475Smrg    return result;
6229d522f475Smrg}
6230d522f475Smrg#endif /* VMS */
6231d522f475Smrg
62320d92cbfdSchristos#define ENV_HUNK(n)	(unsigned) ((((n) + 1) | 31) + 1)
6233d522f475Smrg
62343367019cSmrg/*
62353367019cSmrg * If we do not have unsetenv(), make consistent updates for environ[].
62363367019cSmrg * This could happen on some older machines due to the uneven standardization
62373367019cSmrg * process for the two functions.
62383367019cSmrg *
62393367019cSmrg * That is, putenv() makes a copy of environ, and some implementations do not
62403367019cSmrg * update the environ pointer, so the fallback when unsetenv() is missing would
62413367019cSmrg * not work as intended.  Likewise, the reverse could be true, i.e., unsetenv
62423367019cSmrg * could copy environ.
62433367019cSmrg */
62443367019cSmrg#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV)
62453367019cSmrg#undef HAVE_PUTENV
62463367019cSmrg#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV)
62473367019cSmrg#undef HAVE_UNSETENV
62483367019cSmrg#endif
62493367019cSmrg
6250d522f475Smrg/*
6251d522f475Smrg * copy the environment before Setenv'ing.
6252d522f475Smrg */
6253d522f475Smrgvoid
6254d522f475SmrgxtermCopyEnv(char **oldenv)
6255d522f475Smrg{
62563367019cSmrg#ifdef HAVE_PUTENV
62573367019cSmrg    (void) oldenv;
62583367019cSmrg#else
6259d522f475Smrg    unsigned size;
6260d522f475Smrg    char **newenv;
6261d522f475Smrg
6262d522f475Smrg    for (size = 0; oldenv[size] != NULL; size++) {
6263d522f475Smrg	;
6264d522f475Smrg    }
6265d522f475Smrg
6266d522f475Smrg    newenv = TypeCallocN(char *, ENV_HUNK(size));
6267d522f475Smrg    memmove(newenv, oldenv, size * sizeof(char *));
6268d522f475Smrg    environ = newenv;
62693367019cSmrg#endif
62703367019cSmrg}
62713367019cSmrg
62723367019cSmrg#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV)
62733367019cSmrgstatic int
62743367019cSmrgfindEnv(const char *var, int *lengthp)
62753367019cSmrg{
62763367019cSmrg    char *test;
62773367019cSmrg    int envindex = 0;
62783367019cSmrg    size_t len = strlen(var);
62793367019cSmrg    int found = -1;
62803367019cSmrg
62813367019cSmrg    TRACE(("findEnv(%s=..)\n", var));
62823367019cSmrg
62833367019cSmrg    while ((test = environ[envindex]) != NULL) {
62843367019cSmrg	if (strncmp(test, var, len) == 0 && test[len] == '=') {
62853367019cSmrg	    found = envindex;
62863367019cSmrg	    break;
62873367019cSmrg	}
62883367019cSmrg	envindex++;
62893367019cSmrg    }
62903367019cSmrg    *lengthp = envindex;
62913367019cSmrg    return found;
6292d522f475Smrg}
62933367019cSmrg#endif
6294d522f475Smrg
6295d522f475Smrg/*
6296d522f475Smrg * sets the value of var to be arg in the Unix 4.2 BSD environment env.
6297d522f475Smrg * Var should end with '=' (bindings are of the form "var=value").
6298d522f475Smrg * This procedure assumes the memory for the first level of environ
6299d522f475Smrg * was allocated using calloc, with enough extra room at the end so not
6300d522f475Smrg * to have to do a realloc().
6301d522f475Smrg */
6302d522f475Smrgvoid
6303cd3331d0SmrgxtermSetenv(const char *var, const char *value)
6304d522f475Smrg{
6305d522f475Smrg    if (value != 0) {
63063367019cSmrg#ifdef HAVE_PUTENV
63073367019cSmrg	char *both = malloc(2 + strlen(var) + strlen(value));
63083367019cSmrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
63093367019cSmrg	if (both) {
63103367019cSmrg	    sprintf(both, "%s=%s", var, value);
63113367019cSmrg	    putenv(both);
63123367019cSmrg	}
63133367019cSmrg#else
6314d522f475Smrg	size_t len = strlen(var);
63153367019cSmrg	int envindex;
63163367019cSmrg	int found = findEnv(var, &envindex);
6317d522f475Smrg
6318d522f475Smrg	TRACE(("xtermSetenv(%s=%s)\n", var, value));
6319d522f475Smrg
6320d522f475Smrg	if (found < 0) {
6321d522f475Smrg	    unsigned need = ENV_HUNK(envindex + 1);
6322d522f475Smrg	    unsigned have = ENV_HUNK(envindex);
6323d522f475Smrg
6324d522f475Smrg	    if (need > have) {
6325d522f475Smrg		char **newenv;
6326d522f475Smrg		newenv = TypeMallocN(char *, need);
6327d522f475Smrg		if (newenv == 0) {
63283367019cSmrg		    xtermWarning("Cannot increase environment\n");
6329d522f475Smrg		    return;
6330d522f475Smrg		}
6331d522f475Smrg		memmove(newenv, environ, have * sizeof(*newenv));
6332d522f475Smrg		free(environ);
6333d522f475Smrg		environ = newenv;
6334d522f475Smrg	    }
6335d522f475Smrg
6336d522f475Smrg	    found = envindex;
6337d522f475Smrg	    environ[found + 1] = NULL;
6338d522f475Smrg	    environ = environ;
6339d522f475Smrg	}
6340d522f475Smrg
6341d4fba8b9Smrg	environ[found] = malloc(2 + len + strlen(value));
6342d522f475Smrg	if (environ[found] == 0) {
63433367019cSmrg	    xtermWarning("Cannot allocate environment %s\n", var);
6344d522f475Smrg	    return;
6345d522f475Smrg	}
6346d522f475Smrg	sprintf(environ[found], "%s=%s", var, value);
63473367019cSmrg#endif
6348d522f475Smrg    }
6349d522f475Smrg}
6350d522f475Smrg
63513367019cSmrgvoid
63523367019cSmrgxtermUnsetenv(const char *var)
63533367019cSmrg{
63543367019cSmrg    TRACE(("xtermUnsetenv(%s)\n", var));
63553367019cSmrg#ifdef HAVE_UNSETENV
63563367019cSmrg    unsetenv(var);
63573367019cSmrg#else
63583367019cSmrg    {
63593367019cSmrg	int ignore;
63603367019cSmrg	int item = findEnv(var, &ignore);
63613367019cSmrg	if (item >= 0) {
63623367019cSmrg	    while ((environ[item] = environ[item + 1]) != 0) {
63633367019cSmrg		++item;
63643367019cSmrg	    }
63653367019cSmrg	}
63663367019cSmrg    }
63673367019cSmrg#endif
63683367019cSmrg}
63693367019cSmrg
6370d522f475Smrg/*ARGSUSED*/
6371d522f475Smrgint
63729a64e1c5Smrgxerror(Display *d, XErrorEvent *ev)
6373d522f475Smrg{
63743367019cSmrg    xtermWarning("warning, error event received:\n");
6375d4fba8b9Smrg    TRACE_X_ERR(d, ev);
6376d522f475Smrg    (void) XmuPrintDefaultErrorMessage(d, ev, stderr);
6377d522f475Smrg    Exit(ERROR_XERROR);
6378d522f475Smrg    return 0;			/* appease the compiler */
6379d522f475Smrg}
6380d522f475Smrg
6381712a7ff4Smrgvoid
6382712a7ff4Smrgice_error(IceConn iceConn)
6383712a7ff4Smrg{
6384712a7ff4Smrg    (void) iceConn;
6385712a7ff4Smrg
63863367019cSmrg    xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n",
63873367019cSmrg		 (long) getpid(), errno);
6388712a7ff4Smrg
6389712a7ff4Smrg    Exit(ERROR_ICEERROR);
6390712a7ff4Smrg}
6391712a7ff4Smrg
6392d522f475Smrg/*ARGSUSED*/
6393d522f475Smrgint
6394fa3f02f3Smrgxioerror(Display *dpy)
6395d522f475Smrg{
6396d522f475Smrg    int the_error = errno;
6397d522f475Smrg
63983367019cSmrg    xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
63993367019cSmrg		 the_error, SysErrorMsg(the_error),
64003367019cSmrg		 DisplayString(dpy));
6401d522f475Smrg
6402d522f475Smrg    Exit(ERROR_XIOERROR);
6403d522f475Smrg    return 0;			/* appease the compiler */
6404d522f475Smrg}
6405d522f475Smrg
6406d522f475Smrgvoid
6407d522f475Smrgxt_error(String message)
6408d522f475Smrg{
64093367019cSmrg    xtermWarning("Xt error: %s\n", message);
6410d522f475Smrg
6411d522f475Smrg    /*
6412d522f475Smrg     * Check for the obvious - Xt does a poor job of reporting this.
6413d522f475Smrg     */
6414d522f475Smrg    if (x_getenv("DISPLAY") == 0) {
64153367019cSmrg	xtermWarning("DISPLAY is not set\n");
6416d522f475Smrg    }
6417d522f475Smrg    exit(1);
6418d522f475Smrg}
6419d522f475Smrg
6420d522f475Smrgint
6421d522f475SmrgXStrCmp(char *s1, char *s2)
6422d522f475Smrg{
6423d522f475Smrg    if (s1 && s2)
6424d522f475Smrg	return (strcmp(s1, s2));
6425d522f475Smrg    if (s1 && *s1)
6426d522f475Smrg	return (1);
6427d522f475Smrg    if (s2 && *s2)
6428d522f475Smrg	return (-1);
6429d522f475Smrg    return (0);
6430d522f475Smrg}
6431d522f475Smrg
6432d522f475Smrg#if OPT_TEK4014
6433d522f475Smrgstatic void
6434fa3f02f3Smrgwithdraw_window(Display *dpy, Window w, int scr)
6435d522f475Smrg{
6436d522f475Smrg    TRACE(("withdraw_window %#lx\n", (long) w));
6437d522f475Smrg    (void) XmuUpdateMapHints(dpy, w, NULL);
6438d522f475Smrg    XWithdrawWindow(dpy, w, scr);
6439d522f475Smrg    return;
6440d522f475Smrg}
6441d522f475Smrg#endif
6442d522f475Smrg
6443d522f475Smrgvoid
6444d522f475Smrgset_vt_visibility(Bool on)
6445d522f475Smrg{
6446c219fbebSmrg    XtermWidget xw = term;
6447c219fbebSmrg    TScreen *screen = TScreenOf(xw);
6448d522f475Smrg
6449d522f475Smrg    TRACE(("set_vt_visibility(%d)\n", on));
6450d522f475Smrg    if (on) {
6451c219fbebSmrg	if (!screen->Vshow && xw) {
6452c219fbebSmrg	    VTInit(xw);
6453c219fbebSmrg	    XtMapWidget(XtParent(xw));
6454d522f475Smrg#if OPT_TOOLBAR
6455d522f475Smrg	    /* we need both of these during initialization */
6456c219fbebSmrg	    XtMapWidget(SHELL_OF(xw));
6457d522f475Smrg	    ShowToolbar(resource.toolBar);
6458d522f475Smrg#endif
6459d522f475Smrg	    screen->Vshow = True;
6460d522f475Smrg	}
6461d522f475Smrg    }
6462d522f475Smrg#if OPT_TEK4014
6463d522f475Smrg    else {
6464c219fbebSmrg	if (screen->Vshow && xw) {
6465c219fbebSmrg	    withdraw_window(XtDisplay(xw),
6466c219fbebSmrg			    VShellWindow(xw),
6467c219fbebSmrg			    XScreenNumberOfScreen(XtScreen(xw)));
6468d522f475Smrg	    screen->Vshow = False;
6469d522f475Smrg	}
6470d522f475Smrg    }
6471d522f475Smrg    set_vthide_sensitivity();
6472d522f475Smrg    set_tekhide_sensitivity();
6473d522f475Smrg    update_vttekmode();
6474d522f475Smrg    update_tekshow();
6475d522f475Smrg    update_vtshow();
6476d522f475Smrg#endif
6477d522f475Smrg    return;
6478d522f475Smrg}
6479d522f475Smrg
6480d522f475Smrg#if OPT_TEK4014
6481d522f475Smrgvoid
6482d522f475Smrgset_tek_visibility(Bool on)
6483d522f475Smrg{
6484d4fba8b9Smrg    XtermWidget xw = term;
6485d4fba8b9Smrg
6486d522f475Smrg    TRACE(("set_tek_visibility(%d)\n", on));
6487d522f475Smrg
6488d522f475Smrg    if (on) {
6489d4fba8b9Smrg	if (!TEK4014_SHOWN(xw)) {
6490cd3331d0Smrg	    if (tekWidget == 0) {
6491cd3331d0Smrg		TekInit();	/* will exit on failure */
6492cd3331d0Smrg	    }
6493cd3331d0Smrg	    if (tekWidget != 0) {
6494cd3331d0Smrg		Widget tekParent = SHELL_OF(tekWidget);
6495cd3331d0Smrg		XtRealizeWidget(tekParent);
6496cd3331d0Smrg		XtMapWidget(XtParent(tekWidget));
6497d522f475Smrg#if OPT_TOOLBAR
6498cd3331d0Smrg		/* we need both of these during initialization */
6499cd3331d0Smrg		XtMapWidget(tekParent);
6500cd3331d0Smrg		XtMapWidget(tekWidget);
6501d522f475Smrg#endif
6502cd3331d0Smrg		XtOverrideTranslations(tekParent,
6503cd3331d0Smrg				       XtParseTranslationTable
6504cd3331d0Smrg				       ("<Message>WM_PROTOCOLS: DeleteWindow()"));
6505cd3331d0Smrg		(void) XSetWMProtocols(XtDisplay(tekParent),
6506cd3331d0Smrg				       XtWindow(tekParent),
6507cd3331d0Smrg				       &wm_delete_window, 1);
6508d4fba8b9Smrg		TEK4014_SHOWN(xw) = True;
6509cd3331d0Smrg	    }
6510d522f475Smrg	}
6511d522f475Smrg    } else {
6512d4fba8b9Smrg	if (TEK4014_SHOWN(xw) && tekWidget) {
6513d522f475Smrg	    withdraw_window(XtDisplay(tekWidget),
6514d522f475Smrg			    TShellWindow,
6515d522f475Smrg			    XScreenNumberOfScreen(XtScreen(tekWidget)));
6516d4fba8b9Smrg	    TEK4014_SHOWN(xw) = False;
6517d522f475Smrg	}
6518d522f475Smrg    }
6519d522f475Smrg    set_tekhide_sensitivity();
6520d522f475Smrg    set_vthide_sensitivity();
6521d522f475Smrg    update_vtshow();
6522d522f475Smrg    update_tekshow();
6523d522f475Smrg    update_vttekmode();
6524d522f475Smrg    return;
6525d522f475Smrg}
6526d522f475Smrg
6527d522f475Smrgvoid
6528d522f475Smrgend_tek_mode(void)
6529d522f475Smrg{
6530cd3331d0Smrg    XtermWidget xw = term;
6531cd3331d0Smrg
6532cd3331d0Smrg    if (TEK4014_ACTIVE(xw)) {
6533cd3331d0Smrg	FlushLog(xw);
6534dfb07bc7Smrg	TEK4014_ACTIVE(xw) = False;
6535dfb07bc7Smrg	xtermSetWinSize(xw);
6536d522f475Smrg	longjmp(Tekend, 1);
6537d522f475Smrg    }
6538d522f475Smrg    return;
6539d522f475Smrg}
6540d522f475Smrg
6541d522f475Smrgvoid
6542d522f475Smrgend_vt_mode(void)
6543d522f475Smrg{
6544cd3331d0Smrg    XtermWidget xw = term;
6545cd3331d0Smrg
6546cd3331d0Smrg    if (!TEK4014_ACTIVE(xw)) {
6547cd3331d0Smrg	FlushLog(xw);
6548d4fba8b9Smrg	set_tek_visibility(True);
6549cd3331d0Smrg	TEK4014_ACTIVE(xw) = True;
6550dfb07bc7Smrg	TekSetWinSize(tekWidget);
6551d522f475Smrg	longjmp(VTend, 1);
6552d522f475Smrg    }
6553d522f475Smrg    return;
6554d522f475Smrg}
6555d522f475Smrg
6556d522f475Smrgvoid
6557d522f475Smrgswitch_modes(Bool tovt)		/* if true, then become vt mode */
6558d522f475Smrg{
6559d522f475Smrg    if (tovt) {
6560d522f475Smrg	if (tekRefreshList)
6561d522f475Smrg	    TekRefresh(tekWidget);
6562d522f475Smrg	end_tek_mode();		/* WARNING: this does a longjmp... */
6563d522f475Smrg    } else {
6564d522f475Smrg	end_vt_mode();		/* WARNING: this does a longjmp... */
6565d522f475Smrg    }
6566d522f475Smrg}
6567d522f475Smrg
6568d522f475Smrgvoid
6569d522f475Smrghide_vt_window(void)
6570d522f475Smrg{
6571d522f475Smrg    set_vt_visibility(False);
6572d522f475Smrg    if (!TEK4014_ACTIVE(term))
6573d522f475Smrg	switch_modes(False);	/* switch to tek mode */
6574d522f475Smrg}
6575d522f475Smrg
6576d522f475Smrgvoid
6577d522f475Smrghide_tek_window(void)
6578d522f475Smrg{
6579d522f475Smrg    set_tek_visibility(False);
6580d522f475Smrg    tekRefreshList = (TekLink *) 0;
6581d522f475Smrg    if (TEK4014_ACTIVE(term))
6582d522f475Smrg	switch_modes(True);	/* does longjmp to vt mode */
6583d522f475Smrg}
6584d522f475Smrg#endif /* OPT_TEK4014 */
6585d522f475Smrg
6586d522f475Smrgstatic const char *
6587d522f475Smrgskip_punct(const char *s)
6588d522f475Smrg{
6589d522f475Smrg    while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') {
6590d522f475Smrg	++s;
6591d522f475Smrg    }
6592d522f475Smrg    return s;
6593d522f475Smrg}
6594d522f475Smrg
6595d522f475Smrgstatic int
6596d522f475Smrgcmp_options(const void *a, const void *b)
6597d522f475Smrg{
6598d522f475Smrg    const char *s1 = skip_punct(((const OptionHelp *) a)->opt);
6599d522f475Smrg    const char *s2 = skip_punct(((const OptionHelp *) b)->opt);
6600d522f475Smrg    return strcmp(s1, s2);
6601d522f475Smrg}
6602d522f475Smrg
6603d522f475Smrgstatic int
6604d522f475Smrgcmp_resources(const void *a, const void *b)
6605d522f475Smrg{
6606d522f475Smrg    return strcmp(((const XrmOptionDescRec *) a)->option,
6607d522f475Smrg		  ((const XrmOptionDescRec *) b)->option);
6608d522f475Smrg}
6609d522f475Smrg
6610d522f475SmrgXrmOptionDescRec *
6611d522f475SmrgsortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count)
6612d522f475Smrg{
6613d522f475Smrg    static XrmOptionDescRec *res_array = 0;
6614d522f475Smrg
6615d522f475Smrg#ifdef NO_LEAKS
6616cd3331d0Smrg    if (descs == 0) {
6617d4fba8b9Smrg	FreeAndNull(res_array);
6618d522f475Smrg    } else
6619d522f475Smrg#endif
6620d522f475Smrg    if (res_array == 0) {
6621d522f475Smrg	Cardinal j;
6622d522f475Smrg
6623d522f475Smrg	/* make a sorted index to 'resources' */
6624d522f475Smrg	res_array = TypeCallocN(XrmOptionDescRec, res_count);
6625cd3331d0Smrg	if (res_array != 0) {
6626cd3331d0Smrg	    for (j = 0; j < res_count; j++)
6627cd3331d0Smrg		res_array[j] = descs[j];
6628cd3331d0Smrg	    qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources);
6629cd3331d0Smrg	}
6630d522f475Smrg    }
6631d522f475Smrg    return res_array;
6632d522f475Smrg}
6633d522f475Smrg
6634d522f475Smrg/*
6635d522f475Smrg * The first time this is called, construct sorted index to the main program's
6636d522f475Smrg * list of options, taking into account the on/off options which will be
6637d522f475Smrg * compressed into one token.  It's a lot simpler to do it this way than
6638d522f475Smrg * maintain the list in sorted form with lots of ifdef's.
6639d522f475Smrg */
6640d522f475SmrgOptionHelp *
6641d522f475SmrgsortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs)
6642d522f475Smrg{
6643d522f475Smrg    static OptionHelp *opt_array = 0;
6644d522f475Smrg
6645d522f475Smrg#ifdef NO_LEAKS
6646d522f475Smrg    if (descs == 0 && opt_array != 0) {
6647d522f475Smrg	sortedOptDescs(descs, numDescs);
6648d4fba8b9Smrg	FreeAndNull(opt_array);
6649d522f475Smrg	return 0;
6650d522f475Smrg    } else if (options == 0 || descs == 0) {
6651d522f475Smrg	return 0;
6652d522f475Smrg    }
6653d522f475Smrg#endif
6654d522f475Smrg
6655d522f475Smrg    if (opt_array == 0) {
6656cd3331d0Smrg	size_t opt_count, j;
6657d522f475Smrg#if OPT_TRACE
6658d522f475Smrg	Cardinal k;
6659d522f475Smrg	XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs);
6660d522f475Smrg	int code;
6661cd3331d0Smrg	const char *mesg;
6662d522f475Smrg#else
6663d522f475Smrg	(void) descs;
6664d522f475Smrg	(void) numDescs;
6665d522f475Smrg#endif
6666d522f475Smrg
6667d522f475Smrg	/* count 'options' and make a sorted index to it */
6668d522f475Smrg	for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) {
6669d522f475Smrg	    ;
6670d522f475Smrg	}
6671d522f475Smrg	opt_array = TypeCallocN(OptionHelp, opt_count + 1);
6672d522f475Smrg	for (j = 0; j < opt_count; j++)
6673d522f475Smrg	    opt_array[j] = options[j];
6674d522f475Smrg	qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options);
6675d522f475Smrg
6676d522f475Smrg	/* supply the "turn on/off" strings if needed */
6677d522f475Smrg#if OPT_TRACE
6678d522f475Smrg	for (j = 0; j < opt_count; j++) {
6679712a7ff4Smrg	    if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) {
6680c219fbebSmrg		char temp[80];
6681cd3331d0Smrg		const char *name = opt_array[j].opt + 3;
6682d522f475Smrg		for (k = 0; k < numDescs; ++k) {
6683cd3331d0Smrg		    const char *value = res_array[k].value;
6684d522f475Smrg		    if (res_array[k].option[0] == '-') {
6685d522f475Smrg			code = -1;
6686d522f475Smrg		    } else if (res_array[k].option[0] == '+') {
6687d522f475Smrg			code = 1;
6688d522f475Smrg		    } else {
6689d522f475Smrg			code = 0;
6690d522f475Smrg		    }
66913367019cSmrg		    sprintf(temp, "%.*s",
66923367019cSmrg			    (int) sizeof(temp) - 2,
66933367019cSmrg			    opt_array[j].desc);
6694c219fbebSmrg		    if (x_strindex(temp, "inhibit") != 0)
6695d522f475Smrg			code = -code;
6696d522f475Smrg		    if (code != 0
6697d522f475Smrg			&& res_array[k].value != 0
6698d522f475Smrg			&& !strcmp(name, res_array[k].option + 1)) {
6699d522f475Smrg			if (((code < 0) && !strcmp(value, "on"))
6700d522f475Smrg			    || ((code > 0) && !strcmp(value, "off"))
6701d522f475Smrg			    || ((code > 0) && !strcmp(value, "0"))) {
6702d522f475Smrg			    mesg = "turn on/off";
6703d522f475Smrg			} else {
6704d522f475Smrg			    mesg = "turn off/on";
6705d522f475Smrg			}
6706d522f475Smrg			if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) {
6707712a7ff4Smrg			    if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) {
6708d4fba8b9Smrg				char *s = malloc(strlen(mesg)
6709d4fba8b9Smrg						 + strlen(opt_array[j].desc)
6710d4fba8b9Smrg						 + 2);
6711d522f475Smrg				if (s != 0) {
6712d522f475Smrg				    sprintf(s, "%s %s", mesg, opt_array[j].desc);
6713d522f475Smrg				    opt_array[j].desc = s;
6714d522f475Smrg				}
6715d522f475Smrg			    } else {
6716d522f475Smrg				TRACE(("OOPS "));
6717d522f475Smrg			    }
6718d522f475Smrg			}
6719d522f475Smrg			TRACE(("%s: %s %s: %s (%s)\n",
6720d522f475Smrg			       mesg,
6721d522f475Smrg			       res_array[k].option,
6722d522f475Smrg			       res_array[k].value,
6723d522f475Smrg			       opt_array[j].opt,
6724d522f475Smrg			       opt_array[j].desc));
6725d522f475Smrg			break;
6726d522f475Smrg		    }
6727d522f475Smrg		}
6728d522f475Smrg	    }
6729d522f475Smrg	}
6730d522f475Smrg#endif
6731d522f475Smrg    }
6732d522f475Smrg    return opt_array;
6733d522f475Smrg}
6734d522f475Smrg
6735d522f475Smrg/*
6736d522f475Smrg * Report the character-type locale that xterm was started in.
6737d522f475Smrg */
67383367019cSmrgString
6739d522f475SmrgxtermEnvLocale(void)
6740d522f475Smrg{
67413367019cSmrg    static String result;
6742d522f475Smrg
6743d522f475Smrg    if (result == 0) {
6744d522f475Smrg	if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) {
6745cd3331d0Smrg	    result = x_strdup("C");
6746cd3331d0Smrg	} else {
6747cd3331d0Smrg	    result = x_strdup(result);
6748d522f475Smrg	}
6749d522f475Smrg	TRACE(("xtermEnvLocale ->%s\n", result));
6750d522f475Smrg    }
6751d522f475Smrg    return result;
6752d522f475Smrg}
6753d522f475Smrg
6754d522f475Smrgchar *
6755d522f475SmrgxtermEnvEncoding(void)
6756d522f475Smrg{
6757d522f475Smrg    static char *result;
6758d522f475Smrg
6759d522f475Smrg    if (result == 0) {
6760d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6761d522f475Smrg	result = nl_langinfo(CODESET);
6762d522f475Smrg#else
6763d4fba8b9Smrg	const char *locale = xtermEnvLocale();
6764d522f475Smrg	if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) {
6765d4fba8b9Smrg	    result = x_strdup("ASCII");
6766d522f475Smrg	} else {
6767d4fba8b9Smrg	    result = x_strdup("ISO-8859-1");
6768d522f475Smrg	}
6769d522f475Smrg#endif
6770d522f475Smrg	TRACE(("xtermEnvEncoding ->%s\n", result));
6771d522f475Smrg    }
6772d522f475Smrg    return result;
6773d522f475Smrg}
6774d522f475Smrg
6775d522f475Smrg#if OPT_WIDE_CHARS
6776d522f475Smrg/*
6777d522f475Smrg * Tell whether xterm was started in a locale that uses UTF-8 encoding for
6778d522f475Smrg * characters.  That environment is inherited by subprocesses and used in
6779d522f475Smrg * various library calls.
6780d522f475Smrg */
6781d522f475SmrgBool
6782d522f475SmrgxtermEnvUTF8(void)
6783d522f475Smrg{
6784d522f475Smrg    static Bool init = False;
6785d522f475Smrg    static Bool result = False;
6786d522f475Smrg
6787d522f475Smrg    if (!init) {
6788d522f475Smrg	init = True;
6789d522f475Smrg#ifdef HAVE_LANGINFO_CODESET
6790d522f475Smrg	result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0);
6791d522f475Smrg#else
6792fa3f02f3Smrg	{
6793fa3f02f3Smrg	    char *locale = x_strdup(xtermEnvLocale());
6794fa3f02f3Smrg	    int n;
6795fa3f02f3Smrg	    for (n = 0; locale[n] != 0; ++n) {
6796fa3f02f3Smrg		locale[n] = x_toupper(locale[n]);
6797fa3f02f3Smrg	    }
6798fa3f02f3Smrg	    if (strstr(locale, "UTF-8") != 0)
6799fa3f02f3Smrg		result = True;
6800fa3f02f3Smrg	    else if (strstr(locale, "UTF8") != 0)
6801fa3f02f3Smrg		result = True;
6802fa3f02f3Smrg	    free(locale);
6803fa3f02f3Smrg	}
6804d522f475Smrg#endif
6805d522f475Smrg	TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result)));
6806d522f475Smrg    }
6807d522f475Smrg    return result;
6808d522f475Smrg}
6809d522f475Smrg#endif /* OPT_WIDE_CHARS */
6810d522f475Smrg
6811b7c89284Ssnj/*
6812b7c89284Ssnj * Check if the current widget, or any parent, is the VT100 "xterm" widget.
6813b7c89284Ssnj */
6814b7c89284SsnjXtermWidget
6815b7c89284SsnjgetXtermWidget(Widget w)
6816b7c89284Ssnj{
6817b7c89284Ssnj    XtermWidget xw;
6818b7c89284Ssnj
6819b7c89284Ssnj    if (w == 0) {
6820b7c89284Ssnj	xw = (XtermWidget) CURRENT_EMU();
6821b7c89284Ssnj	if (!IsXtermWidget(xw)) {
6822b7c89284Ssnj	    xw = 0;
6823b7c89284Ssnj	}
6824b7c89284Ssnj    } else if (IsXtermWidget(w)) {
6825b7c89284Ssnj	xw = (XtermWidget) w;
6826b7c89284Ssnj    } else {
6827b7c89284Ssnj	xw = getXtermWidget(XtParent(w));
6828b7c89284Ssnj    }
6829b7c89284Ssnj    TRACE2(("getXtermWidget %p -> %p\n", w, xw));
6830b7c89284Ssnj    return xw;
6831b7c89284Ssnj}
68323367019cSmrg
68333367019cSmrg#if OPT_SESSION_MGT
68343367019cSmrgstatic void
68353367019cSmrgdie_callback(Widget w GCC_UNUSED,
68363367019cSmrg	     XtPointer client_data GCC_UNUSED,
68373367019cSmrg	     XtPointer call_data GCC_UNUSED)
68383367019cSmrg{
68393367019cSmrg    NormalExit();
68403367019cSmrg}
68413367019cSmrg
68423367019cSmrgstatic void
68433367019cSmrgsave_callback(Widget w GCC_UNUSED,
68443367019cSmrg	      XtPointer client_data GCC_UNUSED,
68453367019cSmrg	      XtPointer call_data)
68463367019cSmrg{
68473367019cSmrg    XtCheckpointToken token = (XtCheckpointToken) call_data;
68483367019cSmrg    /* we have nothing to save */
68493367019cSmrg    token->save_success = True;
68503367019cSmrg}
68513367019cSmrg
68523367019cSmrgstatic void
68533367019cSmrgicewatch(IceConn iceConn,
68543367019cSmrg	 IcePointer clientData GCC_UNUSED,
68553367019cSmrg	 Bool opening,
68563367019cSmrg	 IcePointer * watchData GCC_UNUSED)
68573367019cSmrg{
68583367019cSmrg    if (opening) {
68593367019cSmrg	ice_fd = IceConnectionNumber(iceConn);
68603367019cSmrg	TRACE(("got IceConnectionNumber %d\n", ice_fd));
68613367019cSmrg    } else {
68623367019cSmrg	ice_fd = -1;
68633367019cSmrg	TRACE(("reset IceConnectionNumber\n"));
68643367019cSmrg    }
68653367019cSmrg}
68663367019cSmrg
68673367019cSmrgvoid
68683367019cSmrgxtermOpenSession(void)
68693367019cSmrg{
68703367019cSmrg    if (resource.sessionMgt) {
68713367019cSmrg	TRACE(("Enabling session-management callbacks\n"));
68723367019cSmrg	XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL);
68733367019cSmrg	XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL);
68743367019cSmrg    }
68753367019cSmrg}
68763367019cSmrg
68773367019cSmrgvoid
68783367019cSmrgxtermCloseSession(void)
68793367019cSmrg{
68803367019cSmrg    IceRemoveConnectionWatch(icewatch, NULL);
68813367019cSmrg}
68823367019cSmrg#endif /* OPT_SESSION_MGT */
68833367019cSmrg
68843367019cSmrgWidget
68853367019cSmrgxtermOpenApplication(XtAppContext * app_context_return,
68863367019cSmrg		     String my_class,
68873367019cSmrg		     XrmOptionDescRec * options,
68883367019cSmrg		     Cardinal num_options,
68893367019cSmrg		     int *argc_in_out,
6890d4fba8b9Smrg		     char **argv_in_out,
6891fa3f02f3Smrg		     String *fallback_resources,
68923367019cSmrg		     WidgetClass widget_class,
68933367019cSmrg		     ArgList args,
68943367019cSmrg		     Cardinal num_args)
68953367019cSmrg{
68963367019cSmrg    Widget result;
68973367019cSmrg
68983367019cSmrg    XtSetErrorHandler(xt_error);
68993367019cSmrg#if OPT_SESSION_MGT
69003367019cSmrg    result = XtOpenApplication(app_context_return,
69013367019cSmrg			       my_class,
69023367019cSmrg			       options,
69033367019cSmrg			       num_options,
69043367019cSmrg			       argc_in_out,
69053367019cSmrg			       argv_in_out,
69063367019cSmrg			       fallback_resources,
69073367019cSmrg			       widget_class,
69083367019cSmrg			       args,
69093367019cSmrg			       num_args);
69103367019cSmrg    IceAddConnectionWatch(icewatch, NULL);
69113367019cSmrg#else
69129a64e1c5Smrg    (void) widget_class;
69139a64e1c5Smrg    (void) args;
69149a64e1c5Smrg    (void) num_args;
69153367019cSmrg    result = XtAppInitialize(app_context_return,
69163367019cSmrg			     my_class,
69173367019cSmrg			     options,
69183367019cSmrg			     num_options,
69193367019cSmrg			     argc_in_out,
69203367019cSmrg			     argv_in_out,
69213367019cSmrg			     fallback_resources,
69223367019cSmrg			     NULL, 0);
69233367019cSmrg#endif /* OPT_SESSION_MGT */
6924037a25ddSmrg    init_colored_cursor(XtDisplay(result));
6925037a25ddSmrg
6926e8264990Smrg    XtSetErrorHandler(NULL);
69273367019cSmrg
69283367019cSmrg    return result;
69293367019cSmrg}
69303367019cSmrg
6931d4fba8b9Smrg/*
6932d4fba8b9Smrg * Some calls to XGetAtom() will fail, and we don't want to stop.  So we use
6933d4fba8b9Smrg * our own error-handler.
6934d4fba8b9Smrg */
6935d4fba8b9Smrg/* ARGSUSED */
6936d4fba8b9Smrgint
6937d4fba8b9Smrgignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED)
6938d4fba8b9Smrg{
6939d4fba8b9Smrg    return 1;
6940d4fba8b9Smrg}
6941d4fba8b9Smrg
69423367019cSmrgstatic int x11_errors;
69433367019cSmrg
69443367019cSmrgstatic int
69459a64e1c5Smrgcatch_x11_error(Display *display, XErrorEvent *error_event)
69463367019cSmrg{
69473367019cSmrg    (void) display;
69483367019cSmrg    (void) error_event;
69493367019cSmrg    ++x11_errors;
69503367019cSmrg    return 0;
69513367019cSmrg}
69523367019cSmrg
69533367019cSmrgBoolean
6954fa3f02f3SmrgxtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs)
69553367019cSmrg{
69563367019cSmrg    Boolean result = False;
69573367019cSmrg    Status code;
69583367019cSmrg
69593367019cSmrg    memset(attrs, 0, sizeof(*attrs));
69603367019cSmrg    if (win != None) {
69613367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
69623367019cSmrg	x11_errors = 0;
69633367019cSmrg	code = XGetWindowAttributes(dpy, win, attrs);
69643367019cSmrg	XSetErrorHandler(save);
69653367019cSmrg	result = (Boolean) ((code != 0) && !x11_errors);
69663367019cSmrg	if (result) {
69673367019cSmrg	    TRACE_WIN_ATTRS(attrs);
69683367019cSmrg	} else {
69693367019cSmrg	    xtermWarning("invalid window-id %ld\n", (long) win);
69703367019cSmrg	}
69713367019cSmrg    }
69723367019cSmrg    return result;
69733367019cSmrg}
69743367019cSmrg
69753367019cSmrgBoolean
6976fa3f02f3SmrgxtermGetWinProp(Display *display,
69773367019cSmrg		Window win,
69783367019cSmrg		Atom property,
69793367019cSmrg		long long_offset,
69803367019cSmrg		long long_length,
69813367019cSmrg		Atom req_type,
69829a64e1c5Smrg		Atom *actual_type_return,
69833367019cSmrg		int *actual_format_return,
69843367019cSmrg		unsigned long *nitems_return,
69853367019cSmrg		unsigned long *bytes_after_return,
69863367019cSmrg		unsigned char **prop_return)
69873367019cSmrg{
6988d4fba8b9Smrg    Boolean result = False;
69893367019cSmrg
69903367019cSmrg    if (win != None) {
69913367019cSmrg	XErrorHandler save = XSetErrorHandler(catch_x11_error);
69923367019cSmrg	x11_errors = 0;
69933367019cSmrg	if (XGetWindowProperty(display,
69943367019cSmrg			       win,
69953367019cSmrg			       property,
69963367019cSmrg			       long_offset,
69973367019cSmrg			       long_length,
69983367019cSmrg			       False,
69993367019cSmrg			       req_type,
70003367019cSmrg			       actual_type_return,
70013367019cSmrg			       actual_format_return,
70023367019cSmrg			       nitems_return,
70033367019cSmrg			       bytes_after_return,
70043367019cSmrg			       prop_return) == Success
70053367019cSmrg	    && x11_errors == 0) {
70063367019cSmrg	    result = True;
70073367019cSmrg	}
70083367019cSmrg	XSetErrorHandler(save);
70093367019cSmrg    }
70103367019cSmrg    return result;
70113367019cSmrg}
70123367019cSmrg
70133367019cSmrgvoid
70143367019cSmrgxtermEmbedWindow(Window winToEmbedInto)
70153367019cSmrg{
70163367019cSmrg    Display *dpy = XtDisplay(toplevel);
70173367019cSmrg    XWindowAttributes attrs;
70183367019cSmrg
70193367019cSmrg    TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto));
70203367019cSmrg    if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) {
70213367019cSmrg	XtermWidget xw = term;
70223367019cSmrg	TScreen *screen = TScreenOf(xw);
70233367019cSmrg
70243367019cSmrg	XtRealizeWidget(toplevel);
70253367019cSmrg
70263367019cSmrg	TRACE(("...reparenting toplevel %#lx into %#lx\n",
70273367019cSmrg	       XtWindow(toplevel),
70283367019cSmrg	       winToEmbedInto));
70293367019cSmrg	XReparentWindow(dpy,
70303367019cSmrg			XtWindow(toplevel),
70313367019cSmrg			winToEmbedInto, 0, 0);
70323367019cSmrg
70333367019cSmrg	screen->embed_high = (Dimension) attrs.height;
70343367019cSmrg	screen->embed_wide = (Dimension) attrs.width;
70353367019cSmrg    }
70363367019cSmrg}
703794644356Smrg
703894644356Smrgvoid
703994644356Smrgfree_string(String value)
704094644356Smrg{
704194644356Smrg    free((void *) value);
704294644356Smrg}
7043dfb07bc7Smrg
7044dfb07bc7Smrg/* Set tty's idea of window size, using the given file descriptor 'fd'. */
7045d4fba8b9Smrgint
7046dfb07bc7Smrgupdate_winsize(int fd, int rows, int cols, int height, int width)
7047dfb07bc7Smrg{
7048d4fba8b9Smrg    int code = -1;
7049dfb07bc7Smrg#ifdef TTYSIZE_STRUCT
7050d4fba8b9Smrg    static int last_rows = -1;
7051d4fba8b9Smrg    static int last_cols = -1;
7052dfb07bc7Smrg
7053d4fba8b9Smrg    if (rows != last_rows || cols != last_cols) {
7054d4fba8b9Smrg	TTYSIZE_STRUCT ts;
7055d4fba8b9Smrg
7056d4fba8b9Smrg	last_rows = rows;
7057d4fba8b9Smrg	last_cols = cols;
7058d4fba8b9Smrg	setup_winsize(ts, rows, cols, height, width);
7059d4fba8b9Smrg	TRACE_RC(code, SET_TTYSIZE(fd, ts));
7060d4fba8b9Smrg	trace_winsize(ts, "from SET_TTYSIZE");
7061d4fba8b9Smrg    }
7062dfb07bc7Smrg#endif
7063dfb07bc7Smrg
7064dfb07bc7Smrg    (void) rows;
7065dfb07bc7Smrg    (void) cols;
7066dfb07bc7Smrg    (void) height;
7067dfb07bc7Smrg    (void) width;
7068d4fba8b9Smrg
7069d4fba8b9Smrg    return code;
7070dfb07bc7Smrg}
7071dfb07bc7Smrg
7072dfb07bc7Smrg/*
7073dfb07bc7Smrg * Update stty settings to match the values returned by dtterm window
7074dfb07bc7Smrg * manipulation 18 and 19.
7075dfb07bc7Smrg */
7076dfb07bc7Smrgvoid
7077dfb07bc7SmrgxtermSetWinSize(XtermWidget xw)
7078dfb07bc7Smrg{
7079dfb07bc7Smrg#if OPT_TEK4014
7080dfb07bc7Smrg    if (!TEK4014_ACTIVE(xw))
7081dfb07bc7Smrg#endif
7082dfb07bc7Smrg	if (XtIsRealized((Widget) xw)) {
7083dfb07bc7Smrg	    TScreen *screen = TScreenOf(xw);
7084dfb07bc7Smrg
7085dfb07bc7Smrg	    TRACE(("xtermSetWinSize\n"));
7086dfb07bc7Smrg	    update_winsize(screen->respond,
7087dfb07bc7Smrg			   MaxRows(screen),
7088dfb07bc7Smrg			   MaxCols(screen),
7089dfb07bc7Smrg			   Height(screen),
7090dfb07bc7Smrg			   Width(screen));
7091dfb07bc7Smrg	}
7092dfb07bc7Smrg}
7093d4fba8b9Smrg
7094d4fba8b9Smrg#if OPT_XTERM_SGR
7095d4fba8b9Smrg
7096d4fba8b9Smrg#if OPT_TRACE
7097d4fba8b9Smrgstatic char *
7098d4fba8b9SmrgtraceIFlags(IFlags flags)
7099d4fba8b9Smrg{
7100d4fba8b9Smrg    static char result[1000];
7101d4fba8b9Smrg    result[0] = '\0';
7102d4fba8b9Smrg#define DATA(name) if (flags & name) { strcat(result, " " #name); }
7103d4fba8b9Smrg    DATA(INVERSE);
7104d4fba8b9Smrg    DATA(UNDERLINE);
7105d4fba8b9Smrg    DATA(BOLD);
7106d4fba8b9Smrg    DATA(BLINK);
7107d4fba8b9Smrg    DATA(INVISIBLE);
7108d4fba8b9Smrg    DATA(BG_COLOR);
7109d4fba8b9Smrg    DATA(FG_COLOR);
7110d4fba8b9Smrg
7111d4fba8b9Smrg#if OPT_WIDE_ATTRS
7112d4fba8b9Smrg    DATA(ATR_FAINT);
7113d4fba8b9Smrg    DATA(ATR_ITALIC);
7114d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7115d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7116d4fba8b9Smrg    DATA(ATR_DIRECT_FG);
7117d4fba8b9Smrg    DATA(ATR_DIRECT_BG);
7118d4fba8b9Smrg#endif
7119d4fba8b9Smrg#undef DATA
7120d4fba8b9Smrg    return result;
7121d4fba8b9Smrg}
7122d4fba8b9Smrg
7123d4fba8b9Smrgstatic char *
7124d4fba8b9SmrgtraceIStack(unsigned flags)
7125d4fba8b9Smrg{
7126d4fba8b9Smrg    static char result[1000];
7127d4fba8b9Smrg    result[0] = '\0';
7128d4fba8b9Smrg#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); }
7129d4fba8b9Smrg    DATA(INVERSE);
7130d4fba8b9Smrg    DATA(UNDERLINE);
7131d4fba8b9Smrg    DATA(BOLD);
7132d4fba8b9Smrg    DATA(BLINK);
7133d4fba8b9Smrg    DATA(INVISIBLE);
7134d4fba8b9Smrg#if OPT_ISO_COLORS
7135d4fba8b9Smrg    DATA(BG_COLOR);
7136d4fba8b9Smrg    DATA(FG_COLOR);
7137d4fba8b9Smrg#endif
7138d4fba8b9Smrg
7139d4fba8b9Smrg#if OPT_WIDE_ATTRS
7140d4fba8b9Smrg    DATA(ATR_FAINT);
7141d4fba8b9Smrg    DATA(ATR_ITALIC);
7142d4fba8b9Smrg    DATA(ATR_STRIKEOUT);
7143d4fba8b9Smrg    DATA(ATR_DBL_UNDER);
7144d4fba8b9Smrg    /* direct-colors are a special case of ISO-colors (see above) */
7145d4fba8b9Smrg#endif
7146d4fba8b9Smrg#undef DATA
7147d4fba8b9Smrg    return result;
7148d4fba8b9Smrg}
7149d4fba8b9Smrg#endif
7150d4fba8b9Smrg
7151d4fba8b9Smrgvoid
7152d4fba8b9SmrgxtermPushSGR(XtermWidget xw, int value)
7153d4fba8b9Smrg{
7154d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7155d4fba8b9Smrg
7156d4fba8b9Smrg    TRACE(("xtermPushSGR %d mask %#x %s\n",
7157d4fba8b9Smrg	   s->used + 1, (unsigned) value, traceIStack((unsigned) value)));
7158d4fba8b9Smrg
7159d4fba8b9Smrg    if (s->used < MAX_SAVED_SGR) {
7160d4fba8b9Smrg	s->stack[s->used].mask = (IFlags) value;
7161d4fba8b9Smrg#define PUSH_FLAG(name) \
7162d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7163d4fba8b9Smrg	    TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name)))
7164d4fba8b9Smrg#define PUSH_DATA(name) \
7165d4fba8b9Smrg	    s->stack[s->used].name = xw->name;\
7166d4fba8b9Smrg	    TRACE(("...may pop %s %d\n", #name, xw->name))
7167d4fba8b9Smrg	PUSH_FLAG(flags);
7168d4fba8b9Smrg#if OPT_ISO_COLORS
7169d4fba8b9Smrg	PUSH_DATA(sgr_foreground);
7170d4fba8b9Smrg	PUSH_DATA(sgr_background);
7171d4fba8b9Smrg	PUSH_DATA(sgr_38_xcolors);
7172d4fba8b9Smrg#endif
7173d4fba8b9Smrg    }
7174d4fba8b9Smrg    s->used++;
7175d4fba8b9Smrg}
7176d4fba8b9Smrg
7177d4fba8b9Smrg#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits)
7178d4fba8b9Smrg
7179d4fba8b9Smrgvoid
7180d4fba8b9SmrgxtermReportSGR(XtermWidget xw, XTermRect *value)
7181d4fba8b9Smrg{
7182d4fba8b9Smrg    TScreen *screen = TScreenOf(xw);
7183d4fba8b9Smrg    char reply[BUFSIZ];
7184d4fba8b9Smrg    CellData working;
7185d4fba8b9Smrg    int row, col;
7186d4fba8b9Smrg    Boolean first = True;
7187d4fba8b9Smrg
7188d4fba8b9Smrg    TRACE(("xtermReportSGR %d,%d - %d,%d\n",
7189d4fba8b9Smrg	   value->top, value->left,
7190d4fba8b9Smrg	   value->bottom, value->right));
7191d4fba8b9Smrg
7192d4fba8b9Smrg    memset(&working, 0, sizeof(working));
7193d4fba8b9Smrg    for (row = value->top - 1; row < value->bottom; ++row) {
7194d4fba8b9Smrg	LineData *ld = getLineData(screen, row);
7195d4fba8b9Smrg	if (ld == 0)
7196d4fba8b9Smrg	    continue;
7197d4fba8b9Smrg	for (col = value->left - 1; col < value->right; ++col) {
7198d4fba8b9Smrg	    if (first) {
7199d4fba8b9Smrg		first = False;
7200d4fba8b9Smrg		saveCellData(screen, &working, 0, ld, NULL, col);
7201d4fba8b9Smrg	    }
7202d4fba8b9Smrg	    working.attribs &= ld->attribs[col];
7203d4fba8b9Smrg#if OPT_ISO_COLORS
7204d4fba8b9Smrg	    if (working.attribs & FG_COLOR
7205d4fba8b9Smrg		&& GetCellColorFG(working.color)
7206d4fba8b9Smrg		!= GetCellColorFG(ld->color[col])) {
7207d4fba8b9Smrg		IAttrClr(working.attribs, FG_COLOR);
7208d4fba8b9Smrg	    }
7209d4fba8b9Smrg	    if (working.attribs & BG_COLOR
7210d4fba8b9Smrg		&& GetCellColorBG(working.color)
7211d4fba8b9Smrg		!= GetCellColorBG(ld->color[col])) {
7212d4fba8b9Smrg		IAttrClr(working.attribs, BG_COLOR);
7213d4fba8b9Smrg	    }
7214d4fba8b9Smrg#endif
7215d4fba8b9Smrg	}
7216d4fba8b9Smrg    }
7217d4fba8b9Smrg    xtermFormatSGR(xw, reply,
7218d4fba8b9Smrg		   working.attribs,
7219d4fba8b9Smrg		   GetCellColorFG(working.color),
7220d4fba8b9Smrg		   GetCellColorBG(working.color));
7221d4fba8b9Smrg    unparseputc1(xw, ANSI_CSI);
7222d4fba8b9Smrg    unparseputs(xw, reply);
7223d4fba8b9Smrg    unparseputc(xw, 'm');
7224d4fba8b9Smrg    unparse_end(xw);
7225d4fba8b9Smrg}
7226d4fba8b9Smrg
7227d4fba8b9Smrgvoid
7228d4fba8b9SmrgxtermPopSGR(XtermWidget xw)
7229d4fba8b9Smrg{
7230d4fba8b9Smrg    SavedSGR *s = &(xw->saved_sgr);
7231d4fba8b9Smrg
7232d4fba8b9Smrg    TRACE(("xtermPopSGR %d\n", s->used));
7233d4fba8b9Smrg
7234d4fba8b9Smrg    if (s->used > 0) {
7235d4fba8b9Smrg	if (s->used-- <= MAX_SAVED_SGR) {
7236d4fba8b9Smrg	    IFlags mask = s->stack[s->used].mask;
7237d4fba8b9Smrg	    Boolean changed = False;
7238d4fba8b9Smrg
7239d4fba8b9Smrg	    TRACE(("...mask  %#x %s\n", mask, traceIStack(mask)));
7240d4fba8b9Smrg	    TRACE(("...old:  %s\n", traceIFlags(xw->flags)));
7241d4fba8b9Smrg	    TRACE(("...new:  %s\n", traceIFlags(s->stack[s->used].flags)));
7242d4fba8b9Smrg#define POP_FLAG(name) \
7243d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7244d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7245d4fba8b9Smrg		    changed = True; \
7246d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7247d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7248d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7249d4fba8b9Smrg		} \
7250d4fba8b9Smrg	    }
7251d4fba8b9Smrg#define POP_FLAG2(name,part) \
7252d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7253d4fba8b9Smrg	    	if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \
7254d4fba8b9Smrg		    changed = True; \
7255d4fba8b9Smrg		    UIntClr(xw->flags, part); \
7256d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & part)); \
7257d4fba8b9Smrg		    TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \
7258d4fba8b9Smrg		} \
7259d4fba8b9Smrg	    }
7260d4fba8b9Smrg#define POP_DATA(name,value) \
7261d4fba8b9Smrg	    if (xBIT(ps##name - 1) & mask) { \
7262d4fba8b9Smrg	        Bool always = False; \
7263d4fba8b9Smrg	    	if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \
7264d4fba8b9Smrg		    always = changed = True; \
7265d4fba8b9Smrg		    UIntClr(xw->flags, name); \
7266d4fba8b9Smrg		    UIntSet(xw->flags, (s->stack[s->used].flags & name)); \
7267d4fba8b9Smrg		    TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \
7268d4fba8b9Smrg		} \
7269d4fba8b9Smrg		if (always || (xw->value != s->stack[s->used].value)) { \
7270d4fba8b9Smrg		    TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \
7271d4fba8b9Smrg		    xw->value = s->stack[s->used].value; \
7272d4fba8b9Smrg		    changed = True; \
7273d4fba8b9Smrg		} \
7274d4fba8b9Smrg	    }
7275d4fba8b9Smrg	    POP_FLAG(BOLD);
7276d4fba8b9Smrg	    POP_FLAG(UNDERLINE);
7277d4fba8b9Smrg	    POP_FLAG(BLINK);
7278d4fba8b9Smrg	    POP_FLAG(INVERSE);
7279d4fba8b9Smrg	    POP_FLAG(INVISIBLE);
7280d4fba8b9Smrg#if OPT_WIDE_ATTRS
7281d4fba8b9Smrg	    if (xBIT(psATR_ITALIC - 1) & mask) {
7282d4fba8b9Smrg		xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags);
7283d4fba8b9Smrg	    }
7284d4fba8b9Smrg	    POP_FLAG(ATR_ITALIC);
7285d4fba8b9Smrg	    POP_FLAG(ATR_FAINT);
7286d4fba8b9Smrg	    POP_FLAG(ATR_STRIKEOUT);
7287d4fba8b9Smrg	    POP_FLAG(ATR_DBL_UNDER);
7288d4fba8b9Smrg#endif
7289d4fba8b9Smrg#if OPT_ISO_COLORS
7290d4fba8b9Smrg	    POP_DATA(FG_COLOR, sgr_foreground);
7291d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_background);
7292d4fba8b9Smrg	    POP_DATA(BG_COLOR, sgr_38_xcolors);
7293d4fba8b9Smrg#if OPT_DIRECT_COLOR
7294d4fba8b9Smrg	    POP_FLAG2(FG_COLOR, ATR_DIRECT_FG);
7295d4fba8b9Smrg	    POP_FLAG2(BG_COLOR, ATR_DIRECT_BG);
7296d4fba8b9Smrg#endif
7297d4fba8b9Smrg	    if (changed) {
7298d4fba8b9Smrg		setExtendedColors(xw);
7299d4fba8b9Smrg	    }
7300d4fba8b9Smrg#else
7301d4fba8b9Smrg	    (void) changed;
7302d4fba8b9Smrg#endif
7303d4fba8b9Smrg	}
7304d4fba8b9Smrg#if OPT_ISO_COLORS
7305d4fba8b9Smrg	TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n",
7306d4fba8b9Smrg	       traceIFlags(xw->flags),
7307d4fba8b9Smrg	       xw->sgr_foreground,
7308d4fba8b9Smrg	       xw->sgr_background,
7309d4fba8b9Smrg	       xw->sgr_38_xcolors ? " (SGR 38)" : ""));
7310d4fba8b9Smrg#else
7311d4fba8b9Smrg	TRACE(("xtermP -> flags%s\n",
7312d4fba8b9Smrg	       traceIFlags(xw->flags)));
7313d4fba8b9Smrg#endif
7314d4fba8b9Smrg    }
7315d4fba8b9Smrg}
7316d4fba8b9Smrg
7317d4fba8b9Smrg#if OPT_ISO_COLORS
7318d4fba8b9Smrgstatic ColorSlot *
7319d4fba8b9SmrgallocColorSlot(XtermWidget xw, int slot)
7320d4fba8b9Smrg{
7321d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7322d4fba8b9Smrg    ColorSlot *result = NULL;
7323d4fba8b9Smrg
7324d4fba8b9Smrg    if (slot >= 0 && slot < MAX_SAVED_SGR) {
7325d4fba8b9Smrg	if (s->palettes[slot] == NULL) {
7326d4fba8b9Smrg	    s->palettes[slot] = (ColorSlot *) calloc((size_t) 1,
7327d4fba8b9Smrg						     sizeof(ColorSlot)
7328d4fba8b9Smrg						     + (sizeof(ColorRes)
7329d4fba8b9Smrg							* MAXCOLORS));
7330d4fba8b9Smrg	}
7331d4fba8b9Smrg	result = s->palettes[slot];
7332d4fba8b9Smrg    }
7333d4fba8b9Smrg    return result;
7334d4fba8b9Smrg}
7335d4fba8b9Smrg
7336d4fba8b9Smrgstatic void
7337d4fba8b9SmrgpopOldColors(XtermWidget xw, ScrnColors * source)
7338d4fba8b9Smrg{
7339d4fba8b9Smrg    Boolean changed = False;
7340d4fba8b9Smrg    ScrnColors *target = xw->work.oldColors;
7341d4fba8b9Smrg
7342d4fba8b9Smrg    if (source->which != target->which) {
7343d4fba8b9Smrg	changed = True;
7344d4fba8b9Smrg    } else {
7345d4fba8b9Smrg	int n;
7346d4fba8b9Smrg	for (n = 0; n < NCOLORS; ++n) {
7347d4fba8b9Smrg	    if (COLOR_DEFINED(source, n)) {
7348d4fba8b9Smrg		if (COLOR_DEFINED(target, n)) {
7349d4fba8b9Smrg		    if (source->colors[n] != target->colors[n]) {
7350d4fba8b9Smrg			changed = True;
7351d4fba8b9Smrg			break;
7352d4fba8b9Smrg		    }
7353d4fba8b9Smrg		} else {
7354d4fba8b9Smrg		    changed = True;
7355d4fba8b9Smrg		    break;
7356d4fba8b9Smrg		}
7357d4fba8b9Smrg	    } else if (COLOR_DEFINED(target, n)) {
7358d4fba8b9Smrg		changed = True;
7359d4fba8b9Smrg		break;
7360d4fba8b9Smrg	    }
7361d4fba8b9Smrg	}
7362d4fba8b9Smrg    }
7363d4fba8b9Smrg    if (changed) {
7364d4fba8b9Smrg	ChangeColors(xw, source);
7365d4fba8b9Smrg	UpdateOldColors(xw, source);
7366d4fba8b9Smrg    }
7367d4fba8b9Smrg}
7368d4fba8b9Smrg#endif /* OPT_ISO_COLORS */
7369d4fba8b9Smrg
7370d4fba8b9Smrg#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False)
7371d4fba8b9Smrg#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes))
7372d4fba8b9Smrg
7373d4fba8b9Smrg/*
7374d4fba8b9Smrg * By default, a "push" increments the stack after copying to the current
7375d4fba8b9Smrg * slot.  But a specific target allows one to copy into a specific slot.
7376d4fba8b9Smrg */
7377d4fba8b9Smrgvoid
7378d4fba8b9SmrgxtermPushColors(XtermWidget xw, int value)
7379d4fba8b9Smrg{
7380d4fba8b9Smrg#if OPT_ISO_COLORS
7381d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7382d4fba8b9Smrg    int pushed = s->used;
7383d4fba8b9Smrg    int actual = (value <= 0) ? pushed : (value - 1);
7384d4fba8b9Smrg
7385d4fba8b9Smrg    TRACE(("xtermPushColors %d:%d\n", actual, pushed));
7386d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7387d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7388d4fba8b9Smrg	ColorSlot *palette;
7389d4fba8b9Smrg
7390d4fba8b9Smrg	if ((palette = allocColorSlot(xw, actual)) != NULL) {
7391d4fba8b9Smrg	    GetColors(xw, &(palette->base));
7392d4fba8b9Smrg	    CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS);
7393d4fba8b9Smrg	    if (value < 0) {
7394d4fba8b9Smrg		s->used++;
7395d4fba8b9Smrg		if (s->last < s->used)
7396d4fba8b9Smrg		    s->last = s->used;
7397d4fba8b9Smrg	    } else {
7398d4fba8b9Smrg		s->used = value;
7399d4fba8b9Smrg	    }
7400d4fba8b9Smrg	}
7401d4fba8b9Smrg    }
7402d4fba8b9Smrg#else
7403d4fba8b9Smrg    (void) xw;
7404d4fba8b9Smrg    (void) value;
7405d4fba8b9Smrg#endif
7406d4fba8b9Smrg}
7407d4fba8b9Smrg
7408d4fba8b9Smrgvoid
7409d4fba8b9SmrgxtermPopColors(XtermWidget xw, int value)
7410d4fba8b9Smrg{
7411d4fba8b9Smrg#if OPT_ISO_COLORS
7412d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7413d4fba8b9Smrg    int popped = (s->used - 1);
7414d4fba8b9Smrg    int actual = (value <= 0) ? popped : (value - 1);
7415d4fba8b9Smrg
7416d4fba8b9Smrg    TRACE(("xtermPopColors %d:%d\n", actual, popped));
7417d4fba8b9Smrg    if (actual < MAX_SAVED_SGR && actual >= 0) {
7418d4fba8b9Smrg	TScreen *screen = TScreenOf(xw);
7419d4fba8b9Smrg	ColorSlot *palette;
7420d4fba8b9Smrg
7421d4fba8b9Smrg	if ((palette = s->palettes[actual]) != NULL) {
7422d4fba8b9Smrg	    Boolean changed = DiffColorSlot(screen->Acolors,
7423d4fba8b9Smrg					    palette->ansi,
7424d4fba8b9Smrg					    MAXCOLORS);
7425d4fba8b9Smrg
7426d4fba8b9Smrg	    GetOldColors(xw);
7427d4fba8b9Smrg	    popOldColors(xw, &(palette->base));
7428d4fba8b9Smrg	    CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS);
7429d4fba8b9Smrg	    s->used = actual;
7430d4fba8b9Smrg	    if (changed)
7431d4fba8b9Smrg		xtermRepaint(xw);
7432d4fba8b9Smrg	}
7433d4fba8b9Smrg    }
7434d4fba8b9Smrg#else
7435d4fba8b9Smrg    (void) xw;
7436d4fba8b9Smrg    (void) value;
7437d4fba8b9Smrg#endif
7438d4fba8b9Smrg}
7439d4fba8b9Smrg
7440d4fba8b9Smrgvoid
7441d4fba8b9SmrgxtermReportColors(XtermWidget xw)
7442d4fba8b9Smrg{
7443d4fba8b9Smrg    ANSI reply;
7444d4fba8b9Smrg    SavedColors *s = &(xw->saved_colors);
7445d4fba8b9Smrg
7446d4fba8b9Smrg    memset(&reply, 0, sizeof(reply));
7447d4fba8b9Smrg    reply.a_type = ANSI_CSI;
7448d4fba8b9Smrg    reply.a_pintro = '?';
7449d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->used;
7450d4fba8b9Smrg    reply.a_param[reply.a_nparam++] = (ParmType) s->last;
7451d4fba8b9Smrg    reply.a_inters = '#';
7452d4fba8b9Smrg    reply.a_final = 'Q';
7453d4fba8b9Smrg    unparseseq(xw, &reply);
7454d4fba8b9Smrg}
7455d4fba8b9Smrg#endif /* OPT_XTERM_SGR */
7456