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