trace.c revision f2e35a3a
1/* $XTermId: trace.c,v 1.232 2021/02/02 00:20:30 tom Exp $ */
2
3/*
4 * Copyright 1997-2020,2021 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 */
32
33/*
34 * debugging support via TRACE macro.
35 */
36
37#include <xterm.h>		/* for definition of GCC_UNUSED */
38#include <xstrings.h>
39#include <wcwidth.h>
40#include <version.h>
41
42#if OPT_TRACE
43
44#include <data.h>
45#include <trace.h>
46
47#include <time.h>
48#include <stdlib.h>
49#include <unistd.h>
50#include <sys/types.h>
51#include <sys/stat.h>
52#include <stdio.h>
53#include <assert.h>
54
55#include <X11/Xatom.h>
56#include <X11/Xmu/Atoms.h>
57#include <X11/Xmu/Error.h>
58
59#ifdef HAVE_X11_TRANSLATEI_H
60#include <X11/ConvertI.h>
61#include <X11/TranslateI.h>
62#else
63#ifdef __cplusplus
64extern "C" {
65#endif
66
67    extern String _XtPrintXlations(Widget w,
68				   XtTranslations xlations,
69				   Widget accelWidget,
70				   _XtBoolean includeRHS);
71#ifdef __cplusplus
72}
73#endif
74#endif
75const char *trace_who = "parent";
76
77static FILE *trace_fp;
78
79static FILE *
80TraceOpen(void)
81{
82    static const char *trace_out;
83
84    if (trace_fp != 0
85	&& trace_who != trace_out) {
86	fclose(trace_fp);
87	trace_fp = 0;
88    }
89    trace_out = trace_who;
90
91    if (!trace_fp) {
92	static char dot[] = ".";
93	mode_t oldmask = umask(077);
94	/*
95	 * Put the trace-file in user's home-directory if the current
96	 * directory is not writable.
97	 */
98	char *home = (access(dot, R_OK | W_OK) == 0) ? dot : getenv("HOME");
99	char *name = malloc(strlen(home) + strlen(trace_who) + 50);
100#if OPT_TRACE_UNIQUE		/* usually I do not want unique names */
101	int unique;
102	for (unique = 0;; ++unique) {
103	    if (unique)
104		sprintf(name, "%s/Trace-%s.out-%d", home, trace_who, unique);
105	    else
106		sprintf(name, "%s/Trace-%s.out", home, trace_who);
107	    if ((trace_fp = fopen(name, "r")) == 0) {
108		break;
109	    }
110	    fclose(trace_fp);
111	}
112#else
113	sprintf(name, "%s/Trace-%s.out", home, trace_who);
114#endif
115	trace_fp = fopen(name, "w");
116	if (trace_fp != 0) {
117	    fprintf(trace_fp, "%s\n", xtermVersion());
118	    TraceIds(NULL, 0);
119	}
120	if (!trace_fp) {
121	    xtermWarning("Cannot open \"%s\"\n", name);
122	    exit(EXIT_FAILURE);
123	}
124	(void) umask(oldmask);
125	free(name);
126    }
127    return trace_fp;
128}
129
130void
131Trace(const char *fmt, ...)
132{
133    va_list ap;
134    FILE *fp = TraceOpen();
135
136    va_start(ap, fmt);
137    vfprintf(fp, fmt, ap);
138    (void) fflush(fp);
139    va_end(ap);
140}
141
142void
143TraceVA(const char *fmt, va_list ap)
144{
145    FILE *fp = TraceOpen();
146
147    vfprintf(fp, fmt, ap);
148    (void) fflush(fp);
149}
150
151void
152TraceClose(void)
153{
154    if (trace_fp != 0) {
155	(void) fclose(trace_fp);
156	(void) fflush(stdout);
157	(void) fflush(stderr);
158	(void) visibleChars(NULL, 0);
159	(void) visibleIChars(NULL, 0);
160	trace_fp = 0;
161    }
162}
163
164void
165TraceXError(Display *d, XErrorEvent *ev)
166{
167    FILE *fp = TraceOpen();
168    (void) XmuPrintDefaultErrorMessage(d, ev, fp);
169    (void) fflush(fp);
170}
171
172void
173TraceIds(const char *fname, int lnum)
174{
175    Trace("process %d ", (int) getpid());
176#ifdef HAVE_UNISTD_H
177    Trace("real (%u/%u) effective (%u/%u)",
178	  (unsigned) getuid(), (unsigned) getgid(),
179	  (unsigned) geteuid(), (unsigned) getegid());
180#endif
181    if (fname != 0) {
182	Trace(" (%s@%d)\n", fname, lnum);
183    } else {
184	time_t now = time((time_t *) 0);
185	Trace("-- %s", ctime(&now));
186    }
187}
188
189void
190TraceTime(const char *fname, int lnum)
191{
192    time_t now;
193    if (fname != 0) {
194	Trace("datetime (%s@%d) ", fname, lnum);
195    }
196    now = time((time_t *) 0);
197    Trace("-- %s", ctime(&now));
198}
199
200static void
201formatAscii(char *dst, unsigned value)
202{
203    switch (value) {
204    case '\\':
205	sprintf(dst, "\\\\");
206	break;
207    case '\b':
208	sprintf(dst, "\\b");
209	break;
210    case '\n':
211	sprintf(dst, "\\n");
212	break;
213    case '\r':
214	sprintf(dst, "\\r");
215	break;
216    case '\t':
217	sprintf(dst, "\\t");
218	break;
219    default:
220	if (E2A(value) < 32 || (E2A(value) >= 127 && E2A(value) < 160))
221	    sprintf(dst, "\\%03o", value & 0xff);
222	else
223	    sprintf(dst, "%c", CharOf(value));
224	break;
225    }
226}
227
228#if OPT_DEC_CHRSET
229
230const char *
231visibleDblChrset(unsigned chrset)
232{
233    const char *result = "?";
234    switch (chrset) {
235    case CSET_SWL:
236	result = "CSET_SWL";
237	break;
238    case CSET_DHL_TOP:
239	result = "CSET_DHL_TOP";
240	break;
241    case CSET_DHL_BOT:
242	result = "CSET_DHL_BOT";
243	break;
244    case CSET_DWL:
245	result = "CSET_DWL";
246	break;
247    }
248    return result;
249}
250#endif
251
252const char *
253visibleScsCode(DECNRCM_codes chrset)
254{
255#define MAP(to,from) case from: result = to ":" #from; break
256    const char *result = "<ERR>";
257    switch ((DECNRCM_codes) chrset) {
258	MAP("B", nrc_ASCII);
259	MAP("A", nrc_British);
260	MAP("A", nrc_British_Latin_1);
261	MAP("&4", nrc_DEC_Cyrillic);
262	MAP("0", nrc_DEC_Spec_Graphic);
263	MAP("1", nrc_DEC_Alt_Chars);
264	MAP("2", nrc_DEC_Alt_Graphics);
265	MAP("<", nrc_DEC_Supp);
266	MAP("%5", nrc_DEC_Supp_Graphic);
267	MAP(">", nrc_DEC_Technical);
268	MAP("4", nrc_Dutch);
269	MAP("5", nrc_Finnish);
270	MAP("C", nrc_Finnish2);
271	MAP("R", nrc_French);
272	MAP("f", nrc_French2);
273	MAP("Q", nrc_French_Canadian);
274	MAP("9", nrc_French_Canadian2);
275	MAP("K", nrc_German);
276	MAP("\"?", nrc_DEC_Greek_Supp);
277	MAP("\">", nrc_Greek);
278	MAP("F", nrc_ISO_Greek_Supp);
279	MAP("\"4", nrc_DEC_Hebrew_Supp);
280	MAP("%=", nrc_Hebrew);
281	MAP("H", nrc_ISO_Hebrew_Supp);
282	MAP("Y", nrc_Italian);
283	MAP("A", nrc_ISO_Latin_1_Supp);
284	MAP("B", nrc_ISO_Latin_2_Supp);
285	MAP("M", nrc_ISO_Latin_5_Supp);
286	MAP("L", nrc_ISO_Latin_Cyrillic);
287	MAP("`", nrc_Norwegian_Danish);
288	MAP("E", nrc_Norwegian_Danish2);
289	MAP("6", nrc_Norwegian_Danish3);
290	MAP("%6", nrc_Portugese);
291	MAP("&5", nrc_Russian);
292	MAP("%3", nrc_SCS_NRCS);
293	MAP("Z", nrc_Spanish);
294	MAP("7", nrc_Swedish);
295	MAP("H", nrc_Swedish2);
296	MAP("=", nrc_Swiss);
297	MAP("%2", nrc_Turkish);
298	MAP("%0", nrc_DEC_Turkish_Supp);
299	MAP("<UNK>", nrc_Unknown);
300    }
301#undef MAP
302    return result;
303}
304
305const char *
306visibleChars(const Char *buf, unsigned len)
307{
308    static char *result;
309    static unsigned used;
310
311    if (buf != 0) {
312	unsigned limit = ((len + 1) * 8) + 1;
313
314	if (limit > used) {
315	    used = limit;
316	    result = XtRealloc(result, used);
317	}
318	if (result != 0) {
319	    char *dst = result;
320	    *dst = '\0';
321	    while (len--) {
322		unsigned value = *buf++;
323		formatAscii(dst, value);
324		dst += strlen(dst);
325	    }
326	}
327    } else {
328	FreeAndNull(result);
329	used = 0;
330    }
331    return NonNull(result);
332}
333
334const char *
335visibleEventMode(EventMode value)
336{
337    const char *result;
338    switch (value) {
339    case NORMAL:
340	result = "NORMAL";
341	break;
342    case LEFTEXTENSION:
343	result = "LEFTEXTENSION";
344	break;
345    case RIGHTEXTENSION:
346	result = "RIGHTEXTENSION";
347	break;
348    default:
349	result = "?";
350	break;
351    }
352    return result;
353}
354
355const char *
356visibleIChars(const IChar *buf, unsigned len)
357{
358    static char *result;
359    static unsigned used;
360
361    if (buf != 0) {
362	unsigned limit = ((len + 1) * 12) + 1;
363
364	if (limit > used) {
365	    used = limit;
366	    result = XtRealloc(result, used);
367	}
368	if (result != 0) {
369	    char *dst = result;
370	    *dst = '\0';
371	    while (len--) {
372		unsigned value = *buf++;
373#if OPT_WIDE_CHARS
374		if (value > 255)
375		    sprintf(dst, "\\U+%04X", value);
376		else
377#endif
378		    formatAscii(dst, value);
379		dst += strlen(dst);
380	    }
381	}
382    } else {
383	FreeAndNull(result);
384	used = 0;
385    }
386    return NonNull(result);
387}
388
389const char *
390visibleUChar(unsigned chr)
391{
392    IChar buf[1];
393    buf[0] = (IChar) chr;
394    return visibleIChars(buf, 1);
395}
396
397const char *
398visibleEventType(int type)
399{
400    const char *result = "?";
401    switch (type) {
402	CASETYPE(KeyPress);
403	CASETYPE(KeyRelease);
404	CASETYPE(ButtonPress);
405	CASETYPE(ButtonRelease);
406	CASETYPE(MotionNotify);
407	CASETYPE(EnterNotify);
408	CASETYPE(LeaveNotify);
409	CASETYPE(FocusIn);
410	CASETYPE(FocusOut);
411	CASETYPE(KeymapNotify);
412	CASETYPE(Expose);
413	CASETYPE(GraphicsExpose);
414	CASETYPE(NoExpose);
415	CASETYPE(VisibilityNotify);
416	CASETYPE(CreateNotify);
417	CASETYPE(DestroyNotify);
418	CASETYPE(UnmapNotify);
419	CASETYPE(MapNotify);
420	CASETYPE(MapRequest);
421	CASETYPE(ReparentNotify);
422	CASETYPE(ConfigureNotify);
423	CASETYPE(ConfigureRequest);
424	CASETYPE(GravityNotify);
425	CASETYPE(ResizeRequest);
426	CASETYPE(CirculateNotify);
427	CASETYPE(CirculateRequest);
428	CASETYPE(PropertyNotify);
429	CASETYPE(SelectionClear);
430	CASETYPE(SelectionRequest);
431	CASETYPE(SelectionNotify);
432	CASETYPE(ColormapNotify);
433	CASETYPE(ClientMessage);
434	CASETYPE(MappingNotify);
435    }
436    return result;
437}
438
439const char *
440visibleMappingMode(int code)
441{
442    const char *result = "?";
443    switch (code) {
444	CASETYPE(MappingModifier);
445	CASETYPE(MappingKeyboard);
446	CASETYPE(MappingPointer);
447    }
448    return result;
449}
450
451const char *
452visibleNotifyMode(int code)
453{
454    const char *result = "?";
455    switch (code) {
456	CASETYPE(NotifyNormal);
457	CASETYPE(NotifyGrab);
458	CASETYPE(NotifyUngrab);
459	CASETYPE(NotifyWhileGrabbed);
460    }
461    return result;
462}
463
464const char *
465visibleNotifyDetail(int code)
466{
467    const char *result = "?";
468    switch (code) {
469	CASETYPE(NotifyAncestor);
470	CASETYPE(NotifyVirtual);
471	CASETYPE(NotifyInferior);
472	CASETYPE(NotifyNonlinear);
473	CASETYPE(NotifyNonlinearVirtual);
474	CASETYPE(NotifyPointer);
475	CASETYPE(NotifyPointerRoot);
476	CASETYPE(NotifyDetailNone);
477    }
478    return result;
479}
480
481const char *
482visibleSelectionTarget(Display *d, Atom a)
483{
484    const char *result = "?";
485
486    if (a == XA_STRING) {
487	result = "XA_STRING";
488    } else if (a == XA_TEXT(d)) {
489	result = "XA_TEXT()";
490    } else if (a == XA_COMPOUND_TEXT(d)) {
491	result = "XA_COMPOUND_TEXT()";
492    } else if (a == XA_UTF8_STRING(d)) {
493	result = "XA_UTF8_STRING()";
494    } else if (a == XA_TARGETS(d)) {
495	result = "XA_TARGETS()";
496    }
497
498    return result;
499}
500
501const char *
502visibleTekparse(int code)
503{
504    static const struct {
505	int code;
506	const char *name;
507    } table[] = {
508#include "Tekparse.cin"
509    };
510    const char *result = "?";
511    Cardinal n;
512    for (n = 0; n < XtNumber(table); ++n) {
513	if (table[n].code == code) {
514	    result = table[n].name;
515	    break;
516	}
517    }
518    return result;
519}
520
521const char *
522visibleVTparse(int code)
523{
524    static const struct {
525	int code;
526	const char *name;
527    } table[] = {
528#include "VTparse.cin"
529    };
530    const char *result = "?";
531    Cardinal n;
532    for (n = 0; n < XtNumber(table); ++n) {
533	if (table[n].code == code) {
534	    result = table[n].name;
535	    break;
536	}
537    }
538    return result;
539}
540
541const char *
542visibleXError(int code)
543{
544    static char temp[80];
545    const char *result = "?";
546    switch (code) {
547	CASETYPE(Success);
548	CASETYPE(BadRequest);
549	CASETYPE(BadValue);
550	CASETYPE(BadWindow);
551	CASETYPE(BadPixmap);
552	CASETYPE(BadAtom);
553	CASETYPE(BadCursor);
554	CASETYPE(BadFont);
555	CASETYPE(BadMatch);
556	CASETYPE(BadDrawable);
557	CASETYPE(BadAccess);
558	CASETYPE(BadAlloc);
559	CASETYPE(BadColor);
560	CASETYPE(BadGC);
561	CASETYPE(BadIDChoice);
562	CASETYPE(BadName);
563	CASETYPE(BadLength);
564	CASETYPE(BadImplementation);
565    default:
566	sprintf(temp, "%d", code);
567	result = temp;
568	break;
569    }
570    return result;
571}
572
573#if OPT_TRACE_FLAGS
574#define isScrnFlag(flag) ((flag) == LINEWRAPPED)
575
576static char *
577ScrnText(LineData *ld)
578{
579    return visibleIChars(ld->charData, ld->lineSize);
580}
581
582#define SHOW_BAD_LINE(name, ld) \
583	Trace("OOPS " #name " bad row\n")
584
585#define SHOW_SCRN_FLAG(name,code) \
586	Trace(#name " %s:%s\n", \
587	      code ? "*" : "", \
588	      ScrnText(ld))
589
590void
591LineClrFlag(LineData *ld, int flag)
592{
593    if (ld == 0) {
594	SHOW_BAD_LINE(LineClrFlag, ld);
595	assert(0);
596    } else if (isScrnFlag(flag)) {
597	SHOW_SCRN_FLAG(LineClrFlag, 0);
598    }
599
600    LineFlags(ld) &= ~flag;
601}
602
603void
604LineSetFlag(LineData *ld, int flag)
605{
606    if (ld == 0) {
607	SHOW_BAD_LINE(LineSetFlag, ld);
608	assert(0);
609    } else if (isScrnFlag(flag)) {
610	SHOW_SCRN_FLAG(LineSetFlag, 1);
611    }
612
613    LineFlags(ld) |= flag;
614}
615
616int
617LineTstFlag(LineData ld, int flag)
618{
619    int code = 0;
620    if (ld == 0) {
621	SHOW_BAD_LINE(LineTstFlag, ld);
622    } else {
623	code = LineFlags(ld);
624
625	if (isScrnFlag(flag)) {
626	    SHOW_SCRN_FLAG(LineTstFlag, code);
627	}
628    }
629    return code;
630}
631#endif /* OPT_TRACE_FLAGS */
632
633const char *
634TraceAtomName(Display *dpy, Atom atom)
635{
636    static char *result;
637    free(result);
638    if (atom != 0) {
639	result = XGetAtomName(dpy, atom);
640    } else {
641	result = x_strdup("NONE");
642    }
643    return result;
644}
645
646/*
647 * Trace the normal or alternate screen, showing color values up to 16, e.g.,
648 * for debugging with vttest.
649 */
650void
651TraceScreen(XtermWidget xw, int whichBuf)
652{
653    TScreen *screen = TScreenOf(xw);
654
655    if (screen->editBuf_index[whichBuf]) {
656	int row;
657
658	TRACE(("TraceScreen %d:\n", whichBuf));
659	for (row = 0; row <= screen->max_row; ++row) {
660	    LineData *ld = getLineData(screen, row);
661
662	    TRACE((" %3d:", row));
663	    if (ld != 0) {
664		int col;
665
666		for (col = 0; col < ld->lineSize; ++col) {
667		    int ch = (int) ld->charData[col];
668		    if (ch < ' ')
669			ch = ' ';
670		    if (ch >= 127)
671			ch = '#';
672		    TRACE(("%c", ch));
673		}
674		TRACE((":\n"));
675
676#if 0
677		TRACE(("  xx:"));
678		for (col = 0; col < ld->lineSize; ++col) {
679		    unsigned attrs = ld->attribs[col];
680		    char ch;
681		    if (attrs & PROTECTED) {
682			ch = '*';
683		    } else if (attrs & BLINK) {
684			ch = 'B';
685		    } else if (attrs & CHARDRAWN) {
686			ch = '+';
687		    } else {
688			ch = ' ';
689		    }
690		    TRACE(("%c", ch));
691		}
692		TRACE((":\n"));
693#endif
694
695#if 0
696		TRACE(("  fg:"));
697		for (col = 0; col < ld->lineSize; ++col) {
698		    unsigned fg = extract_fg(xw, ld->color[col], ld->attribs[col]);
699		    if (fg > 15)
700			fg = 15;
701		    TRACE(("%1x", fg));
702		}
703		TRACE((":\n"));
704
705		TRACE(("  bg:"));
706		for (col = 0; col < ld->lineSize; ++col) {
707		    unsigned bg = extract_bg(xw, ld->color[col], ld->attribs[col]);
708		    if (bg > 15)
709			bg = 15;
710		    TRACE(("%1x", bg));
711		}
712		TRACE((":\n"));
713#endif
714	    } else {
715		TRACE(("null lineData\n"));
716	    }
717	}
718    } else {
719	TRACE(("TraceScreen %d is nil\n", whichBuf));
720    }
721}
722
723static char *
724formatEventMask(char *target, unsigned source, Boolean buttons)
725{
726#define DATA(name) { name ## Mask, #name }
727    static struct {
728	unsigned mask;
729	const char *name;
730    } table[] = {
731	DATA(Shift),
732	    DATA(Lock),
733	    DATA(Control),
734	    DATA(Mod1),
735	    DATA(Mod2),
736	    DATA(Mod3),
737	    DATA(Mod4),
738	    DATA(Mod5),
739	    DATA(Button1),
740	    DATA(Button2),
741	    DATA(Button3),
742	    DATA(Button4),
743	    DATA(Button5),
744    };
745#undef DATA
746    Cardinal n;
747    char marker = L_CURL;
748    char *base = target;
749
750    for (n = 0; n < XtNumber(table); ++n) {
751	if (!buttons && (table[n].mask >= Button1Mask))
752	    continue;
753	if ((table[n].mask & source)) {
754	    UIntClr(source, table[n].mask);
755	    sprintf(target, "%c%s", marker, table[n].name);
756	    target += strlen(target);
757	    marker = '|';
758	}
759    }
760
761    if (source != 0) {
762	sprintf(target, "%c?%#x", marker, source);
763	target += strlen(target);
764	marker = '|';
765    }
766
767    if (marker == L_CURL)
768	*target++ = L_CURL;
769    *target++ = R_CURL;
770
771    *target = '\0';
772    return base;
773}
774
775void
776TraceEvent(const char *tag, XEvent *ev, String *params, Cardinal *num_params)
777{
778    char mask_buffer[160];
779
780    TRACE(("Event #%lu %s: %#lx %s",
781	   ev->xany.serial,
782	   tag,
783	   ev->xany.window,
784	   visibleEventType(ev->type)));
785
786    switch (ev->type) {
787    case KeyPress:
788	/* FALLTHRU */
789    case KeyRelease:
790	TRACE((" keycode 0x%04X %s",
791	       ev->xkey.keycode,
792	       formatEventMask(mask_buffer, ev->xkey.state, False)));
793	break;
794    case ButtonPress:
795	/* FALLTHRU */
796    case ButtonRelease:
797	TRACE((" button %u state %#x %s",
798	       ev->xbutton.button,
799	       ev->xbutton.state,
800	       formatEventMask(mask_buffer, ev->xbutton.state, True)));
801	break;
802    case MotionNotify:
803	TRACE((" (%d,%d) state %#x %s",
804	       ev->xmotion.y_root,
805	       ev->xmotion.x_root,
806	       ev->xmotion.state,
807	       formatEventMask(mask_buffer, ev->xmotion.state, True)));
808	break;
809    case EnterNotify:
810    case LeaveNotify:
811	TRACE((" at %d,%d root %d,%d %s %s",
812	       ev->xcrossing.y,
813	       ev->xcrossing.x,
814	       ev->xcrossing.y_root,
815	       ev->xcrossing.x_root,
816	       visibleNotifyMode(ev->xcrossing.mode),
817	       visibleNotifyDetail(ev->xcrossing.detail)));
818	break;
819    case FocusIn:
820    case FocusOut:
821	TRACE((" %s %s",
822	       visibleNotifyMode(ev->xfocus.mode),
823	       visibleNotifyDetail(ev->xfocus.detail)));
824	break;
825    case MapNotify:
826	TRACE((" event %#lx %s",
827	       ev->xmap.event,
828	       ev->xmap.override_redirect ? "override" : ""));
829	break;
830    case UnmapNotify:
831	TRACE((" event %#lx %s",
832	       ev->xunmap.event,
833	       ev->xunmap.from_configure ? "configure" : ""));
834	break;
835    case ReparentNotify:
836	TRACE((" at %d,%d event %#lx parent %#lx %s",
837	       ev->xreparent.y,
838	       ev->xreparent.x,
839	       ev->xreparent.event,
840	       ev->xreparent.parent,
841	       ev->xreparent.override_redirect ? "override" : ""));
842	break;
843    case ConfigureNotify:
844	TRACE((" at %d,%d size %dx%d bd %d above %#lx",
845	       ev->xconfigure.y,
846	       ev->xconfigure.x,
847	       ev->xconfigure.height,
848	       ev->xconfigure.width,
849	       ev->xconfigure.border_width,
850	       ev->xconfigure.above));
851	break;
852    case CreateNotify:
853	TRACE((" at %d,%d size %dx%d bd %d",
854	       ev->xcreatewindow.y,
855	       ev->xcreatewindow.x,
856	       ev->xcreatewindow.height,
857	       ev->xcreatewindow.width,
858	       ev->xcreatewindow.border_width));
859	break;
860    case ResizeRequest:
861	TRACE((" size %dx%d",
862	       ev->xresizerequest.height,
863	       ev->xresizerequest.width));
864	break;
865    case PropertyNotify:
866	TRACE((" %s %s",
867	       TraceAtomName(XtDisplay(toplevel), ev->xproperty.atom),
868	       ((ev->xproperty.state == PropertyNewValue)
869		? "NewValue"
870		: ((ev->xproperty.state == PropertyDelete)
871		   ? "Delete"
872		   : "?"))));
873
874	break;
875    case Expose:
876	TRACE((" at %d,%d size %dx%d count %d",
877	       ev->xexpose.y,
878	       ev->xexpose.x,
879	       ev->xexpose.height,
880	       ev->xexpose.width,
881	       ev->xexpose.count));
882	break;
883    case MappingNotify:
884	TRACE((" %s first_keycode %d count %d",
885	       visibleMappingMode(ev->xmapping.request),
886	       ev->xmapping.first_keycode,
887	       ev->xmapping.count));
888	break;
889    case VisibilityNotify:
890	TRACE((" state %d",
891	       ev->xvisibility.state));
892	break;
893    case KeymapNotify:
894	{
895	    Cardinal j;
896	    for (j = 0; j < XtNumber(ev->xkeymap.key_vector); ++j) {
897		if (ev->xkeymap.key_vector[j] != 0) {
898		    Cardinal k;
899		    for (k = 0; k < 8; ++k) {
900			if (ev->xkeymap.key_vector[j] & (1 << k)) {
901			    TRACE((" key%u", (j * 8) + k));
902			}
903		    }
904		}
905	    }
906	}
907	break;
908    case NoExpose:
909	TRACE((" send_event:%d display %p major:%d minor:%d",
910	       ev->xnoexpose.send_event,
911	       (void *) ev->xnoexpose.display,
912	       ev->xnoexpose.major_code,
913	       ev->xnoexpose.minor_code));
914	break;
915    case SelectionClear:
916	TRACE((" selection:%s",
917	       TraceAtomName(ev->xselectionclear.display,
918			     ev->xselectionclear.selection)));
919	break;
920    case SelectionRequest:
921	TRACE((" owner:%#lx requestor:%#lx",
922	       ev->xselectionrequest.owner,
923	       ev->xselectionrequest.requestor));
924	TRACE((" selection:%s",
925	       TraceAtomName(ev->xselectionrequest.display,
926			     ev->xselectionrequest.selection)));
927	TRACE((" target:%s",
928	       TraceAtomName(ev->xselectionrequest.display,
929			     ev->xselectionrequest.target)));
930	TRACE((" property:%s",
931	       TraceAtomName(ev->xselectionrequest.display,
932			     ev->xselectionrequest.property)));
933	break;
934    default:
935	TRACE((":FIXME"));
936	break;
937    }
938    TRACE(("\n"));
939    if (params != 0 && *num_params != 0) {
940	Cardinal n;
941	for (n = 0; n < *num_params; ++n) {
942	    TRACE(("  param[%d] = %s\n", n, params[n]));
943	}
944    }
945}
946
947#if OPT_RENDERFONT && OPT_WIDE_CHARS
948void
949TraceFallback(XtermWidget xw, const char *tag, unsigned wc, int n, XftFont *font)
950{
951    TScreen *screen = TScreenOf(xw);
952    XGlyphInfo gi;
953    int expect = my_wcwidth((wchar_t) wc);
954    int hijack = mk_wcwidth_cjk((wchar_t) wc);
955    int actual;
956
957    XftTextExtents32(screen->display, font, &wc, 1, &gi);
958    actual = ((gi.xOff + FontWidth(screen) - 1)
959	      / FontWidth(screen));
960
961    TRACE(("FALLBACK #%d %s U+%04X %d,%d pos,"
962	   " %d,%d off," " %dx%d size,"
963	   " %d/%d next," " %d vs %d/%d cells%s\n",
964	   n + 1, tag, wc,
965	   gi.y, gi.x,
966	   gi.yOff, gi.xOff,
967	   gi.height, gi.width,
968	   font->max_advance_width,
969	   FontWidth(screen),
970	   actual, expect, hijack,
971	   ((actual != expect)
972	    ? ((actual == hijack)
973	       ? " OOPS"
974	       : " oops")
975	    : "")));
976}
977#endif /* OPT_RENDERFONT */
978
979void
980TraceFocus(Widget w, XEvent *ev)
981{
982    TRACE(("trace_focus event type %d:%s\n",
983	   ev->type, visibleEventType(ev->type)));
984    switch (ev->type) {
985    case FocusIn:
986    case FocusOut:
987	{
988	    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
989	    TRACE(("\tdetail: %s\n", visibleNotifyDetail(event->detail)));
990	    TRACE(("\tmode:   %s\n", visibleNotifyMode(event->mode)));
991	    TRACE(("\twindow: %#lx\n", event->window));
992	}
993	break;
994    case EnterNotify:
995    case LeaveNotify:
996	{
997	    XCrossingEvent *event = (XCrossingEvent *) ev;
998	    TRACE(("\tdetail:    %s\n", visibleNotifyDetail(event->detail)));
999	    TRACE(("\tmode:      %s\n", visibleNotifyMode(event->mode)));
1000	    TRACE(("\twindow:    %#lx\n", event->window));
1001	    TRACE(("\tfocus:     %d\n", event->focus));
1002	    TRACE(("\troot:      %#lx\n", event->root));
1003	    TRACE(("\tsubwindow: %#lx\n", event->subwindow));
1004	}
1005	break;
1006    }
1007    while (w != 0) {
1008	TRACE(("w %p -> %#lx\n", (void *) w, XtWindow(w)));
1009	w = XtParent(w);
1010    }
1011}
1012
1013void
1014TraceSizeHints(XSizeHints * hints)
1015{
1016    TRACE(("size hints:\n"));
1017    if (hints->flags & (USPosition | PPosition)) {
1018	TRACE(("   position   %d,%d%s%s\n", hints->y, hints->x,
1019	       (hints->flags & USPosition) ? " user" : "",
1020	       (hints->flags & PPosition) ? " prog" : ""));
1021    }
1022    if (hints->flags & (USSize | PSize)) {
1023	TRACE(("   size       %d,%d%s%s\n", hints->height, hints->width,
1024	       (hints->flags & USSize) ? " user" : "",
1025	       (hints->flags & PSize) ? " prog" : ""));
1026    }
1027    if (hints->flags & PMinSize) {
1028	TRACE(("   min        %d,%d\n", hints->min_height, hints->min_width));
1029    }
1030    if (hints->flags & PMaxSize) {
1031	TRACE(("   max        %d,%d\n", hints->max_height, hints->max_width));
1032    }
1033    if (hints->flags & PResizeInc) {
1034	TRACE(("   inc        %d,%d\n", hints->height_inc, hints->width_inc));
1035    } else {
1036	TRACE(("   inc        NONE!\n"));
1037    }
1038    if (hints->flags & PAspect) {
1039	TRACE(("   min aspect %d/%d\n", hints->min_aspect.y, hints->min_aspect.y));
1040	TRACE(("   max aspect %d/%d\n", hints->max_aspect.y, hints->max_aspect.y));
1041    }
1042    if (hints->flags & PBaseSize) {
1043	TRACE(("   base       %d,%d\n", hints->base_height, hints->base_width));
1044    }
1045    if (hints->flags & PWinGravity) {
1046	TRACE(("   gravity    %d\n", hints->win_gravity));
1047    }
1048}
1049
1050static void
1051TraceEventMask(const char *tag, long mask)
1052{
1053#define DATA(name) { name##Mask, #name }
1054    /* *INDENT-OFF* */
1055    static struct {
1056	long mask;
1057	const char *name;
1058    } table[] = {
1059	DATA(KeyPress),
1060	DATA(KeyRelease),
1061	DATA(ButtonPress),
1062	DATA(ButtonRelease),
1063	DATA(EnterWindow),
1064	DATA(LeaveWindow),
1065	DATA(PointerMotion),
1066	DATA(PointerMotionHint),
1067	DATA(Button1Motion),
1068	DATA(Button2Motion),
1069	DATA(Button3Motion),
1070	DATA(Button4Motion),
1071	DATA(Button5Motion),
1072	DATA(ButtonMotion),
1073	DATA(KeymapState),
1074	DATA(Exposure),
1075	DATA(VisibilityChange),
1076	DATA(StructureNotify),
1077	DATA(ResizeRedirect),
1078	DATA(SubstructureNotify),
1079	DATA(SubstructureRedirect),
1080	DATA(FocusChange),
1081	DATA(PropertyChange),
1082	DATA(ColormapChange),
1083	DATA(OwnerGrabButton),
1084    };
1085#undef DATA
1086    Cardinal n;
1087    /* *INDENT-ON* */
1088
1089    for (n = 0; n < XtNumber(table); ++n) {
1090	if (table[n].mask & mask) {
1091	    TRACE(("%s %s\n", tag, table[n].name));
1092	}
1093    }
1094}
1095
1096void
1097TraceWindowAttributes(XWindowAttributes * attrs)
1098{
1099    TRACE(("window attributes:\n"));
1100    TRACE(("   position     %d,%d\n", attrs->y, attrs->x));
1101    TRACE(("   size         %dx%d\n", attrs->height, attrs->width));
1102    TRACE(("   border       %d\n", attrs->border_width));
1103    TRACE(("   depth        %d\n", attrs->depth));
1104    TRACE(("   bit_gravity  %d\n", attrs->bit_gravity));
1105    TRACE(("   win_gravity  %d\n", attrs->win_gravity));
1106    TRACE(("   root         %#lx\n", (long) attrs->root));
1107    TRACE(("   class        %s\n", ((attrs->class == InputOutput)
1108				    ? "InputOutput"
1109				    : ((attrs->class == InputOnly)
1110				       ? "InputOnly"
1111				       : "unknown"))));
1112    TRACE(("   map_state    %s\n", ((attrs->map_state == IsUnmapped)
1113				    ? "IsUnmapped"
1114				    : ((attrs->map_state == IsUnviewable)
1115				       ? "IsUnviewable"
1116				       : ((attrs->map_state == IsViewable)
1117					  ? "IsViewable"
1118					  : "unknown")))));
1119    TRACE(("   all_events\n"));
1120    TraceEventMask("        ", attrs->all_event_masks);
1121    TRACE(("   your_events\n"));
1122    TraceEventMask("        ", attrs->your_event_mask);
1123    TRACE(("   no_propagate\n"));
1124    TraceEventMask("        ", attrs->do_not_propagate_mask);
1125}
1126
1127void
1128TraceWMSizeHints(XtermWidget xw)
1129{
1130    XSizeHints sizehints = xw->hints;
1131
1132    getXtermSizeHints(xw);
1133    TraceSizeHints(&xw->hints);
1134    xw->hints = sizehints;
1135}
1136
1137const char *
1138ModifierName(unsigned modifier)
1139{
1140    const char *s = "";
1141    if (modifier & ShiftMask)
1142	s = " Shift";
1143    else if (modifier & LockMask)
1144	s = " Lock";
1145    else if (modifier & ControlMask)
1146	s = " Control";
1147    else if (modifier & Mod1Mask)
1148	s = " Mod1";
1149    else if (modifier & Mod2Mask)
1150	s = " Mod2";
1151    else if (modifier & Mod3Mask)
1152	s = " Mod3";
1153    else if (modifier & Mod4Mask)
1154	s = " Mod4";
1155    else if (modifier & Mod5Mask)
1156	s = " Mod5";
1157    return s;
1158}
1159
1160void
1161TraceTranslations(const char *name, Widget w)
1162{
1163    String result;
1164    XErrorHandler save = XSetErrorHandler(ignore_x11_error);
1165    XtTranslations xlations;
1166    Widget xcelerat;
1167
1168    TRACE(("TraceTranslations for %s (widget %#lx) " TRACE_L "\n",
1169	   name, (long) w));
1170    if (w) {
1171	XtVaGetValues(w,
1172		      XtNtranslations, &xlations,
1173		      XtNaccelerators, &xcelerat,
1174		      (XtPointer) 0);
1175	TRACE(("... xlations %#08lx\n", (long) xlations));
1176	TRACE(("... xcelerat %#08lx\n", (long) xcelerat));
1177	result = _XtPrintXlations(w, xlations, xcelerat, True);
1178	TRACE(("%s\n", NonNull(result)));
1179	if (result)
1180	    XFree((char *) result);
1181    } else {
1182	TRACE(("none (widget is null)\n"));
1183    }
1184    TRACE((TRACE_R "\n"));
1185    XSetErrorHandler(save);
1186}
1187
1188XtGeometryResult
1189TraceResizeRequest(const char *fn, int ln, Widget w,
1190		   unsigned reqwide,
1191		   unsigned reqhigh,
1192		   Dimension *gotwide,
1193		   Dimension *gothigh)
1194{
1195    XtGeometryResult rc;
1196
1197    TRACE(("%s@%d ResizeRequest %ux%u\n", fn, ln, reqhigh, reqwide));
1198    rc = XtMakeResizeRequest((Widget) w,
1199			     (Dimension) reqwide,
1200			     (Dimension) reqhigh,
1201			     gotwide, gothigh);
1202    TRACE(("... ResizeRequest -> "));
1203    if (gothigh && gotwide)
1204	TRACE(("%dx%d ", *gothigh, *gotwide));
1205    TRACE(("(%d)\n", rc));
1206    return rc;
1207}
1208
1209#define XRES_S(name) Trace(#name " = %s\n", NonNull(resp->name))
1210#define XRES_B(name) Trace(#name " = %s\n", MtoS(resp->name))
1211#define XRES_I(name) Trace(#name " = %d\n", resp->name)
1212
1213void
1214TraceXtermResources(void)
1215{
1216    XTERM_RESOURCE *resp = &resource;
1217
1218    Trace("XTERM_RESOURCE settings:\n");
1219    XRES_S(icon_geometry);
1220    XRES_S(title);
1221    XRES_S(icon_hint);
1222    XRES_S(icon_name);
1223    XRES_S(term_name);
1224    XRES_S(tty_modes);
1225    XRES_I(minBufSize);
1226    XRES_I(maxBufSize);
1227    XRES_B(hold_screen);
1228    XRES_B(utmpInhibit);
1229    XRES_B(utmpDisplayId);
1230    XRES_B(messages);
1231    XRES_S(menuLocale);
1232    XRES_S(omitTranslation);
1233    XRES_S(keyboardType);
1234#if OPT_PRINT_ON_EXIT
1235    XRES_I(printModeNow);
1236    XRES_I(printModeOnXError);
1237    XRES_I(printOptsNow);
1238    XRES_I(printOptsOnXError);
1239    XRES_S(printFileNow);
1240    XRES_S(printFileOnXError);
1241#endif
1242#if OPT_SUNPC_KBD
1243    XRES_B(sunKeyboard);
1244#endif
1245#if OPT_HP_FUNC_KEYS
1246    XRES_B(hpFunctionKeys);
1247#endif
1248#if OPT_SCO_FUNC_KEYS
1249    XRES_B(scoFunctionKeys);
1250#endif
1251#if OPT_SUN_FUNC_KEYS
1252    XRES_B(sunFunctionKeys);
1253#endif
1254#if OPT_INITIAL_ERASE
1255    XRES_B(ptyInitialErase);
1256    XRES_B(backarrow_is_erase);
1257#endif
1258    XRES_B(useInsertMode);
1259#if OPT_ZICONBEEP
1260    XRES_I(zIconBeep);
1261    XRES_S(zIconFormat);
1262#endif
1263#if OPT_PTY_HANDSHAKE
1264    XRES_B(wait_for_map);
1265    XRES_B(ptyHandshake);
1266    XRES_B(ptySttySize);
1267#endif
1268#if OPT_REPORT_CCLASS
1269    XRES_B(reportCClass);
1270#endif
1271#if OPT_REPORT_COLORS
1272    XRES_B(reportColors);
1273#endif
1274#if OPT_REPORT_FONTS
1275    XRES_B(reportFonts);
1276#endif
1277#if OPT_REPORT_ICONS
1278    XRES_B(reportIcons);
1279#endif
1280#if OPT_SAME_NAME
1281    XRES_B(sameName);
1282#endif
1283#if OPT_SESSION_MGT
1284    XRES_B(sessionMgt);
1285#endif
1286#if OPT_TOOLBAR
1287    XRES_B(toolBar);
1288#endif
1289#if OPT_MAXIMIZE
1290    XRES_B(maximized);
1291    XRES_S(fullscreen_s);
1292#endif
1293#if USE_DOUBLE_BUFFER
1294    XRES_B(buffered);
1295    XRES_I(buffered_fps);
1296#endif
1297}
1298
1299void
1300TraceArgv(const char *tag, char **argv)
1301{
1302    TRACE(("%s:\n", tag));
1303    if (argv != 0) {
1304	int n = 0;
1305
1306	while (*argv != 0) {
1307	    TRACE(("  %d:%s\n", n++, *argv++));
1308	}
1309    }
1310}
1311
1312static char *
1313parse_option(char *dst, String src, int first)
1314{
1315    char *s;
1316
1317    if (!strncmp(src, "-/+", (size_t) 3)) {
1318	dst[0] = (char) first;
1319	strcpy(dst + 1, src + 3);
1320    } else {
1321	strcpy(dst, src);
1322    }
1323    for (s = dst; *s != '\0'; s++) {
1324	if (*s == '#' || *s == '%' || *s == 'S') {
1325	    s[1] = '\0';
1326	} else if (*s == ' ') {
1327	    *s = '\0';
1328	    break;
1329	}
1330    }
1331    return dst;
1332}
1333
1334static Bool
1335same_option(OptionHelp * opt, XrmOptionDescRec * res)
1336{
1337    char temp[BUFSIZ];
1338    return !strcmp(parse_option(temp, opt->opt, res->option[0]), res->option);
1339}
1340
1341static Bool
1342standard_option(String opt)
1343{
1344    static const char *table[] =
1345    {
1346	"+rv",
1347	"+synchronous",
1348	"-background",
1349	"-bd",
1350	"-bg",
1351	"-bordercolor",
1352	"-borderwidth",
1353	"-bw",
1354	"-display",
1355	"-fg",
1356	"-fn",
1357	"-font",
1358	"-foreground",
1359	"-geometry",
1360	"-iconic",
1361	"-name",
1362	"-reverse",
1363	"-rv",
1364	"-selectionTimeout",
1365	"-synchronous",
1366	"-title",
1367	"-xnllanguage",
1368	"-xrm",
1369	"-xtsessionID",
1370    };
1371    Cardinal n;
1372    char temp[BUFSIZ];
1373
1374    opt = parse_option(temp, opt, '-');
1375    for (n = 0; n < XtNumber(table); n++) {
1376	if (!strcmp(opt, table[n]))
1377	    return True;
1378    }
1379    return False;
1380}
1381
1382/*
1383 * Analyse the options/help messages for inconsistencies.
1384 */
1385void
1386TraceOptions(OptionHelp * options, XrmOptionDescRec * resources, Cardinal res_count)
1387{
1388    OptionHelp *opt_array = sortedOpts(options, resources, res_count);
1389    size_t j, k;
1390    XrmOptionDescRec *res_array = sortedOptDescs(resources, res_count);
1391    Bool first, found;
1392
1393    TRACE(("Checking options-tables for inconsistencies:\n"));
1394
1395#if 0
1396    TRACE(("Options listed in help-message:\n"));
1397    for (j = 0; options[j].opt != 0; j++)
1398	TRACE(("%5d %-28s %s\n", j, opt_array[j].opt, opt_array[j].desc));
1399    TRACE(("Options listed in resource-table:\n"));
1400    for (j = 0; j < res_count; j++)
1401	TRACE(("%5d %-28s %s\n", j, res_array[j].option, res_array[j].specifier));
1402#endif
1403
1404    /* list all options[] not found in resources[] */
1405    for (j = 0, first = True; options[j].opt != 0; j++) {
1406	found = False;
1407	for (k = 0; k < res_count; k++) {
1408	    if (same_option(&opt_array[j], &res_array[k])) {
1409		found = True;
1410		break;
1411	    }
1412	}
1413	if (!found) {
1414	    if (first) {
1415		TRACE(("Options listed in help, not found in resource list:\n"));
1416		first = False;
1417	    }
1418	    TRACE(("  %-28s%s\n", opt_array[j].opt,
1419		   standard_option(opt_array[j].opt) ? " (standard)" : ""));
1420	}
1421    }
1422
1423    /* list all resources[] not found in options[] */
1424    for (j = 0, first = True; j < res_count; j++) {
1425	found = False;
1426	for (k = 0; options[k].opt != 0; k++) {
1427	    if (same_option(&opt_array[k], &res_array[j])) {
1428		found = True;
1429		break;
1430	    }
1431	}
1432	if (!found) {
1433	    if (first) {
1434		TRACE(("Resource list items not found in options-help:\n"));
1435		first = False;
1436	    }
1437	    TRACE(("  %s\n", res_array[j].option));
1438	}
1439    }
1440
1441    TRACE(("Resource list items that will be ignored by XtOpenApplication:\n"));
1442    for (j = 0; j < res_count; j++) {
1443	switch (res_array[j].argKind) {
1444	case XrmoptionSkipArg:
1445	    TRACE(("  %-28s {param}\n", res_array[j].option));
1446	    break;
1447	case XrmoptionSkipNArgs:
1448	    TRACE(("  %-28s {%ld params}\n", res_array[j].option, (long)
1449		   res_array[j].value));
1450	    break;
1451	case XrmoptionSkipLine:
1452	    TRACE(("  %-28s {remainder of line}\n", res_array[j].option));
1453	    break;
1454	case XrmoptionIsArg:
1455	case XrmoptionNoArg:
1456	case XrmoptionResArg:
1457	case XrmoptionSepArg:
1458	case XrmoptionStickyArg:
1459	default:
1460	    break;
1461	}
1462    }
1463}
1464#else
1465extern void empty_trace(void);
1466void
1467empty_trace(void)
1468{
1469}
1470#endif
1471