trace.c revision a5ae21e4
1/* $XTermId: trace.c,v 1.234 2021/09/14 20:09:56 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 GraphicsExpose:
916	TRACE((" send_event:%d display %p major:%d minor:%d",
917	       ev->xgraphicsexpose.send_event,
918	       (void *) ev->xgraphicsexpose.display,
919	       ev->xgraphicsexpose.major_code,
920	       ev->xgraphicsexpose.minor_code));
921	break;
922    case SelectionClear:
923	TRACE((" selection:%s",
924	       TraceAtomName(ev->xselectionclear.display,
925			     ev->xselectionclear.selection)));
926	break;
927    case SelectionRequest:
928	TRACE((" owner:%#lx requestor:%#lx",
929	       ev->xselectionrequest.owner,
930	       ev->xselectionrequest.requestor));
931	TRACE((" selection:%s",
932	       TraceAtomName(ev->xselectionrequest.display,
933			     ev->xselectionrequest.selection)));
934	TRACE((" target:%s",
935	       TraceAtomName(ev->xselectionrequest.display,
936			     ev->xselectionrequest.target)));
937	TRACE((" property:%s",
938	       TraceAtomName(ev->xselectionrequest.display,
939			     ev->xselectionrequest.property)));
940	break;
941    default:
942	TRACE((":FIXME"));
943	break;
944    }
945    TRACE(("\n"));
946    if (params != 0 && *num_params != 0) {
947	Cardinal n;
948	for (n = 0; n < *num_params; ++n) {
949	    TRACE(("  param[%d] = %s\n", n, params[n]));
950	}
951    }
952}
953
954#if OPT_RENDERFONT && OPT_WIDE_CHARS
955void
956TraceFallback(XtermWidget xw, const char *tag, unsigned wc, int n, XftFont *font)
957{
958    TScreen *screen = TScreenOf(xw);
959    XGlyphInfo gi;
960    int expect = my_wcwidth((wchar_t) wc);
961    int hijack = mk_wcwidth_cjk((wchar_t) wc);
962    int actual;
963
964    XftTextExtents32(screen->display, font, &wc, 1, &gi);
965    actual = ((gi.xOff + FontWidth(screen) - 1)
966	      / FontWidth(screen));
967
968    TRACE(("FALLBACK #%d %s U+%04X %d,%d pos,"
969	   " %d,%d off," " %dx%d size,"
970	   " %d/%d next," " %d vs %d/%d cells%s\n",
971	   n + 1, tag, wc,
972	   gi.y, gi.x,
973	   gi.yOff, gi.xOff,
974	   gi.height, gi.width,
975	   font->max_advance_width,
976	   FontWidth(screen),
977	   actual, expect, hijack,
978	   ((actual != expect)
979	    ? ((actual == hijack)
980	       ? " OOPS"
981	       : " oops")
982	    : "")));
983}
984#endif /* OPT_RENDERFONT */
985
986void
987TraceFocus(Widget w, XEvent *ev)
988{
989    TRACE(("trace_focus event type %d:%s\n",
990	   ev->type, visibleEventType(ev->type)));
991    switch (ev->type) {
992    case FocusIn:
993    case FocusOut:
994	{
995	    XFocusChangeEvent *event = (XFocusChangeEvent *) ev;
996	    TRACE(("\tdetail: %s\n", visibleNotifyDetail(event->detail)));
997	    TRACE(("\tmode:   %s\n", visibleNotifyMode(event->mode)));
998	    TRACE(("\twindow: %#lx\n", event->window));
999	}
1000	break;
1001    case EnterNotify:
1002    case LeaveNotify:
1003	{
1004	    XCrossingEvent *event = (XCrossingEvent *) ev;
1005	    TRACE(("\tdetail:    %s\n", visibleNotifyDetail(event->detail)));
1006	    TRACE(("\tmode:      %s\n", visibleNotifyMode(event->mode)));
1007	    TRACE(("\twindow:    %#lx\n", event->window));
1008	    TRACE(("\tfocus:     %d\n", event->focus));
1009	    TRACE(("\troot:      %#lx\n", event->root));
1010	    TRACE(("\tsubwindow: %#lx\n", event->subwindow));
1011	}
1012	break;
1013    }
1014    while (w != 0) {
1015	TRACE(("w %p -> %#lx\n", (void *) w, XtWindow(w)));
1016	w = XtParent(w);
1017    }
1018}
1019
1020void
1021TraceSizeHints(XSizeHints * hints)
1022{
1023    TRACE(("size hints:\n"));
1024    if (hints->flags & (USPosition | PPosition)) {
1025	TRACE(("   position   %d,%d%s%s\n", hints->y, hints->x,
1026	       (hints->flags & USPosition) ? " user" : "",
1027	       (hints->flags & PPosition) ? " prog" : ""));
1028    }
1029    if (hints->flags & (USSize | PSize)) {
1030	TRACE(("   size       %d,%d%s%s\n", hints->height, hints->width,
1031	       (hints->flags & USSize) ? " user" : "",
1032	       (hints->flags & PSize) ? " prog" : ""));
1033    }
1034    if (hints->flags & PMinSize) {
1035	TRACE(("   min        %d,%d\n", hints->min_height, hints->min_width));
1036    }
1037    if (hints->flags & PMaxSize) {
1038	TRACE(("   max        %d,%d\n", hints->max_height, hints->max_width));
1039    }
1040    if (hints->flags & PResizeInc) {
1041	TRACE(("   inc        %d,%d\n", hints->height_inc, hints->width_inc));
1042    } else {
1043	TRACE(("   inc        NONE!\n"));
1044    }
1045    if (hints->flags & PAspect) {
1046	TRACE(("   min aspect %d/%d\n", hints->min_aspect.y, hints->min_aspect.y));
1047	TRACE(("   max aspect %d/%d\n", hints->max_aspect.y, hints->max_aspect.y));
1048    }
1049    if (hints->flags & PBaseSize) {
1050	TRACE(("   base       %d,%d\n", hints->base_height, hints->base_width));
1051    }
1052    if (hints->flags & PWinGravity) {
1053	TRACE(("   gravity    %d\n", hints->win_gravity));
1054    }
1055}
1056
1057static void
1058TraceEventMask(const char *tag, long mask)
1059{
1060#define DATA(name) { name##Mask, #name }
1061    /* *INDENT-OFF* */
1062    static struct {
1063	long mask;
1064	const char *name;
1065    } table[] = {
1066	DATA(KeyPress),
1067	DATA(KeyRelease),
1068	DATA(ButtonPress),
1069	DATA(ButtonRelease),
1070	DATA(EnterWindow),
1071	DATA(LeaveWindow),
1072	DATA(PointerMotion),
1073	DATA(PointerMotionHint),
1074	DATA(Button1Motion),
1075	DATA(Button2Motion),
1076	DATA(Button3Motion),
1077	DATA(Button4Motion),
1078	DATA(Button5Motion),
1079	DATA(ButtonMotion),
1080	DATA(KeymapState),
1081	DATA(Exposure),
1082	DATA(VisibilityChange),
1083	DATA(StructureNotify),
1084	DATA(ResizeRedirect),
1085	DATA(SubstructureNotify),
1086	DATA(SubstructureRedirect),
1087	DATA(FocusChange),
1088	DATA(PropertyChange),
1089	DATA(ColormapChange),
1090	DATA(OwnerGrabButton),
1091    };
1092#undef DATA
1093    Cardinal n;
1094    /* *INDENT-ON* */
1095
1096    for (n = 0; n < XtNumber(table); ++n) {
1097	if (table[n].mask & mask) {
1098	    TRACE(("%s %s\n", tag, table[n].name));
1099	}
1100    }
1101}
1102
1103void
1104TraceWindowAttributes(XWindowAttributes * attrs)
1105{
1106    TRACE(("window attributes:\n"));
1107    TRACE(("   position     %d,%d\n", attrs->y, attrs->x));
1108    TRACE(("   size         %dx%d\n", attrs->height, attrs->width));
1109    TRACE(("   border       %d\n", attrs->border_width));
1110    TRACE(("   depth        %d\n", attrs->depth));
1111    TRACE(("   bit_gravity  %d\n", attrs->bit_gravity));
1112    TRACE(("   win_gravity  %d\n", attrs->win_gravity));
1113    TRACE(("   root         %#lx\n", (long) attrs->root));
1114    TRACE(("   class        %s\n", ((attrs->class == InputOutput)
1115				    ? "InputOutput"
1116				    : ((attrs->class == InputOnly)
1117				       ? "InputOnly"
1118				       : "unknown"))));
1119    TRACE(("   map_state    %s\n", ((attrs->map_state == IsUnmapped)
1120				    ? "IsUnmapped"
1121				    : ((attrs->map_state == IsUnviewable)
1122				       ? "IsUnviewable"
1123				       : ((attrs->map_state == IsViewable)
1124					  ? "IsViewable"
1125					  : "unknown")))));
1126    TRACE(("   all_events\n"));
1127    TraceEventMask("        ", attrs->all_event_masks);
1128    TRACE(("   your_events\n"));
1129    TraceEventMask("        ", attrs->your_event_mask);
1130    TRACE(("   no_propagate\n"));
1131    TraceEventMask("        ", attrs->do_not_propagate_mask);
1132}
1133
1134void
1135TraceWMSizeHints(XtermWidget xw)
1136{
1137    XSizeHints sizehints = xw->hints;
1138
1139    getXtermSizeHints(xw);
1140    TraceSizeHints(&xw->hints);
1141    xw->hints = sizehints;
1142}
1143
1144const char *
1145ModifierName(unsigned modifier)
1146{
1147    const char *s = "";
1148    if (modifier & ShiftMask)
1149	s = " Shift";
1150    else if (modifier & LockMask)
1151	s = " Lock";
1152    else if (modifier & ControlMask)
1153	s = " Control";
1154    else if (modifier & Mod1Mask)
1155	s = " Mod1";
1156    else if (modifier & Mod2Mask)
1157	s = " Mod2";
1158    else if (modifier & Mod3Mask)
1159	s = " Mod3";
1160    else if (modifier & Mod4Mask)
1161	s = " Mod4";
1162    else if (modifier & Mod5Mask)
1163	s = " Mod5";
1164    return s;
1165}
1166
1167void
1168TraceTranslations(const char *name, Widget w)
1169{
1170    String result;
1171    XErrorHandler save = XSetErrorHandler(ignore_x11_error);
1172    XtTranslations xlations;
1173    Widget xcelerat;
1174
1175    TRACE(("TraceTranslations for %s (widget %#lx) " TRACE_L "\n",
1176	   name, (long) w));
1177    if (w) {
1178	XtVaGetValues(w,
1179		      XtNtranslations, &xlations,
1180		      XtNaccelerators, &xcelerat,
1181		      (XtPointer) 0);
1182	TRACE(("... xlations %#08lx\n", (long) xlations));
1183	TRACE(("... xcelerat %#08lx\n", (long) xcelerat));
1184	result = _XtPrintXlations(w, xlations, xcelerat, True);
1185	TRACE(("%s\n", NonNull(result)));
1186	if (result)
1187	    XFree((char *) result);
1188    } else {
1189	TRACE(("none (widget is null)\n"));
1190    }
1191    TRACE((TRACE_R "\n"));
1192    XSetErrorHandler(save);
1193}
1194
1195XtGeometryResult
1196TraceResizeRequest(const char *fn, int ln, Widget w,
1197		   unsigned reqwide,
1198		   unsigned reqhigh,
1199		   Dimension *gotwide,
1200		   Dimension *gothigh)
1201{
1202    XtGeometryResult rc;
1203
1204    TRACE(("%s@%d ResizeRequest %ux%u\n", fn, ln, reqhigh, reqwide));
1205    rc = XtMakeResizeRequest((Widget) w,
1206			     (Dimension) reqwide,
1207			     (Dimension) reqhigh,
1208			     gotwide, gothigh);
1209    TRACE(("... ResizeRequest -> "));
1210    if (gothigh && gotwide)
1211	TRACE(("%dx%d ", *gothigh, *gotwide));
1212    TRACE(("(%d)\n", rc));
1213    return rc;
1214}
1215
1216#define XRES_S(name) Trace(#name " = %s\n", NonNull(resp->name))
1217#define XRES_B(name) Trace(#name " = %s\n", MtoS(resp->name))
1218#define XRES_I(name) Trace(#name " = %d\n", resp->name)
1219
1220void
1221TraceXtermResources(void)
1222{
1223    XTERM_RESOURCE *resp = &resource;
1224
1225    Trace("XTERM_RESOURCE settings:\n");
1226    XRES_S(icon_geometry);
1227    XRES_S(title);
1228    XRES_S(icon_hint);
1229    XRES_S(icon_name);
1230    XRES_S(term_name);
1231    XRES_S(tty_modes);
1232    XRES_I(minBufSize);
1233    XRES_I(maxBufSize);
1234    XRES_B(hold_screen);
1235    XRES_B(utmpInhibit);
1236    XRES_B(utmpDisplayId);
1237    XRES_B(messages);
1238    XRES_S(menuLocale);
1239    XRES_S(omitTranslation);
1240    XRES_S(keyboardType);
1241#ifdef HAVE_LIB_XCURSOR
1242    XRES_S(cursorTheme);
1243#endif
1244#if OPT_PRINT_ON_EXIT
1245    XRES_I(printModeNow);
1246    XRES_I(printModeOnXError);
1247    XRES_I(printOptsNow);
1248    XRES_I(printOptsOnXError);
1249    XRES_S(printFileNow);
1250    XRES_S(printFileOnXError);
1251#endif
1252#if OPT_SUNPC_KBD
1253    XRES_B(sunKeyboard);
1254#endif
1255#if OPT_HP_FUNC_KEYS
1256    XRES_B(hpFunctionKeys);
1257#endif
1258#if OPT_SCO_FUNC_KEYS
1259    XRES_B(scoFunctionKeys);
1260#endif
1261#if OPT_SUN_FUNC_KEYS
1262    XRES_B(sunFunctionKeys);
1263#endif
1264#if OPT_INITIAL_ERASE
1265    XRES_B(ptyInitialErase);
1266    XRES_B(backarrow_is_erase);
1267#endif
1268    XRES_B(useInsertMode);
1269#if OPT_ZICONBEEP
1270    XRES_I(zIconBeep);
1271    XRES_S(zIconFormat);
1272#endif
1273#if OPT_PTY_HANDSHAKE
1274    XRES_B(wait_for_map);
1275    XRES_B(ptyHandshake);
1276    XRES_B(ptySttySize);
1277#endif
1278#if OPT_REPORT_CCLASS
1279    XRES_B(reportCClass);
1280#endif
1281#if OPT_REPORT_COLORS
1282    XRES_B(reportColors);
1283#endif
1284#if OPT_REPORT_FONTS
1285    XRES_B(reportFonts);
1286#endif
1287#if OPT_REPORT_ICONS
1288    XRES_B(reportIcons);
1289#endif
1290#if OPT_SAME_NAME
1291    XRES_B(sameName);
1292#endif
1293#if OPT_SESSION_MGT
1294    XRES_B(sessionMgt);
1295#endif
1296#if OPT_TOOLBAR
1297    XRES_B(toolBar);
1298#endif
1299#if OPT_MAXIMIZE
1300    XRES_B(maximized);
1301    XRES_S(fullscreen_s);
1302#endif
1303#if USE_DOUBLE_BUFFER
1304    XRES_B(buffered);
1305    XRES_I(buffered_fps);
1306#endif
1307}
1308
1309void
1310TraceArgv(const char *tag, char **argv)
1311{
1312    TRACE(("%s:\n", tag));
1313    if (argv != 0) {
1314	int n = 0;
1315
1316	while (*argv != 0) {
1317	    TRACE(("  %d:%s\n", n++, *argv++));
1318	}
1319    }
1320}
1321
1322static char *
1323parse_option(char *dst, String src, int first)
1324{
1325    char *s;
1326
1327    if (!strncmp(src, "-/+", (size_t) 3)) {
1328	dst[0] = (char) first;
1329	strcpy(dst + 1, src + 3);
1330    } else {
1331	strcpy(dst, src);
1332    }
1333    for (s = dst; *s != '\0'; s++) {
1334	if (*s == '#' || *s == '%' || *s == 'S') {
1335	    s[1] = '\0';
1336	} else if (*s == ' ') {
1337	    *s = '\0';
1338	    break;
1339	}
1340    }
1341    return dst;
1342}
1343
1344static Bool
1345same_option(OptionHelp * opt, XrmOptionDescRec * res)
1346{
1347    char temp[BUFSIZ];
1348    return !strcmp(parse_option(temp, opt->opt, res->option[0]), res->option);
1349}
1350
1351static Bool
1352standard_option(String opt)
1353{
1354    static const char *table[] =
1355    {
1356	"+rv",
1357	"+synchronous",
1358	"-background",
1359	"-bd",
1360	"-bg",
1361	"-bordercolor",
1362	"-borderwidth",
1363	"-bw",
1364	"-display",
1365	"-fg",
1366	"-fn",
1367	"-font",
1368	"-foreground",
1369	"-geometry",
1370	"-iconic",
1371	"-name",
1372	"-reverse",
1373	"-rv",
1374	"-selectionTimeout",
1375	"-synchronous",
1376	"-title",
1377	"-xnllanguage",
1378	"-xrm",
1379	"-xtsessionID",
1380    };
1381    Cardinal n;
1382    char temp[BUFSIZ];
1383
1384    opt = parse_option(temp, opt, '-');
1385    for (n = 0; n < XtNumber(table); n++) {
1386	if (!strcmp(opt, table[n]))
1387	    return True;
1388    }
1389    return False;
1390}
1391
1392/*
1393 * Analyse the options/help messages for inconsistencies.
1394 */
1395void
1396TraceOptions(OptionHelp * options, XrmOptionDescRec * resources, Cardinal res_count)
1397{
1398    OptionHelp *opt_array = sortedOpts(options, resources, res_count);
1399    size_t j, k;
1400    XrmOptionDescRec *res_array = sortedOptDescs(resources, res_count);
1401    Bool first, found;
1402
1403    TRACE(("Checking options-tables for inconsistencies:\n"));
1404
1405#if 0
1406    TRACE(("Options listed in help-message:\n"));
1407    for (j = 0; options[j].opt != 0; j++)
1408	TRACE(("%5d %-28s %s\n", j, opt_array[j].opt, opt_array[j].desc));
1409    TRACE(("Options listed in resource-table:\n"));
1410    for (j = 0; j < res_count; j++)
1411	TRACE(("%5d %-28s %s\n", j, res_array[j].option, res_array[j].specifier));
1412#endif
1413
1414    /* list all options[] not found in resources[] */
1415    for (j = 0, first = True; options[j].opt != 0; j++) {
1416	found = False;
1417	for (k = 0; k < res_count; k++) {
1418	    if (same_option(&opt_array[j], &res_array[k])) {
1419		found = True;
1420		break;
1421	    }
1422	}
1423	if (!found) {
1424	    if (first) {
1425		TRACE(("Options listed in help, not found in resource list:\n"));
1426		first = False;
1427	    }
1428	    TRACE(("  %-28s%s\n", opt_array[j].opt,
1429		   standard_option(opt_array[j].opt) ? " (standard)" : ""));
1430	}
1431    }
1432
1433    /* list all resources[] not found in options[] */
1434    for (j = 0, first = True; j < res_count; j++) {
1435	found = False;
1436	for (k = 0; options[k].opt != 0; k++) {
1437	    if (same_option(&opt_array[k], &res_array[j])) {
1438		found = True;
1439		break;
1440	    }
1441	}
1442	if (!found) {
1443	    if (first) {
1444		TRACE(("Resource list items not found in options-help:\n"));
1445		first = False;
1446	    }
1447	    TRACE(("  %s\n", res_array[j].option));
1448	}
1449    }
1450
1451    TRACE(("Resource list items that will be ignored by XtOpenApplication:\n"));
1452    for (j = 0; j < res_count; j++) {
1453	switch (res_array[j].argKind) {
1454	case XrmoptionSkipArg:
1455	    TRACE(("  %-28s {param}\n", res_array[j].option));
1456	    break;
1457	case XrmoptionSkipNArgs:
1458	    TRACE(("  %-28s {%ld params}\n", res_array[j].option, (long)
1459		   res_array[j].value));
1460	    break;
1461	case XrmoptionSkipLine:
1462	    TRACE(("  %-28s {remainder of line}\n", res_array[j].option));
1463	    break;
1464	case XrmoptionIsArg:
1465	case XrmoptionNoArg:
1466	case XrmoptionResArg:
1467	case XrmoptionSepArg:
1468	case XrmoptionStickyArg:
1469	default:
1470	    break;
1471	}
1472    }
1473}
1474#else
1475extern void empty_trace(void);
1476void
1477empty_trace(void)
1478{
1479}
1480#endif
1481