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