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