misc.c revision 9a64e1c5
1/* $XTermId: misc.c,v 1.712 2014/05/26 14:45:58 tom Exp $ */ 2 3/* 4 * Copyright 1999-2013,2014 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 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 34 * 35 * All Rights Reserved 36 * 37 * Permission to use, copy, modify, and distribute this software and its 38 * documentation for any purpose and without fee is hereby granted, 39 * provided that the above copyright notice appear in all copies and that 40 * both that copyright notice and this permission notice appear in 41 * supporting documentation, and that the name of Digital Equipment 42 * Corporation not be used in advertising or publicity pertaining to 43 * distribution of the software without specific, written prior permission. 44 * 45 * 46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 52 * SOFTWARE. 53 */ 54 55#include <version.h> 56#include <main.h> 57#include <xterm.h> 58 59#include <sys/stat.h> 60#include <stdio.h> 61#include <stdarg.h> 62#include <signal.h> 63#include <ctype.h> 64#include <pwd.h> 65#include <sys/wait.h> 66 67#include <X11/keysym.h> 68#include <X11/Xatom.h> 69#include <X11/cursorfont.h> 70#include <X11/Xlocale.h> 71 72#include <X11/Xmu/Error.h> 73#include <X11/Xmu/SysUtil.h> 74#include <X11/Xmu/WinUtil.h> 75#include <X11/Xmu/Xmu.h> 76#if HAVE_X11_SUNKEYSYM_H 77#include <X11/Sunkeysym.h> 78#endif 79 80#ifdef HAVE_LIBXPM 81#include <X11/xpm.h> 82#endif 83 84#ifdef HAVE_LANGINFO_CODESET 85#include <langinfo.h> 86#endif 87 88#include <xutf8.h> 89 90#include <data.h> 91#include <error.h> 92#include <menu.h> 93#include <fontutils.h> 94#include <xstrings.h> 95#include <xtermcap.h> 96#include <VTparse.h> 97#include <graphics.h> 98#include <graphics_regis.h> 99#include <graphics_sixel.h> 100 101#include <assert.h> 102 103#if (XtSpecificationRelease < 6) 104#ifndef X_GETTIMEOFDAY 105#define X_GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *)0) 106#endif 107#endif 108 109#ifdef VMS 110#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT" 111#ifdef ALLOWLOGFILEEXEC 112#undef ALLOWLOGFILEEXEC 113#endif 114#endif /* VMS */ 115 116#if OPT_TEK4014 117#define OUR_EVENT(event,Type) \ 118 (event.type == Type && \ 119 (event.xcrossing.window == XtWindow(XtParent(xw)) || \ 120 (tekWidget && \ 121 event.xcrossing.window == XtWindow(XtParent(tekWidget))))) 122#else 123#define OUR_EVENT(event,Type) \ 124 (event.type == Type && \ 125 (event.xcrossing.window == XtWindow(XtParent(xw)))) 126#endif 127 128static Boolean xtermAllocColor(XtermWidget, XColor *, const char *); 129static Cursor make_hidden_cursor(XtermWidget); 130 131static char emptyString[] = ""; 132 133#if OPT_EXEC_XTERM 134/* Like readlink(2), but returns a malloc()ed buffer, or NULL on 135 error; adapted from libc docs */ 136static char * 137Readlink(const char *filename) 138{ 139 char *buf = NULL; 140 size_t size = 100; 141 int n; 142 143 for (;;) { 144 buf = TypeRealloc(char, size, buf); 145 memset(buf, 0, size); 146 147 n = (int) readlink(filename, buf, size); 148 if (n < 0) { 149 free(buf); 150 return NULL; 151 } 152 153 if ((unsigned) n < size) { 154 return buf; 155 } 156 157 size *= 2; 158 } 159} 160#endif /* OPT_EXEC_XTERM */ 161 162static void 163Sleep(int msec) 164{ 165 static struct timeval select_timeout; 166 167 select_timeout.tv_sec = 0; 168 select_timeout.tv_usec = msec * 1000; 169 select(0, 0, 0, 0, &select_timeout); 170} 171 172static void 173selectwindow(XtermWidget xw, int flag) 174{ 175 TScreen *screen = TScreenOf(xw); 176 177 TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag)); 178 179#if OPT_TEK4014 180 if (TEK4014_ACTIVE(xw)) { 181 if (!Ttoggled) 182 TCursorToggle(tekWidget, TOGGLE); 183 screen->select |= flag; 184 if (!Ttoggled) 185 TCursorToggle(tekWidget, TOGGLE); 186 } else 187#endif 188 { 189#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD 190 TInput *input = lookupTInput(xw, (Widget) xw); 191 if (input && input->xic) 192 XSetICFocus(input->xic); 193#endif 194 195 if (screen->cursor_state && CursorMoved(screen)) 196 HideCursor(); 197 screen->select |= flag; 198 if (screen->cursor_state) 199 ShowCursor(); 200 } 201 GetScrollLock(screen); 202} 203 204static void 205unselectwindow(XtermWidget xw, int flag) 206{ 207 TScreen *screen = TScreenOf(xw); 208 209 TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag)); 210 211 if (screen->hide_pointer && screen->pointer_mode < pFocused) { 212 screen->hide_pointer = False; 213 xtermDisplayCursor(xw); 214 } 215 216 if (!screen->always_highlight) { 217#if OPT_TEK4014 218 if (TEK4014_ACTIVE(xw)) { 219 if (!Ttoggled) 220 TCursorToggle(tekWidget, TOGGLE); 221 screen->select &= ~flag; 222 if (!Ttoggled) 223 TCursorToggle(tekWidget, TOGGLE); 224 } else 225#endif 226 { 227#if OPT_I18N_SUPPORT && OPT_INPUT_METHOD 228 TInput *input = lookupTInput(xw, (Widget) xw); 229 if (input && input->xic) 230 XUnsetICFocus(input->xic); 231#endif 232 233 screen->select &= ~flag; 234 if (screen->cursor_state && CursorMoved(screen)) 235 HideCursor(); 236 if (screen->cursor_state) 237 ShowCursor(); 238 } 239 } 240} 241 242static void 243DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev) 244{ 245 TScreen *screen = TScreenOf(xw); 246 247 TRACE(("DoSpecialEnterNotify(%d)\n", screen->select)); 248 TRACE_FOCUS(xw, ev); 249 if (((ev->detail) != NotifyInferior) && 250 ev->focus && 251 !(screen->select & FOCUS)) 252 selectwindow(xw, INWINDOW); 253} 254 255static void 256DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev) 257{ 258 TScreen *screen = TScreenOf(xw); 259 260 TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select)); 261 TRACE_FOCUS(xw, ev); 262 if (((ev->detail) != NotifyInferior) && 263 ev->focus && 264 !(screen->select & FOCUS)) 265 unselectwindow(xw, INWINDOW); 266} 267 268#ifndef XUrgencyHint 269#define XUrgencyHint (1L << 8) /* X11R5 does not define */ 270#endif 271 272static void 273setXUrgency(XtermWidget xw, Bool enable) 274{ 275 TScreen *screen = TScreenOf(xw); 276 277 if (screen->bellIsUrgent) { 278 XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw)); 279 if (h != 0) { 280 if (enable && !(screen->select & FOCUS)) { 281 h->flags |= XUrgencyHint; 282 } else { 283 h->flags &= ~XUrgencyHint; 284 } 285 XSetWMHints(screen->display, VShellWindow(xw), h); 286 } 287 } 288} 289 290void 291do_xevents(void) 292{ 293 TScreen *screen = TScreenOf(term); 294 295 if (xtermAppPending() 296 || 297#if defined(VMS) || defined(__VMS) 298 screen->display->qlen > 0 299#else 300 GetBytesAvailable(ConnectionNumber(screen->display)) > 0 301#endif 302 ) 303 xevents(); 304} 305 306void 307xtermDisplayCursor(XtermWidget xw) 308{ 309 TScreen *screen = TScreenOf(xw); 310 311 if (screen->Vshow) { 312 if (screen->hide_pointer) { 313 TRACE(("Display hidden_cursor\n")); 314 XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor); 315 } else { 316 TRACE(("Display pointer_cursor\n")); 317 recolor_cursor(screen, 318 screen->pointer_cursor, 319 T_COLOR(screen, MOUSE_FG), 320 T_COLOR(screen, MOUSE_BG)); 321 XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor); 322 } 323 } 324} 325 326void 327xtermShowPointer(XtermWidget xw, Bool enable) 328{ 329 static int tried = -1; 330 TScreen *screen = TScreenOf(xw); 331 332#if OPT_TEK4014 333 if (TEK4014_SHOWN(xw)) 334 enable = True; 335#endif 336 337 /* 338 * Whether we actually hide the pointer depends on the pointer-mode and 339 * the mouse-mode: 340 */ 341 if (!enable) { 342 switch (screen->pointer_mode) { 343 case pNever: 344 enable = True; 345 break; 346 case pNoMouse: 347 if (screen->send_mouse_pos != MOUSE_OFF) 348 enable = True; 349 break; 350 case pAlways: 351 case pFocused: 352 break; 353 } 354 } 355 356 if (enable) { 357 if (screen->hide_pointer) { 358 screen->hide_pointer = False; 359 xtermDisplayCursor(xw); 360 switch (screen->send_mouse_pos) { 361 case ANY_EVENT_MOUSE: 362 break; 363 default: 364 MotionOff(screen, xw); 365 break; 366 } 367 } 368 } else if (!(screen->hide_pointer) && (tried <= 0)) { 369 if (screen->hidden_cursor == 0) { 370 screen->hidden_cursor = make_hidden_cursor(xw); 371 } 372 if (screen->hidden_cursor == 0) { 373 tried = 1; 374 } else { 375 tried = 0; 376 screen->hide_pointer = True; 377 xtermDisplayCursor(xw); 378 MotionOn(screen, xw); 379 } 380 } 381} 382 383#if OPT_TRACE 384static void 385TraceExposeEvent(XEvent *arg) 386{ 387 XExposeEvent *event = (XExposeEvent *) arg; 388 389 TRACE(("pending Expose %ld %d: %d,%d %dx%d %#lx\n", 390 event->serial, 391 event->count, 392 event->y, 393 event->x, 394 event->height, 395 event->width, 396 event->window)); 397} 398 399#else 400#define TraceExposeEvent(event) /* nothing */ 401#endif 402 403/* true if p contains q */ 404#define ExposeContains(p,q) \ 405 ((p)->y <= (q)->y \ 406 && (p)->x <= (q)->x \ 407 && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \ 408 && ((p)->x + (p)->width) >= ((q)->x + (q)->width)) 409 410static XtInputMask 411mergeExposeEvents(XEvent *target) 412{ 413 XEvent next_event; 414 XExposeEvent *p, *q; 415 416 TRACE(("pending Expose...?\n")); 417 TraceExposeEvent(target); 418 XtAppNextEvent(app_con, target); 419 p = (XExposeEvent *) target; 420 421 while (XtAppPending(app_con) 422 && XtAppPeekEvent(app_con, &next_event) 423 && next_event.type == Expose) { 424 Boolean merge_this = False; 425 426 TraceExposeEvent(&next_event); 427 q = (XExposeEvent *) (&next_event); 428 XtAppNextEvent(app_con, &next_event); 429 430 /* 431 * If either window is contained within the other, merge the events. 432 * The traces show that there are also cases where a full repaint of 433 * a window is broken into 3 or more rectangles, which do not arrive 434 * in the same instant. We could merge those if xterm were modified 435 * to skim several events ahead. 436 */ 437 if (p->window == q->window) { 438 if (ExposeContains(p, q)) { 439 TRACE(("pending Expose...merged forward\n")); 440 merge_this = True; 441 next_event = *target; 442 } else if (ExposeContains(q, p)) { 443 TRACE(("pending Expose...merged backward\n")); 444 merge_this = True; 445 } 446 } 447 if (!merge_this) { 448 XtDispatchEvent(target); 449 } 450 *target = next_event; 451 } 452 XtDispatchEvent(target); 453 return XtAppPending(app_con); 454} 455 456#if OPT_TRACE 457static void 458TraceConfigureEvent(XEvent *arg) 459{ 460 XConfigureEvent *event = (XConfigureEvent *) arg; 461 462 TRACE(("pending Configure %ld %d,%d %dx%d %#lx\n", 463 event->serial, 464 event->y, 465 event->x, 466 event->height, 467 event->width, 468 event->window)); 469} 470 471#else 472#define TraceConfigureEvent(event) /* nothing */ 473#endif 474 475/* 476 * On entry, we have peeked at the event queue and see a configure-notify 477 * event. Remove that from the queue so we can look further. 478 * 479 * Then, as long as there is a configure-notify event in the queue, remove 480 * that. If the adjacent events are for different windows, process the older 481 * event and update the event used for comparing windows. If they are for the 482 * same window, only the newer event is of interest. 483 * 484 * Finally, process the (remaining) configure-notify event. 485 */ 486static XtInputMask 487mergeConfigureEvents(XEvent *target) 488{ 489 XEvent next_event; 490 XConfigureEvent *p, *q; 491 492 XtAppNextEvent(app_con, target); 493 p = (XConfigureEvent *) target; 494 495 TRACE(("pending Configure...?%s\n", XtAppPending(app_con) ? "yes" : "no")); 496 TraceConfigureEvent(target); 497 498 if (XtAppPending(app_con) 499 && XtAppPeekEvent(app_con, &next_event) 500 && next_event.type == ConfigureNotify) { 501 Boolean merge_this = False; 502 503 TraceConfigureEvent(&next_event); 504 XtAppNextEvent(app_con, &next_event); 505 q = (XConfigureEvent *) (&next_event); 506 507 if (p->window == q->window) { 508 TRACE(("pending Configure...merged\n")); 509 merge_this = True; 510 } 511 if (!merge_this) { 512 TRACE(("pending Configure...skipped\n")); 513 XtDispatchEvent(target); 514 } 515 *target = next_event; 516 } 517 XtDispatchEvent(target); 518 return XtAppPending(app_con); 519} 520 521/* 522 * Filter redundant Expose- and ConfigureNotify-events. This is limited to 523 * adjacent events because there could be other event-loop processing. Absent 524 * that limitation, it might be possible to scan ahead to find when the screen 525 * would be completely updated, skipping unnecessary re-repainting before that 526 * point. 527 * 528 * Note: all cases should allow doing XtAppNextEvent if result is true. 529 */ 530XtInputMask 531xtermAppPending(void) 532{ 533 XtInputMask result = XtAppPending(app_con); 534 XEvent this_event; 535 536 while (result && XtAppPeekEvent(app_con, &this_event)) { 537 if (this_event.type == Expose) { 538 result = mergeExposeEvents(&this_event); 539 TRACE(("got merged expose events\n")); 540 } else if (this_event.type == ConfigureNotify) { 541 result = mergeConfigureEvents(&this_event); 542 TRACE(("got merged configure notify events\n")); 543 } else { 544 TRACE(("pending %s\n", visibleEventType(this_event.type))); 545 break; 546 } 547 } 548 return result; 549} 550 551void 552xevents(void) 553{ 554 XtermWidget xw = term; 555 TScreen *screen = TScreenOf(xw); 556 XEvent event; 557 XtInputMask input_mask; 558 559 if (need_cleanup) 560 NormalExit(); 561 562 if (screen->scroll_amt) 563 FlushScroll(xw); 564 /* 565 * process timeouts, relying on the fact that XtAppProcessEvent 566 * will process the timeout and return without blockng on the 567 * XEvent queue. Other sources i.e., the pty are handled elsewhere 568 * with select(). 569 */ 570 while ((input_mask = xtermAppPending()) != 0) { 571 if (input_mask & XtIMTimer) 572 XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer); 573#if OPT_SESSION_MGT 574 /* 575 * Session management events are alternative input events. Deal with 576 * them in the same way. 577 */ 578 else if (input_mask & XtIMAlternateInput) 579 XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput); 580#endif 581 else 582 break; 583 } 584 585 /* 586 * If there's no XEvents, don't wait around... 587 */ 588 if ((input_mask & XtIMXEvent) != XtIMXEvent) 589 return; 590 do { 591 /* 592 * This check makes xterm hang when in mouse hilite tracking mode. 593 * We simply ignore all events except for those not passed down to 594 * this function, e.g., those handled in in_put(). 595 */ 596 if (screen->waitingForTrackInfo) { 597 Sleep(10); 598 return; 599 } 600 XtAppNextEvent(app_con, &event); 601 /* 602 * Hack to get around problems with the toolkit throwing away 603 * eventing during the exclusive grab of the menu popup. By 604 * looking at the event ourselves we make sure that we can 605 * do the right thing. 606 */ 607 if (OUR_EVENT(event, EnterNotify)) { 608 DoSpecialEnterNotify(xw, &event.xcrossing); 609 } else if (OUR_EVENT(event, LeaveNotify)) { 610 DoSpecialLeaveNotify(xw, &event.xcrossing); 611 } else if ((screen->send_mouse_pos == ANY_EVENT_MOUSE 612#if OPT_DEC_LOCATOR 613 || screen->send_mouse_pos == DEC_LOCATOR 614#endif /* OPT_DEC_LOCATOR */ 615 ) 616 && event.xany.type == MotionNotify 617 && event.xcrossing.window == XtWindow(xw)) { 618 SendMousePosition(xw, &event); 619 xtermShowPointer(xw, True); 620 continue; 621 } 622 623 /* 624 * If the event is interesting (and not a keyboard event), turn the 625 * mouse pointer back on. 626 */ 627 if (screen->hide_pointer) { 628 if (screen->pointer_mode >= pFocused) { 629 switch (event.xany.type) { 630 case MotionNotify: 631 xtermShowPointer(xw, True); 632 break; 633 } 634 } else { 635 switch (event.xany.type) { 636 case KeyPress: 637 case KeyRelease: 638 case ButtonPress: 639 case ButtonRelease: 640 /* also these... */ 641 case Expose: 642 case NoExpose: 643 case PropertyNotify: 644 case ClientMessage: 645 break; 646 default: 647 xtermShowPointer(xw, True); 648 break; 649 } 650 } 651 } 652 653 if (!event.xany.send_event || 654 screen->allowSendEvents || 655 ((event.xany.type != KeyPress) && 656 (event.xany.type != KeyRelease) && 657 (event.xany.type != ButtonPress) && 658 (event.xany.type != ButtonRelease))) { 659 660 XtDispatchEvent(&event); 661 } 662 } while (xtermAppPending() & XtIMXEvent); 663} 664 665static Cursor 666make_hidden_cursor(XtermWidget xw) 667{ 668 TScreen *screen = TScreenOf(xw); 669 Cursor c; 670 Display *dpy = screen->display; 671 XFontStruct *fn; 672 673 static XColor dummy; 674 675 /* 676 * Prefer nil2 (which is normally available) to "fixed" (which is supposed 677 * to be "always" available), since it's a smaller glyph in case the 678 * server insists on drawing _something_. 679 */ 680 TRACE(("Ask for nil2 font\n")); 681 if ((fn = XLoadQueryFont(dpy, "nil2")) == 0) { 682 TRACE(("...Ask for fixed font\n")); 683 fn = XLoadQueryFont(dpy, DEFFONT); 684 } 685 686 if (fn != 0) { 687 /* a space character seems to work as a cursor (dots are not needed) */ 688 c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy); 689 XFreeFont(dpy, fn); 690 } else { 691 c = 0; 692 } 693 TRACE(("XCreateGlyphCursor ->%#lx\n", c)); 694 return (c); 695} 696 697/* 698 * Xlib uses Xcursor to customize cursor coloring, which interferes with 699 * xterm's pointerColor resource. Work around this by providing our own 700 * default theme. Testing seems to show that we only have to provide this 701 * until the window is initialized. 702 */ 703void 704init_colored_cursor(void) 705{ 706#ifdef HAVE_LIB_XCURSOR 707 const char *theme = "index.theme"; 708 const char *pattern = "xtermXXXXXX"; 709 const char *tmp_dir; 710 char *filename; 711 char *env = getenv("XCURSOR_THEME"); 712 size_t needed; 713 FILE *fp; 714 715 xterm_cursor_theme = 0; 716 if (IsEmpty(env)) { 717 if ((tmp_dir = getenv("TMPDIR")) == 0) { 718 tmp_dir = P_tmpdir; 719 } 720 needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern); 721 if ((filename = malloc(needed)) != 0) { 722 sprintf(filename, "%s/%s", tmp_dir, pattern); 723 724#ifdef HAVE_MKDTEMP 725 xterm_cursor_theme = mkdtemp(filename); 726#else 727 if (mktemp(filename) != 0 728 && mkdir(filename, 0700) == 0) { 729 xterm_cursor_theme = filename; 730 } 731#endif 732 /* 733 * Actually, Xcursor does what _we_ want just by steering its 734 * search path away from home. We are setting up the complete 735 * theme just in case the library ever acquires a maintainer. 736 */ 737 if (xterm_cursor_theme != 0) { 738 char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme); 739 strcat(leaf, "/"); 740 strcat(leaf, theme); 741 if ((fp = fopen(xterm_cursor_theme, "w")) != 0) { 742 fprintf(fp, "[Icon Theme]\n"); 743 fclose(fp); 744 *leaf = '\0'; 745 xtermSetenv("XCURSOR_PATH", xterm_cursor_theme); 746 *leaf = '/'; 747 } 748 atexit(cleanup_colored_cursor); 749 } 750 } 751 } 752#endif /* HAVE_LIB_XCURSOR */ 753} 754 755/* 756 * Once done, discard the file and directory holding it. 757 */ 758void 759cleanup_colored_cursor(void) 760{ 761#ifdef HAVE_LIB_XCURSOR 762 if (xterm_cursor_theme != 0) { 763 char *my_path = getenv("XCURSOR_PATH"); 764 struct stat sb; 765 if (!IsEmpty(my_path) 766 && stat(my_path, &sb) == 0 767 && (sb.st_mode & S_IFMT) == S_IFDIR) { 768 unlink(xterm_cursor_theme); 769 rmdir(my_path); 770 free(xterm_cursor_theme); 771 xterm_cursor_theme = 0; 772 } 773 } 774#endif /* HAVE_LIB_XCURSOR */ 775} 776 777Cursor 778make_colored_cursor(unsigned cursorindex, /* index into font */ 779 unsigned long fg, /* pixel value */ 780 unsigned long bg) /* pixel value */ 781{ 782 TScreen *screen = TScreenOf(term); 783 Cursor c; 784 Display *dpy = screen->display; 785 786 c = XCreateFontCursor(dpy, cursorindex); 787 if (c != None) { 788 recolor_cursor(screen, c, fg, bg); 789 } 790 return (c); 791} 792 793/* ARGSUSED */ 794void 795HandleKeyPressed(Widget w GCC_UNUSED, 796 XEvent *event, 797 String *params GCC_UNUSED, 798 Cardinal *nparams GCC_UNUSED) 799{ 800 TRACE(("Handle insert-seven-bit for %p\n", (void *) w)); 801 Input(term, &event->xkey, False); 802} 803 804/* ARGSUSED */ 805void 806HandleEightBitKeyPressed(Widget w GCC_UNUSED, 807 XEvent *event, 808 String *params GCC_UNUSED, 809 Cardinal *nparams GCC_UNUSED) 810{ 811 TRACE(("Handle insert-eight-bit for %p\n", (void *) w)); 812 Input(term, &event->xkey, True); 813} 814 815/* ARGSUSED */ 816void 817HandleStringEvent(Widget w GCC_UNUSED, 818 XEvent *event GCC_UNUSED, 819 String *params, 820 Cardinal *nparams) 821{ 822 823 if (*nparams != 1) 824 return; 825 826 if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') { 827 const char *abcdef = "ABCDEF"; 828 const char *xxxxxx; 829 Char c; 830 UString p; 831 unsigned value = 0; 832 833 for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) != 834 '\0'; p++) { 835 value *= 16; 836 if (c >= '0' && c <= '9') 837 value += (unsigned) (c - '0'); 838 else if ((xxxxxx = (strchr) (abcdef, c)) != 0) 839 value += (unsigned) (xxxxxx - abcdef) + 10; 840 else 841 break; 842 } 843 if (c == '\0') { 844 Char hexval[2]; 845 hexval[0] = (Char) value; 846 hexval[1] = 0; 847 StringInput(term, hexval, (size_t) 1); 848 } 849 } else { 850 StringInput(term, (const Char *) *params, strlen(*params)); 851 } 852} 853 854#if OPT_EXEC_XTERM 855 856#ifndef PROCFS_ROOT 857#define PROCFS_ROOT "/proc" 858#endif 859 860/* ARGSUSED */ 861void 862HandleSpawnTerminal(Widget w GCC_UNUSED, 863 XEvent *event GCC_UNUSED, 864 String *params, 865 Cardinal *nparams) 866{ 867 TScreen *screen = TScreenOf(term); 868 char *child_cwd = NULL; 869 char *child_exe; 870 pid_t pid; 871 872 /* 873 * Try to find the actual program which is running in the child process. 874 * This works for Linux. If we cannot find the program, fall back to the 875 * xterm program (which is usually adequate). Give up if we are given only 876 * a relative path to xterm, since that would not always match $PATH. 877 */ 878 child_exe = Readlink(PROCFS_ROOT "/self/exe"); 879 if (!child_exe) { 880 if (strncmp(ProgramName, "./", (size_t) 2) 881 && strncmp(ProgramName, "../", (size_t) 3)) { 882 child_exe = xtermFindShell(ProgramName, True); 883 } else { 884 xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName); 885 } 886 if (child_exe == 0) 887 return; 888 } 889 890 /* 891 * Determine the current working directory of the child so that we can 892 * spawn a new terminal in the same directory. 893 * 894 * If we cannot get the CWD of the child, just use our own. 895 */ 896 if (screen->pid) { 897 char child_cwd_link[sizeof(PROCFS_ROOT) + 80]; 898 sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) screen->pid); 899 child_cwd = Readlink(child_cwd_link); 900 } 901 902 /* The reaper will take care of cleaning up the child */ 903 pid = fork(); 904 if (pid == -1) { 905 xtermWarning("Could not fork: %s\n", SysErrorMsg(errno)); 906 } else if (!pid) { 907 /* We are the child */ 908 if (child_cwd) { 909 IGNORE_RC(chdir(child_cwd)); /* We don't care if this fails */ 910 } 911 912 if (setuid(screen->uid) == -1 913 || setgid(screen->gid) == -1) { 914 xtermWarning("Cannot reset uid/gid\n"); 915 } else { 916 unsigned myargc = *nparams + 1; 917 char **myargv = TypeMallocN(char *, myargc + 1); 918 unsigned n = 0; 919 920 myargv[n++] = child_exe; 921 922 while (n < myargc) { 923 myargv[n++] = (char *) *params++; 924 } 925 926 myargv[n] = 0; 927 execv(child_exe, myargv); 928 929 /* If we get here, we've failed */ 930 xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno)); 931 } 932 _exit(0); 933 } 934 935 /* We are the parent; clean up */ 936 if (child_cwd) 937 free(child_cwd); 938 free(child_exe); 939} 940#endif /* OPT_EXEC_XTERM */ 941 942/* 943 * Rather than sending characters to the host, put them directly into our 944 * input queue. That lets a user have access to any of the control sequences 945 * for a key binding. This is the equivalent of local function key support. 946 * 947 * NOTE: This code does not support the hexadecimal kludge used in 948 * HandleStringEvent because it prevents us from sending an arbitrary string 949 * (but it appears in a lot of examples - so we are stuck with it). The 950 * standard string converter does recognize "\" for newline ("\n") and for 951 * octal constants (e.g., "\007" for BEL). So we assume the user can make do 952 * without a specialized converter. (Don't try to use \000, though). 953 */ 954/* ARGSUSED */ 955void 956HandleInterpret(Widget w GCC_UNUSED, 957 XEvent *event GCC_UNUSED, 958 String *params, 959 Cardinal *param_count) 960{ 961 if (*param_count == 1) { 962 const char *value = params[0]; 963 int need = (int) strlen(value); 964 int used = (int) (VTbuffer->next - VTbuffer->buffer); 965 int have = (int) (VTbuffer->last - VTbuffer->buffer); 966 967 if (have - used + need < BUF_SIZE) { 968 969 fillPtyData(term, VTbuffer, value, (int) strlen(value)); 970 971 TRACE(("Interpret %s\n", value)); 972 VTbuffer->update++; 973 } 974 } 975} 976 977/*ARGSUSED*/ 978void 979HandleEnterWindow(Widget w GCC_UNUSED, 980 XtPointer eventdata GCC_UNUSED, 981 XEvent *event GCC_UNUSED, 982 Boolean *cont GCC_UNUSED) 983{ 984 /* NOP since we handled it above */ 985 TRACE(("HandleEnterWindow ignored\n")); 986 TRACE_FOCUS(w, event); 987} 988 989/*ARGSUSED*/ 990void 991HandleLeaveWindow(Widget w GCC_UNUSED, 992 XtPointer eventdata GCC_UNUSED, 993 XEvent *event GCC_UNUSED, 994 Boolean *cont GCC_UNUSED) 995{ 996 /* NOP since we handled it above */ 997 TRACE(("HandleLeaveWindow ignored\n")); 998 TRACE_FOCUS(w, event); 999} 1000 1001/*ARGSUSED*/ 1002void 1003HandleFocusChange(Widget w GCC_UNUSED, 1004 XtPointer eventdata GCC_UNUSED, 1005 XEvent *ev, 1006 Boolean *cont GCC_UNUSED) 1007{ 1008 XFocusChangeEvent *event = (XFocusChangeEvent *) ev; 1009 XtermWidget xw = term; 1010 TScreen *screen = TScreenOf(xw); 1011 1012 TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n", 1013 visibleEventType(event->type), 1014 visibleNotifyMode(event->mode), 1015 visibleNotifyDetail(event->detail))); 1016 TRACE_FOCUS(xw, event); 1017 1018 if (screen->quiet_grab 1019 && (event->mode == NotifyGrab || event->mode == NotifyUngrab)) { 1020 /* EMPTY */ ; 1021 } else if ((event->type == FocusIn || event->type == FocusOut) 1022 && event->detail == NotifyPointer) { 1023 /* 1024 * NotifyPointer is sent to the window where the pointer is, and is 1025 * in addition to events sent to the old/new focus-windows. 1026 */ 1027 /* EMPTY */ ; 1028 } else if (event->type == FocusIn) { 1029 setXUrgency(xw, False); 1030 1031 /* 1032 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in 1033 * one of our windows. Use this to reset a case where one xterm is 1034 * partly obscuring another, and X gets (us) confused about whether the 1035 * pointer was in the window. In particular, this can happen if the 1036 * user is resizing the obscuring window, causing some events to not be 1037 * delivered to the obscured window. 1038 */ 1039 if (event->detail == NotifyNonlinear 1040 && (screen->select & INWINDOW) != 0) { 1041 unselectwindow(xw, INWINDOW); 1042 } 1043 selectwindow(xw, 1044 ((event->detail == NotifyPointer) 1045 ? INWINDOW 1046 : FOCUS)); 1047 SendFocusButton(xw, event); 1048 } else { 1049#if OPT_FOCUS_EVENT 1050 if (event->type == FocusOut) { 1051 SendFocusButton(xw, event); 1052 } 1053#endif 1054 /* 1055 * XGrabKeyboard() will generate NotifyGrab event that we want to 1056 * ignore. 1057 */ 1058 if (event->mode != NotifyGrab) { 1059 unselectwindow(xw, 1060 ((event->detail == NotifyPointer) 1061 ? INWINDOW 1062 : FOCUS)); 1063 } 1064 if (screen->grabbedKbd && (event->mode == NotifyUngrab)) { 1065 Bell(xw, XkbBI_Info, 100); 1066 ReverseVideo(xw); 1067 screen->grabbedKbd = False; 1068 update_securekbd(); 1069 } 1070 } 1071} 1072 1073static long lastBellTime; /* in milliseconds */ 1074 1075#if defined(HAVE_XKB_BELL_EXT) 1076static Atom 1077AtomBell(XtermWidget xw, int which) 1078{ 1079#define DATA(name) { XkbBI_##name, XkbBN_##name } 1080 static struct { 1081 int value; 1082 const char *name; 1083 } table[] = { 1084 DATA(Info), 1085 DATA(MarginBell), 1086 DATA(MinorError), 1087 DATA(TerminalBell) 1088 }; 1089 Cardinal n; 1090 Atom result = None; 1091 1092 for (n = 0; n < XtNumber(table); ++n) { 1093 if (table[n].value == which) { 1094 result = XInternAtom(XtDisplay(xw), table[n].name, False); 1095 break; 1096 } 1097 } 1098 return result; 1099} 1100#endif 1101 1102void 1103xtermBell(XtermWidget xw, int which, int percent) 1104{ 1105 TScreen *screen = TScreenOf(xw); 1106#if defined(HAVE_XKB_BELL_EXT) 1107 Atom tony = AtomBell(xw, which); 1108#endif 1109 1110 switch (which) { 1111 case XkbBI_Info: 1112 case XkbBI_MinorError: 1113 case XkbBI_MajorError: 1114 case XkbBI_TerminalBell: 1115 switch (screen->warningVolume) { 1116 case bvOff: 1117 percent = -100; 1118 break; 1119 case bvLow: 1120 break; 1121 case bvHigh: 1122 percent = 100; 1123 break; 1124 } 1125 break; 1126 case XkbBI_MarginBell: 1127 switch (screen->marginVolume) { 1128 case bvOff: 1129 percent = -100; 1130 break; 1131 case bvLow: 1132 break; 1133 case bvHigh: 1134 percent = 100; 1135 break; 1136 } 1137 break; 1138 default: 1139 break; 1140 } 1141 1142#if defined(HAVE_XKB_BELL_EXT) 1143 if (tony != None) { 1144 XkbBell(screen->display, VShellWindow(xw), percent, tony); 1145 } else 1146#endif 1147 XBell(screen->display, percent); 1148} 1149 1150void 1151Bell(XtermWidget xw, int which, int percent) 1152{ 1153 TScreen *screen = TScreenOf(xw); 1154 struct timeval curtime; 1155 long now_msecs; 1156 1157 TRACE(("BELL %d %d%%\n", which, percent)); 1158 if (!XtIsRealized((Widget) xw)) { 1159 return; 1160 } 1161 1162 setXUrgency(xw, True); 1163 1164 /* has enough time gone by that we are allowed to ring 1165 the bell again? */ 1166 if (screen->bellSuppressTime) { 1167 if (screen->bellInProgress) { 1168 do_xevents(); 1169 if (screen->bellInProgress) { /* even after new events? */ 1170 return; 1171 } 1172 } 1173 X_GETTIMEOFDAY(&curtime); 1174 now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000; 1175 if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 && 1176 now_msecs - lastBellTime < screen->bellSuppressTime) { 1177 return; 1178 } 1179 lastBellTime = now_msecs; 1180 } 1181 1182 if (screen->visualbell) { 1183 VisualBell(); 1184 } else { 1185 xtermBell(xw, which, percent); 1186 } 1187 1188 if (screen->poponbell) 1189 XRaiseWindow(screen->display, VShellWindow(xw)); 1190 1191 if (screen->bellSuppressTime) { 1192 /* now we change a property and wait for the notify event to come 1193 back. If the server is suspending operations while the bell 1194 is being emitted (problematic for audio bell), this lets us 1195 know when the previous bell has finished */ 1196 Widget w = CURRENT_EMU(); 1197 XChangeProperty(XtDisplay(w), XtWindow(w), 1198 XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0); 1199 screen->bellInProgress = True; 1200 } 1201} 1202 1203#define VB_DELAY screen->visualBellDelay 1204 1205static void 1206flashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height) 1207{ 1208 int y = 0; 1209 int x = 0; 1210 1211 if (screen->flash_line) { 1212 y = CursorY(screen, screen->cur_row); 1213 height = (unsigned) FontHeight(screen); 1214 } 1215 XFillRectangle(screen->display, window, visualGC, x, y, width, height); 1216 XFlush(screen->display); 1217 Sleep(VB_DELAY); 1218 XFillRectangle(screen->display, window, visualGC, x, y, width, height); 1219} 1220 1221void 1222VisualBell(void) 1223{ 1224 TScreen *screen = TScreenOf(term); 1225 1226 if (VB_DELAY > 0) { 1227 Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^ 1228 T_COLOR(screen, TEXT_BG)); 1229 XGCValues gcval; 1230 GC visualGC; 1231 1232 gcval.function = GXxor; 1233 gcval.foreground = xorPixel; 1234 visualGC = XtGetGC((Widget) term, GCFunction + GCForeground, &gcval); 1235#if OPT_TEK4014 1236 if (TEK4014_ACTIVE(term)) { 1237 TekScreen *tekscr = TekScreenOf(tekWidget); 1238 flashWindow(screen, TWindow(tekscr), visualGC, 1239 TFullWidth(tekscr), 1240 TFullHeight(tekscr)); 1241 } else 1242#endif 1243 { 1244 flashWindow(screen, VWindow(screen), visualGC, 1245 FullWidth(screen), 1246 FullHeight(screen)); 1247 } 1248 XtReleaseGC((Widget) term, visualGC); 1249 } 1250} 1251 1252/* ARGSUSED */ 1253void 1254HandleBellPropertyChange(Widget w GCC_UNUSED, 1255 XtPointer data GCC_UNUSED, 1256 XEvent *ev, 1257 Boolean *more GCC_UNUSED) 1258{ 1259 TScreen *screen = TScreenOf(term); 1260 1261 if (ev->xproperty.atom == XA_NOTICE) { 1262 screen->bellInProgress = False; 1263 } 1264} 1265 1266void 1267xtermWarning(const char *fmt,...) 1268{ 1269 int save_err = errno; 1270 va_list ap; 1271 1272 TRACE(("xtermWarning fmt='%s'\n", fmt)); 1273 fprintf(stderr, "%s: ", ProgramName); 1274 va_start(ap, fmt); 1275 vfprintf(stderr, fmt, ap); 1276 (void) fflush(stderr); 1277 1278 va_end(ap); 1279 errno = save_err; 1280} 1281 1282void 1283xtermPerror(const char *fmt,...) 1284{ 1285 int save_err = errno; 1286 char *msg = strerror(errno); 1287 va_list ap; 1288 1289 TRACE(("xtermPerror fmt='%s', msg='%s'\n", fmt, NonNull(msg))); 1290 fprintf(stderr, "%s: ", ProgramName); 1291 va_start(ap, fmt); 1292 vfprintf(stderr, fmt, ap); 1293 fprintf(stderr, ": %s\n", msg); 1294 (void) fflush(stderr); 1295 1296 va_end(ap); 1297 errno = save_err; 1298} 1299 1300Window 1301WMFrameWindow(XtermWidget xw) 1302{ 1303 Window win_root, win_current, *children; 1304 Window win_parent = 0; 1305 unsigned int nchildren; 1306 1307 win_current = XtWindow(xw); 1308 1309 /* find the parent which is child of root */ 1310 do { 1311 if (win_parent) 1312 win_current = win_parent; 1313 XQueryTree(TScreenOf(xw)->display, 1314 win_current, 1315 &win_root, 1316 &win_parent, 1317 &children, 1318 &nchildren); 1319 XFree(children); 1320 } while (win_root != win_parent); 1321 1322 return win_current; 1323} 1324 1325#if OPT_DABBREV 1326/* 1327 * The following code implements `dynamic abbreviation' expansion a la 1328 * Emacs. It looks in the preceding visible screen and its scrollback 1329 * to find expansions of a typed word. It compares consecutive 1330 * expansions and ignores one of them if they are identical. 1331 * (Tomasz J. Cholewo, t.cholewo@ieee.org) 1332 */ 1333 1334#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0') 1335 1336static int 1337dabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld) 1338{ 1339 int result = -1; 1340 int firstLine = -(screen->savedlines); 1341 1342 *ld = getLineData(screen, cell->row); 1343 while (cell->row >= firstLine) { 1344 if (--(cell->col) >= 0) { 1345 result = (int) (*ld)->charData[cell->col]; 1346 break; 1347 } 1348 if (--(cell->row) < firstLine) 1349 break; /* ...there is no previous line */ 1350 *ld = getLineData(screen, cell->row); 1351 cell->col = MaxCols(screen); 1352 if (!LineTstWrapped(*ld)) { 1353 result = ' '; /* treat lines as separate */ 1354 break; 1355 } 1356 } 1357 return result; 1358} 1359 1360static char * 1361dabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld) 1362{ 1363 TScreen *screen = TScreenOf(xw); 1364 char *abword; 1365 int c; 1366 char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1); 1367 char *result = 0; 1368 1369 abword = ab_end; 1370 *abword = '\0'; /* end of string marker */ 1371 1372 while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 && 1373 IS_WORD_CONSTITUENT(c)) { 1374 if (abword > xw->work.dabbrev_data) /* store only the last chars */ 1375 *(--abword) = (char) c; 1376 } 1377 1378 if (c >= 0) { 1379 result = abword; 1380 } else if (abword != ab_end) { 1381 result = abword; 1382 } 1383 1384 if (result != 0) { 1385 while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 && 1386 !IS_WORD_CONSTITUENT(c)) { 1387 ; /* skip preceding spaces */ 1388 } 1389 (cell->col)++; /* can be | > screen->max_col| */ 1390 } 1391 return result; 1392} 1393 1394static int 1395dabbrev_expand(XtermWidget xw) 1396{ 1397 TScreen *screen = TScreenOf(xw); 1398 int pty = screen->respond; /* file descriptor of pty */ 1399 1400 static CELL cell; 1401 static char *dabbrev_hint = 0, *lastexpansion = 0; 1402 static unsigned int expansions; 1403 1404 char *expansion; 1405 Char *copybuffer; 1406 size_t hint_len; 1407 size_t del_cnt; 1408 size_t buf_cnt; 1409 int result = 0; 1410 LineData *ld; 1411 1412 if (!screen->dabbrev_working) { /* initialize */ 1413 expansions = 0; 1414 cell.col = screen->cur_col; 1415 cell.row = screen->cur_row; 1416 1417 if (dabbrev_hint != 0) 1418 free(dabbrev_hint); 1419 1420 if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) { 1421 1422 if (lastexpansion != 0) 1423 free(lastexpansion); 1424 1425 if ((lastexpansion = strdup(dabbrev_hint)) != 0) { 1426 1427 /* make own copy */ 1428 if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) { 1429 screen->dabbrev_working = True; 1430 /* we are in the middle of dabbrev process */ 1431 } 1432 } else { 1433 return result; 1434 } 1435 } else { 1436 return result; 1437 } 1438 if (!screen->dabbrev_working) { 1439 if (lastexpansion != 0) { 1440 free(lastexpansion); 1441 lastexpansion = 0; 1442 } 1443 return result; 1444 } 1445 } 1446 1447 if (dabbrev_hint == 0) 1448 return result; 1449 1450 hint_len = strlen(dabbrev_hint); 1451 for (;;) { 1452 if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) { 1453 if (expansions >= 2) { 1454 expansions = 0; 1455 cell.col = screen->cur_col; 1456 cell.row = screen->cur_row; 1457 continue; 1458 } 1459 break; 1460 } 1461 if (!strncmp(dabbrev_hint, expansion, hint_len) && /* empty hint matches everything */ 1462 strlen(expansion) > hint_len && /* trivial expansion disallowed */ 1463 strcmp(expansion, lastexpansion)) /* different from previous */ 1464 break; 1465 } 1466 1467 if (expansion != 0) { 1468 del_cnt = strlen(lastexpansion) - hint_len; 1469 buf_cnt = del_cnt + strlen(expansion) - hint_len; 1470 1471 if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) { 1472 /* delete previous expansion */ 1473 memset(copybuffer, screen->dabbrev_erase_char, del_cnt); 1474 memmove(copybuffer + del_cnt, 1475 expansion + hint_len, 1476 strlen(expansion) - hint_len); 1477 v_write(pty, copybuffer, (unsigned) buf_cnt); 1478 /* v_write() just reset our flag */ 1479 screen->dabbrev_working = True; 1480 free(copybuffer); 1481 1482 free(lastexpansion); 1483 1484 if ((lastexpansion = strdup(expansion)) != 0) { 1485 result = 1; 1486 expansions++; 1487 } 1488 } 1489 } 1490 1491 return result; 1492} 1493 1494/*ARGSUSED*/ 1495void 1496HandleDabbrevExpand(Widget w, 1497 XEvent *event GCC_UNUSED, 1498 String *params GCC_UNUSED, 1499 Cardinal *nparams GCC_UNUSED) 1500{ 1501 XtermWidget xw; 1502 1503 TRACE(("Handle dabbrev-expand for %p\n", (void *) w)); 1504 if ((xw = getXtermWidget(w)) != 0) { 1505 if (!dabbrev_expand(xw)) 1506 Bell(xw, XkbBI_TerminalBell, 0); 1507 } 1508} 1509#endif /* OPT_DABBREV */ 1510 1511#if OPT_MAXIMIZE 1512/*ARGSUSED*/ 1513void 1514HandleDeIconify(Widget w, 1515 XEvent *event GCC_UNUSED, 1516 String *params GCC_UNUSED, 1517 Cardinal *nparams GCC_UNUSED) 1518{ 1519 XtermWidget xw; 1520 1521 if ((xw = getXtermWidget(w)) != 0) { 1522 TScreen *screen = TScreenOf(xw); 1523 XMapWindow(screen->display, VShellWindow(xw)); 1524 } 1525} 1526 1527/*ARGSUSED*/ 1528void 1529HandleIconify(Widget w, 1530 XEvent *event GCC_UNUSED, 1531 String *params GCC_UNUSED, 1532 Cardinal *nparams GCC_UNUSED) 1533{ 1534 XtermWidget xw; 1535 1536 if ((xw = getXtermWidget(w)) != 0) { 1537 TScreen *screen = TScreenOf(xw); 1538 XIconifyWindow(screen->display, 1539 VShellWindow(xw), 1540 DefaultScreen(screen->display)); 1541 } 1542} 1543 1544int 1545QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height) 1546{ 1547 TScreen *screen = TScreenOf(xw); 1548 XSizeHints hints; 1549 long supp = 0; 1550 Window root_win; 1551 int root_x = -1; /* saved co-ordinates */ 1552 int root_y = -1; 1553 unsigned root_border; 1554 unsigned root_depth; 1555 int code; 1556 1557 if (XGetGeometry(screen->display, 1558 RootWindowOfScreen(XtScreen(xw)), 1559 &root_win, 1560 &root_x, 1561 &root_y, 1562 width, 1563 height, 1564 &root_border, 1565 &root_depth)) { 1566 TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n", 1567 root_x, 1568 root_y, 1569 *width, 1570 *height, 1571 root_border)); 1572 1573 *width -= (root_border * 2); 1574 *height -= (root_border * 2); 1575 1576 hints.flags = PMaxSize; 1577 if (XGetWMNormalHints(screen->display, 1578 VShellWindow(xw), 1579 &hints, 1580 &supp) 1581 && (hints.flags & PMaxSize) != 0) { 1582 1583 TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n", 1584 hints.max_width, 1585 hints.max_height)); 1586 1587 if ((unsigned) hints.max_width < *width) 1588 *width = (unsigned) hints.max_width; 1589 if ((unsigned) hints.max_height < *height) 1590 *height = (unsigned) hints.max_height; 1591 } 1592 code = 1; 1593 } else { 1594 *width = 0; 1595 *height = 0; 1596 code = 0; 1597 } 1598 return code; 1599} 1600 1601void 1602RequestMaximize(XtermWidget xw, int maximize) 1603{ 1604 TScreen *screen = TScreenOf(xw); 1605 XWindowAttributes wm_attrs, vshell_attrs; 1606 unsigned root_width, root_height; 1607 Boolean success = False; 1608 1609 TRACE(("RequestMaximize %d:%s\n", 1610 maximize, 1611 (maximize 1612 ? "maximize" 1613 : "restore"))); 1614 1615 /* 1616 * Before any maximize, ensure that we can capture the current screensize 1617 * as well as the estimated root-window size. 1618 */ 1619 if (maximize 1620 && QueryMaximize(xw, &root_width, &root_height) 1621 && xtermGetWinAttrs(screen->display, 1622 WMFrameWindow(xw), 1623 &wm_attrs) 1624 && xtermGetWinAttrs(screen->display, 1625 VShellWindow(xw), 1626 &vshell_attrs)) { 1627 1628 if (screen->restore_data != True 1629 || screen->restore_width != root_width 1630 || screen->restore_height != root_height) { 1631 screen->restore_data = True; 1632 screen->restore_x = wm_attrs.x + wm_attrs.border_width; 1633 screen->restore_y = wm_attrs.y + wm_attrs.border_width; 1634 screen->restore_width = (unsigned) vshell_attrs.width; 1635 screen->restore_height = (unsigned) vshell_attrs.height; 1636 TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n", 1637 screen->restore_x, 1638 screen->restore_y, 1639 screen->restore_width, 1640 screen->restore_height)); 1641 } 1642 1643 /* subtract wm decoration dimensions */ 1644 root_width -= (unsigned) ((wm_attrs.width - vshell_attrs.width) 1645 + (wm_attrs.border_width * 2)); 1646 root_height -= (unsigned) ((wm_attrs.height - vshell_attrs.height) 1647 + (wm_attrs.border_width * 2)); 1648 success = True; 1649 } else if (screen->restore_data) { 1650 success = True; 1651 maximize = 0; 1652 } 1653 1654 if (success) { 1655 switch (maximize) { 1656 case 3: 1657 FullScreen(xw, 3); /* depends on EWMH */ 1658 break; 1659 case 2: 1660 FullScreen(xw, 2); /* depends on EWMH */ 1661 break; 1662 case 1: 1663 FullScreen(xw, 0); /* overrides any EWMH hint */ 1664 XMoveResizeWindow(screen->display, VShellWindow(xw), 1665 0 + wm_attrs.border_width, /* x */ 1666 0 + wm_attrs.border_width, /* y */ 1667 root_width, 1668 root_height); 1669 break; 1670 1671 default: 1672 FullScreen(xw, 0); /* reset any EWMH hint */ 1673 if (screen->restore_data) { 1674 screen->restore_data = False; 1675 1676 TRACE(("HandleRestoreSize: position %d,%d size %d,%d\n", 1677 screen->restore_x, 1678 screen->restore_y, 1679 screen->restore_width, 1680 screen->restore_height)); 1681 1682 XMoveResizeWindow(screen->display, 1683 VShellWindow(xw), 1684 screen->restore_x, 1685 screen->restore_y, 1686 screen->restore_width, 1687 screen->restore_height); 1688 } 1689 break; 1690 } 1691 } 1692} 1693 1694/*ARGSUSED*/ 1695void 1696HandleMaximize(Widget w, 1697 XEvent *event GCC_UNUSED, 1698 String *params GCC_UNUSED, 1699 Cardinal *nparams GCC_UNUSED) 1700{ 1701 XtermWidget xw; 1702 1703 if ((xw = getXtermWidget(w)) != 0) { 1704 RequestMaximize(xw, 1); 1705 } 1706} 1707 1708/*ARGSUSED*/ 1709void 1710HandleRestoreSize(Widget w, 1711 XEvent *event GCC_UNUSED, 1712 String *params GCC_UNUSED, 1713 Cardinal *nparams GCC_UNUSED) 1714{ 1715 XtermWidget xw; 1716 1717 if ((xw = getXtermWidget(w)) != 0) { 1718 RequestMaximize(xw, 0); 1719 } 1720} 1721#endif /* OPT_MAXIMIZE */ 1722 1723void 1724Redraw(void) 1725{ 1726 TScreen *screen = TScreenOf(term); 1727 XExposeEvent event; 1728 1729 TRACE(("Redraw\n")); 1730 1731 event.type = Expose; 1732 event.display = screen->display; 1733 event.x = 0; 1734 event.y = 0; 1735 event.count = 0; 1736 1737 if (VWindow(screen)) { 1738 event.window = VWindow(screen); 1739 event.width = term->core.width; 1740 event.height = term->core.height; 1741 (*term->core.widget_class->core_class.expose) ((Widget) term, 1742 (XEvent *) &event, 1743 NULL); 1744 if (ScrollbarWidth(screen)) { 1745 (screen->scrollWidget->core.widget_class->core_class.expose) 1746 (screen->scrollWidget, (XEvent *) &event, NULL); 1747 } 1748 } 1749#if OPT_TEK4014 1750 if (TEK4014_SHOWN(term)) { 1751 TekScreen *tekscr = TekScreenOf(tekWidget); 1752 event.window = TWindow(tekscr); 1753 event.width = tekWidget->core.width; 1754 event.height = tekWidget->core.height; 1755 TekExpose((Widget) tekWidget, (XEvent *) &event, NULL); 1756 } 1757#endif 1758} 1759 1760#ifdef VMS 1761#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d" 1762#else 1763#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d" 1764#endif 1765 1766void 1767timestamp_filename(char *dst, const char *src) 1768{ 1769 time_t tstamp; 1770 struct tm *tstruct; 1771 1772 tstamp = time((time_t *) 0); 1773 tstruct = localtime(&tstamp); 1774 sprintf(dst, TIMESTAMP_FMT, 1775 src, 1776 (int) tstruct->tm_year + 1900, 1777 tstruct->tm_mon + 1, 1778 tstruct->tm_mday, 1779 tstruct->tm_hour, 1780 tstruct->tm_min, 1781 tstruct->tm_sec); 1782} 1783 1784int 1785open_userfile(uid_t uid, gid_t gid, char *path, Bool append) 1786{ 1787 int fd; 1788 struct stat sb; 1789 1790#ifdef VMS 1791 if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { 1792 int the_error = errno; 1793 xtermWarning("cannot open %s: %d:%s\n", 1794 path, 1795 the_error, 1796 SysErrorMsg(the_error)); 1797 return -1; 1798 } 1799 chown(path, uid, gid); 1800#else 1801 if ((access(path, F_OK) != 0 && (errno != ENOENT)) 1802 || (creat_as(uid, gid, append, path, 0644) <= 0) 1803 || ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) { 1804 int the_error = errno; 1805 xtermWarning("cannot open %s: %d:%s\n", 1806 path, 1807 the_error, 1808 SysErrorMsg(the_error)); 1809 return -1; 1810 } 1811#endif 1812 1813 /* 1814 * Doublecheck that the user really owns the file that we've opened before 1815 * we do any damage, and that it is not world-writable. 1816 */ 1817 if (fstat(fd, &sb) < 0 1818 || sb.st_uid != uid 1819 || (sb.st_mode & 022) != 0) { 1820 xtermWarning("you do not own %s\n", path); 1821 close(fd); 1822 return -1; 1823 } 1824 return fd; 1825} 1826 1827#ifndef VMS 1828/* 1829 * Create a file only if we could with the permissions of the real user id. 1830 * We could emulate this with careful use of access() and following 1831 * symbolic links, but that is messy and has race conditions. 1832 * Forking is messy, too, but we can't count on setreuid() or saved set-uids 1833 * being available. 1834 * 1835 * Note: When called for user logging, we have ensured that the real and 1836 * effective user ids are the same, so this remains as a convenience function 1837 * for the debug logs. 1838 * 1839 * Returns 1840 * 1 if we can proceed to open the file in relative safety, 1841 * -1 on error, e.g., cannot fork 1842 * 0 otherwise. 1843 */ 1844int 1845creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode) 1846{ 1847 int fd; 1848 pid_t pid; 1849 int retval = 0; 1850 int childstat = 0; 1851#ifndef HAVE_WAITPID 1852 int waited; 1853 void (*chldfunc) (int); 1854 1855 chldfunc = signal(SIGCHLD, SIG_DFL); 1856#endif /* HAVE_WAITPID */ 1857 1858 TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n", 1859 (int) uid, (int) geteuid(), 1860 (int) gid, (int) getegid(), 1861 append, 1862 pathname, 1863 mode)); 1864 1865 if (uid == geteuid() && gid == getegid()) { 1866 fd = open(pathname, 1867 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 1868 mode); 1869 if (fd >= 0) 1870 close(fd); 1871 return (fd >= 0); 1872 } 1873 1874 pid = fork(); 1875 switch (pid) { 1876 case 0: /* child */ 1877 if (setgid(gid) == -1 1878 || setuid(uid) == -1) { 1879 /* we cannot report an error here via stderr, just quit */ 1880 retval = 1; 1881 } else { 1882 fd = open(pathname, 1883 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 1884 mode); 1885 if (fd >= 0) { 1886 close(fd); 1887 retval = 0; 1888 } else { 1889 retval = 1; 1890 } 1891 } 1892 _exit(retval); 1893 /* NOTREACHED */ 1894 case -1: /* error */ 1895 return retval; 1896 default: /* parent */ 1897#ifdef HAVE_WAITPID 1898 while (waitpid(pid, &childstat, 0) < 0) { 1899#ifdef EINTR 1900 if (errno == EINTR) 1901 continue; 1902#endif /* EINTR */ 1903#ifdef ERESTARTSYS 1904 if (errno == ERESTARTSYS) 1905 continue; 1906#endif /* ERESTARTSYS */ 1907 break; 1908 } 1909#else /* HAVE_WAITPID */ 1910 waited = wait(&childstat); 1911 signal(SIGCHLD, chldfunc); 1912 /* 1913 Since we had the signal handler uninstalled for a while, 1914 we might have missed the termination of our screen child. 1915 If we can check for this possibility without hanging, do so. 1916 */ 1917 do 1918 if (waited == TScreenOf(term)->pid) 1919 NormalExit(); 1920 while ((waited = nonblocking_wait()) > 0) ; 1921#endif /* HAVE_WAITPID */ 1922#ifndef WIFEXITED 1923#define WIFEXITED(status) ((status & 0xff) != 0) 1924#endif 1925 if (WIFEXITED(childstat)) 1926 retval = 1; 1927 return retval; 1928 } 1929} 1930#endif /* !VMS */ 1931 1932int 1933xtermResetIds(TScreen *screen) 1934{ 1935 int result = 0; 1936 if (setgid(screen->gid) == -1) { 1937 xtermWarning("unable to reset group-id\n"); 1938 result = -1; 1939 } 1940 if (setuid(screen->uid) == -1) { 1941 xtermWarning("unable to reset user-id\n"); 1942 result = -1; 1943 } 1944 return result; 1945} 1946 1947#ifdef ALLOWLOGGING 1948 1949/* 1950 * Logging is a security hole, since it allows a setuid program to write 1951 * arbitrary data to an arbitrary file. So it is disabled by default. 1952 */ 1953 1954#ifdef ALLOWLOGFILEEXEC 1955static void 1956logpipe(int sig GCC_UNUSED) 1957{ 1958 XtermWidget xw = term; 1959 TScreen *screen = TScreenOf(xw); 1960 1961 DEBUG_MSG("handle:logpipe\n"); 1962#ifdef SYSV 1963 (void) signal(SIGPIPE, SIG_IGN); 1964#endif /* SYSV */ 1965 if (screen->logging) 1966 CloseLog(xw); 1967} 1968#endif /* ALLOWLOGFILEEXEC */ 1969 1970void 1971StartLog(XtermWidget xw) 1972{ 1973 static char *log_default; 1974 TScreen *screen = TScreenOf(xw); 1975 1976 if (screen->logging || (screen->inhibit & I_LOG)) 1977 return; 1978#ifdef VMS /* file name is fixed in VMS variant */ 1979 screen->logfd = open(XTERM_VMS_LOGFILE, 1980 O_CREAT | O_TRUNC | O_APPEND | O_RDWR, 1981 0640); 1982 if (screen->logfd < 0) 1983 return; /* open failed */ 1984#else /*VMS */ 1985 if (screen->logfile == NULL || *screen->logfile == 0) { 1986 if (screen->logfile) 1987 free(screen->logfile); 1988 if (log_default == NULL) { 1989#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME) 1990 char log_def_name[512]; /* see sprintf below */ 1991 char hostname[255 + 1]; /* Internet standard limit (RFC 1035): 1992 ``To simplify implementations, the 1993 total length of a domain name (i.e., 1994 label octets and label length 1995 octets) is restricted to 255 octets 1996 or less.'' */ 1997 char yyyy_mm_dd_hh_mm_ss[4 + 5 * (1 + 2) + 1]; 1998 time_t now; 1999 struct tm *ltm; 2000 2001 now = time((time_t *) 0); 2002 ltm = (struct tm *) localtime(&now); 2003 if ((gethostname(hostname, sizeof(hostname)) == 0) && 2004 (strftime(yyyy_mm_dd_hh_mm_ss, 2005 sizeof(yyyy_mm_dd_hh_mm_ss), 2006 "%Y.%m.%d.%H.%M.%S", ltm) > 0)) { 2007 (void) sprintf(log_def_name, "Xterm.log.%.255s.%.20s.%d", 2008 hostname, yyyy_mm_dd_hh_mm_ss, (int) getpid()); 2009 } 2010 if ((log_default = x_strdup(log_def_name)) == NULL) 2011 return; 2012#else 2013 const char *log_def_name = "XtermLog.XXXXXX"; 2014 if ((log_default = x_strdup(log_def_name)) == NULL) 2015 return; 2016 2017 mktemp(log_default); 2018#endif 2019 } 2020 if ((screen->logfile = x_strdup(log_default)) == 0) 2021 return; 2022 } 2023 if (*screen->logfile == '|') { /* exec command */ 2024#ifdef ALLOWLOGFILEEXEC 2025 /* 2026 * Warning, enabling this "feature" allows arbitrary programs 2027 * to be run. If ALLOWLOGFILECHANGES is enabled, this can be 2028 * done through escape sequences.... You have been warned. 2029 */ 2030 int pid; 2031 int p[2]; 2032 static char *shell; 2033 struct passwd pw; 2034 2035 if ((shell = x_getenv("SHELL")) == NULL) { 2036 2037 if (x_getpwuid(screen->uid, &pw)) { 2038 char *name = x_getlogin(screen->uid, &pw); 2039 if (*(pw.pw_shell)) { 2040 shell = pw.pw_shell; 2041 } 2042 free(name); 2043 } 2044 } 2045 2046 if (shell == 0) { 2047 static char dummy[] = "/bin/sh"; 2048 shell = dummy; 2049 } 2050 2051 if (access(shell, X_OK) != 0) { 2052 xtermPerror("Can't execute `%s'\n", shell); 2053 return; 2054 } 2055 2056 if (pipe(p) < 0) { 2057 xtermPerror("Can't make a pipe connection\n"); 2058 return; 2059 } else if ((pid = fork()) < 0) { 2060 xtermPerror("Can't fork...\n"); 2061 return; 2062 } 2063 if (pid == 0) { /* child */ 2064 /* 2065 * Close our output (we won't be talking back to the 2066 * parent), and redirect our child's output to the 2067 * original stderr. 2068 */ 2069 close(p[1]); 2070 dup2(p[0], 0); 2071 close(p[0]); 2072 dup2(fileno(stderr), 1); 2073 dup2(fileno(stderr), 2); 2074 2075 close(fileno(stderr)); 2076 close(ConnectionNumber(screen->display)); 2077 close(screen->respond); 2078 2079 signal(SIGHUP, SIG_DFL); 2080 signal(SIGCHLD, SIG_DFL); 2081 2082 /* (this is redundant) */ 2083 if (xtermResetIds(screen) < 0) 2084 exit(ERROR_SETUID); 2085 2086 if (access(shell, X_OK) == 0) { 2087 execl(shell, shell, "-c", &screen->logfile[1], (void *) 0); 2088 xtermWarning("Can't exec `%s'\n", &screen->logfile[1]); 2089 } else { 2090 xtermWarning("Can't execute `%s'\n", shell); 2091 } 2092 exit(ERROR_LOGEXEC); 2093 } 2094 close(p[0]); 2095 screen->logfd = p[1]; 2096 signal(SIGPIPE, logpipe); 2097#else 2098 Bell(xw, XkbBI_Info, 0); 2099 Bell(xw, XkbBI_Info, 0); 2100 return; 2101#endif 2102 } else { 2103 if ((screen->logfd = open_userfile(screen->uid, 2104 screen->gid, 2105 screen->logfile, 2106 (log_default != 0))) < 0) 2107 return; 2108 } 2109#endif /*VMS */ 2110 screen->logstart = VTbuffer->next; 2111 screen->logging = True; 2112 update_logging(); 2113} 2114 2115void 2116CloseLog(XtermWidget xw) 2117{ 2118 TScreen *screen = TScreenOf(xw); 2119 2120 if (!screen->logging || (screen->inhibit & I_LOG)) 2121 return; 2122 FlushLog(xw); 2123 close(screen->logfd); 2124 screen->logging = False; 2125 update_logging(); 2126} 2127 2128void 2129FlushLog(XtermWidget xw) 2130{ 2131 TScreen *screen = TScreenOf(xw); 2132 2133 if (screen->logging && !(screen->inhibit & I_LOG)) { 2134 Char *cp; 2135 int i; 2136 2137#ifdef VMS /* avoid logging output loops which otherwise occur sometimes 2138 when there is no output and cp/screen->logstart are 1 apart */ 2139 if (!tt_new_output) 2140 return; 2141 tt_new_output = False; 2142#endif /* VMS */ 2143 cp = VTbuffer->next; 2144 if (screen->logstart != 0 2145 && (i = (int) (cp - screen->logstart)) > 0) { 2146 IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i)); 2147 } 2148 screen->logstart = VTbuffer->next; 2149 } 2150} 2151 2152#endif /* ALLOWLOGGING */ 2153 2154/***====================================================================***/ 2155 2156int 2157getVisualInfo(XtermWidget xw) 2158{ 2159#define MYFMT "getVisualInfo \ 2160depth %d, \ 2161type %d (%s), \ 2162size %d \ 2163rgb masks (%04lx/%04lx/%04lx)\n" 2164#define MYARG \ 2165 vi->depth,\ 2166 vi->class,\ 2167 ((vi->class & 1) ? "dynamic" : "static"),\ 2168 vi->colormap_size,\ 2169 vi->red_mask,\ 2170 vi->green_mask,\ 2171 vi->blue_mask 2172 2173 TScreen *screen = TScreenOf(xw); 2174 Display *dpy = screen->display; 2175 XVisualInfo myTemplate; 2176 2177 if (xw->visInfo == 0 && xw->numVisuals == 0) { 2178 myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy, 2179 XDefaultScreen(dpy))); 2180 xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask, 2181 &myTemplate, &xw->numVisuals); 2182 2183 if ((xw->visInfo != 0) && (xw->numVisuals > 0)) { 2184 XVisualInfo *vi = xw->visInfo; 2185 if (resource.reportColors) { 2186 printf(MYFMT, MYARG); 2187 } 2188 TRACE((MYFMT, MYARG)); 2189 } 2190 } 2191 return (xw->visInfo != 0) && (xw->numVisuals > 0); 2192#undef MYFMT 2193#undef MYARG 2194} 2195 2196#if OPT_ISO_COLORS 2197static void 2198ReportAnsiColorRequest(XtermWidget xw, int colornum, int final) 2199{ 2200 if (AllowColorOps(xw, ecGetAnsiColor)) { 2201 XColor color; 2202 Colormap cmap = xw->core.colormap; 2203 char buffer[80]; 2204 2205 TRACE(("ReportAnsiColorRequest %d\n", colornum)); 2206 color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]); 2207 XQueryColor(TScreenOf(xw)->display, cmap, &color); 2208 sprintf(buffer, "4;%d;rgb:%04x/%04x/%04x", 2209 colornum, 2210 color.red, 2211 color.green, 2212 color.blue); 2213 unparseputc1(xw, ANSI_OSC); 2214 unparseputs(xw, buffer); 2215 unparseputc1(xw, final); 2216 unparse_end(xw); 2217 } 2218} 2219 2220static void 2221getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep) 2222{ 2223 if (getVisualInfo(xw)) { 2224 *typep = (unsigned) xw->visInfo->class; 2225 *sizep = (unsigned) xw->visInfo->colormap_size; 2226 } else { 2227 *typep = 0; 2228 *sizep = 0; 2229 } 2230} 2231 2232#define MAX_COLORTABLE 4096 2233 2234/* 2235 * Make only one call to XQueryColors(), since it can be slow. 2236 */ 2237static Boolean 2238loadColorTable(XtermWidget xw, unsigned length) 2239{ 2240 Colormap cmap = xw->core.colormap; 2241 TScreen *screen = TScreenOf(xw); 2242 unsigned i; 2243 Boolean result = (screen->cmap_data != 0); 2244 2245 if (!result 2246 && length != 0 2247 && length < MAX_COLORTABLE) { 2248 screen->cmap_data = TypeMallocN(XColor, (size_t) length); 2249 if (screen->cmap_data != 0) { 2250 screen->cmap_size = length; 2251 2252 for (i = 0; i < screen->cmap_size; i++) { 2253 screen->cmap_data[i].pixel = (unsigned long) i; 2254 } 2255 result = (Boolean) (XQueryColors(screen->display, 2256 cmap, 2257 screen->cmap_data, 2258 (int) screen->cmap_size) != 0); 2259 } 2260 } 2261 return result; 2262} 2263 2264/* 2265 * Find closest color for "def" in "cmap". 2266 * Set "def" to the resulting color. 2267 * 2268 * Based on Monish Shah's "find_closest_color()" for Vim 6.0, 2269 * modified with ideas from David Tong's "noflash" library. 2270 * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk. 2271 * 2272 * Return False if not able to find or allocate a color. 2273 */ 2274static Boolean 2275allocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def) 2276{ 2277 TScreen *screen = TScreenOf(xw); 2278 Boolean result = False; 2279 char *tried; 2280 double diff, thisRGB, bestRGB; 2281 unsigned attempts; 2282 unsigned bestInx; 2283 unsigned cmap_type; 2284 unsigned cmap_size; 2285 unsigned i; 2286 2287 getColormapInfo(xw, &cmap_type, &cmap_size); 2288 2289 if ((cmap_type & 1) != 0) { 2290 2291 if (loadColorTable(xw, cmap_size)) { 2292 2293 tried = TypeCallocN(char, (size_t) cmap_size); 2294 if (tried != 0) { 2295 2296 /* 2297 * Try (possibly each entry in the color map) to find the best 2298 * approximation to the requested color. 2299 */ 2300 for (attempts = 0; attempts < cmap_size; attempts++) { 2301 Boolean first = True; 2302 2303 bestRGB = 0.0; 2304 bestInx = 0; 2305 for (i = 0; i < cmap_size; i++) { 2306 if (!tried[bestInx]) { 2307 /* 2308 * Look for the best match based on luminance. 2309 * Measure this by the least-squares difference of 2310 * the weighted R/G/B components from the color map 2311 * versus the requested color. Use the Y (luma) 2312 * component of the YIQ color space model for 2313 * weights that correspond to the luminance. 2314 */ 2315#define AddColorWeight(weight, color) \ 2316 diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \ 2317 thisRGB = diff * diff 2318 2319 AddColorWeight(0.30, red); 2320 AddColorWeight(0.61, green); 2321 AddColorWeight(0.11, blue); 2322 2323 if (first || (thisRGB < bestRGB)) { 2324 first = False; 2325 bestInx = i; 2326 bestRGB = thisRGB; 2327 } 2328 } 2329 } 2330 if (XAllocColor(screen->display, cmap, 2331 &screen->cmap_data[bestInx]) != 0) { 2332 *def = screen->cmap_data[bestInx]; 2333 TRACE(("...closest %x/%x/%x\n", def->red, 2334 def->green, def->blue)); 2335 result = True; 2336 break; 2337 } 2338 /* 2339 * It failed - either the color map entry was readonly, or 2340 * another client has allocated the entry. Mark the entry 2341 * so we will ignore it 2342 */ 2343 tried[bestInx] = True; 2344 } 2345 free(tried); 2346 } 2347 } 2348 } 2349 return result; 2350} 2351 2352#ifndef ULONG_MAX 2353#define ULONG_MAX (unsigned long)(~(0L)) 2354#endif 2355 2356#define CheckColor(result, value) \ 2357 result = 0; \ 2358 if (value.red) \ 2359 result |= 1; \ 2360 if (value.green) \ 2361 result |= 2; \ 2362 if (value.blue) \ 2363 result |= 4 2364 2365#define SelectColor(state, value, result) \ 2366 switch (state) { \ 2367 default: \ 2368 case 1: \ 2369 result = value.red; \ 2370 break; \ 2371 case 2: \ 2372 result = value.green; \ 2373 break; \ 2374 case 4: \ 2375 result = value.blue; \ 2376 break; \ 2377 } 2378 2379/* 2380 * Check if the color map consists of values in exactly one of the red, green 2381 * or blue columns. If it is not, we do not know how to use it for the exact 2382 * match. 2383 */ 2384static int 2385simpleColors(XColor *colortable, unsigned length) 2386{ 2387 unsigned n; 2388 int state = 0; 2389 int check; 2390 2391 for (n = 0; n < length; ++n) { 2392 if (state > 0) { 2393 CheckColor(check, colortable[n]); 2394 if (check > 0 && check != state) { 2395 state = 0; 2396 break; 2397 } 2398 } else { 2399 CheckColor(state, colortable[n]); 2400 } 2401 } 2402 switch (state) { 2403 case 1: 2404 case 2: 2405 case 4: 2406 break; 2407 default: 2408 state = 0; 2409 break; 2410 } 2411 return state; 2412} 2413 2414/* 2415 * Shift the mask left or right to put its most significant bit at the 16-bit 2416 * mark. 2417 */ 2418static unsigned 2419normalizeMask(unsigned mask) 2420{ 2421 while (mask < 0x8000) { 2422 mask <<= 1; 2423 } 2424 while (mask >= 0x10000) { 2425 mask >>= 1; 2426 } 2427 return mask; 2428} 2429 2430static unsigned 2431searchColors(XColor *colortable, unsigned mask, unsigned length, unsigned 2432 color, int state) 2433{ 2434 unsigned result = 0; 2435 unsigned n; 2436 unsigned long best = ULONG_MAX; 2437 unsigned long diff; 2438 unsigned value; 2439 2440 mask = normalizeMask(mask); 2441 for (n = 0; n < length; ++n) { 2442 SelectColor(state, colortable[n], value); 2443 diff = ((color & mask) - (value & mask)); 2444 diff *= diff; 2445 if (diff < best) { 2446#if 0 2447 TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n", 2448 n, color, 2449 colortable[n].red, 2450 colortable[n].green, 2451 colortable[n].blue, 2452 diff)); 2453#endif 2454 result = n; 2455 best = diff; 2456 } 2457 } 2458 SelectColor(state, colortable[result], value); 2459 return value; 2460} 2461 2462/* 2463 * This is a workaround for a longstanding defect in the X libraries. 2464 * 2465 * According to 2466 * http://www.unix.com/man-page/all/3x/XAllocColoA/ 2467 * 2468 * XAllocColor() acts differently on static and dynamic visuals. On Pseu- 2469 * doColor, DirectColor, and GrayScale visuals, XAllocColor() fails if 2470 * there are no unallocated colorcells and no allocated read-only cell 2471 * exactly matches the requested RGB values. On StaticColor, TrueColor, 2472 * and StaticGray visuals, XAllocColor() returns the closest RGB values 2473 * available in the colormap. The colorcell_in_out structure returns the 2474 * actual RGB values allocated. 2475 * 2476 * That is, XAllocColor() should suffice unless the color map is full. In that 2477 * case, allocateClosestRGB() is useful for the dynamic display classes such as 2478 * PseudoColor. It is not useful for TrueColor, since XQueryColors() does not 2479 * return regular RGB triples (unless a different scheme was used for 2480 * specifying the pixel values); only the blue value is filled in. However, it 2481 * is filled in with the colors that the server supports. 2482 * 2483 * Also (the reason for this function), XAllocColor() does not really work as 2484 * described. For some TrueColor configurations it merely returns a close 2485 * approximation, but not the closest. 2486 */ 2487static Boolean 2488allocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def) 2489{ 2490 XColor save = *def; 2491 TScreen *screen = TScreenOf(xw); 2492 Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0); 2493 2494 /* 2495 * If this is a statically allocated display with too many items to store 2496 * in our array, i.e., TrueColor, see if we can improve on the result by 2497 * using the color values actually supported by the server. 2498 */ 2499 if (result) { 2500 unsigned cmap_type; 2501 unsigned cmap_size; 2502 int state; 2503 2504 getColormapInfo(xw, &cmap_type, &cmap_size); 2505 2506 if (cmap_type == TrueColor) { 2507 XColor temp = *def; 2508 2509 if (loadColorTable(xw, cmap_size) 2510 && (state = simpleColors(screen->cmap_data, cmap_size)) > 0) { 2511#define SearchColors(which) \ 2512 temp.which = (unsigned short) searchColors(screen->cmap_data, \ 2513 (unsigned) xw->visInfo->which##_mask,\ 2514 cmap_size, \ 2515 save.which, \ 2516 state) 2517 SearchColors(red); 2518 SearchColors(green); 2519 SearchColors(blue); 2520 if (XAllocColor(screen->display, cmap, &temp) != 0) { 2521#if OPT_TRACE 2522 if (temp.red != save.red 2523 || temp.green != save.green 2524 || temp.blue != save.blue) { 2525 TRACE(("...improved %x/%x/%x ->%x/%x/%x\n", 2526 save.red, save.green, save.blue, 2527 temp.red, temp.green, temp.blue)); 2528 } else { 2529 TRACE(("...no improvement for %x/%x/%x\n", 2530 save.red, save.green, save.blue)); 2531 } 2532#endif 2533 *def = temp; 2534 } 2535 } 2536 } 2537 } 2538 2539 return result; 2540} 2541 2542/* 2543 * Allocate a color for the "ANSI" colors. That actually includes colors up 2544 * to 256. 2545 * 2546 * Returns 2547 * -1 on error 2548 * 0 on no change 2549 * 1 if a new color was allocated. 2550 */ 2551static int 2552AllocateAnsiColor(XtermWidget xw, 2553 ColorRes * res, 2554 const char *spec) 2555{ 2556 int result; 2557 XColor def; 2558 2559 if (xtermAllocColor(xw, &def, spec)) { 2560 if ( 2561#if OPT_COLOR_RES 2562 res->mode == True && 2563#endif 2564 EQL_COLOR_RES(res, def.pixel)) { 2565 result = 0; 2566 } else { 2567 result = 1; 2568 SET_COLOR_RES(res, def.pixel); 2569 res->red = def.red; 2570 res->green = def.green; 2571 res->blue = def.blue; 2572 TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n", 2573 (int) (res - TScreenOf(xw)->Acolors), spec, 2574 def.red, 2575 def.green, 2576 def.blue, 2577 def.pixel)); 2578#if OPT_COLOR_RES 2579 if (!res->mode) 2580 result = 0; 2581 res->mode = True; 2582#endif 2583 } 2584 } else { 2585 TRACE(("AllocateAnsiColor %s (failed)\n", spec)); 2586 result = -1; 2587 } 2588 return (result); 2589} 2590 2591#if OPT_COLOR_RES 2592Pixel 2593xtermGetColorRes(XtermWidget xw, ColorRes * res) 2594{ 2595 Pixel result = 0; 2596 2597 if (res->mode) { 2598 result = res->value; 2599 } else { 2600 TRACE(("xtermGetColorRes for Acolors[%d]\n", 2601 (int) (res - TScreenOf(xw)->Acolors))); 2602 2603 if (res >= TScreenOf(xw)->Acolors) { 2604 assert(res - TScreenOf(xw)->Acolors < MAXCOLORS); 2605 2606 if (AllocateAnsiColor(xw, res, res->resource) < 0) { 2607 res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value; 2608 res->mode = -True; 2609 xtermWarning("Cannot allocate color \"%s\"\n", 2610 NonNull(res->resource)); 2611 } 2612 result = res->value; 2613 } else { 2614 result = 0; 2615 } 2616 } 2617 return result; 2618} 2619#endif 2620 2621static int 2622ChangeOneAnsiColor(XtermWidget xw, int color, const char *name) 2623{ 2624 int code; 2625 2626 if (color < 0 || color >= MAXCOLORS) { 2627 code = -1; 2628 } else { 2629 ColorRes *res = &(TScreenOf(xw)->Acolors[color]); 2630 2631 TRACE(("ChangeAnsiColor for Acolors[%d]\n", color)); 2632 code = AllocateAnsiColor(xw, res, name); 2633 } 2634 return code; 2635} 2636 2637/* 2638 * Set or query entries in the Acolors[] array by parsing pairs of color/name 2639 * values from the given buffer. 2640 * 2641 * The color can be any legal index into Acolors[], which consists of the 2642 * 16/88/256 "ANSI" colors, followed by special color values for the various 2643 * colorXX resources. The indices for the special color values are not 2644 * simple to work with, so an alternative is to use the calls which pass in 2645 * 'first' set to the beginning of those indices. 2646 * 2647 * If the name is "?", report to the host the current value for the color. 2648 */ 2649static Bool 2650ChangeAnsiColorRequest(XtermWidget xw, 2651 char *buf, 2652 int first, 2653 int final) 2654{ 2655 char *name; 2656 int color; 2657 int repaint = False; 2658 int code; 2659 int last = (MAXCOLORS - first); 2660 2661 TRACE(("ChangeAnsiColorRequest string='%s'\n", buf)); 2662 2663 while (buf && *buf) { 2664 name = strchr(buf, ';'); 2665 if (name == NULL) 2666 break; 2667 *name = '\0'; 2668 name++; 2669 color = atoi(buf); 2670 if (color < 0 || color >= last) 2671 break; /* quit on any error */ 2672 buf = strchr(name, ';'); 2673 if (buf) { 2674 *buf = '\0'; 2675 buf++; 2676 } 2677 if (!strcmp(name, "?")) { 2678 ReportAnsiColorRequest(xw, color + first, final); 2679 } else { 2680 code = ChangeOneAnsiColor(xw, color + first, name); 2681 if (code < 0) { 2682 /* stop on any error */ 2683 break; 2684 } else if (code > 0) { 2685 repaint = True; 2686 } 2687 /* FIXME: free old color somehow? We aren't for the other color 2688 * change style (dynamic colors). 2689 */ 2690 } 2691 } 2692 2693 return (repaint); 2694} 2695 2696static Bool 2697ResetOneAnsiColor(XtermWidget xw, int color, int start) 2698{ 2699 Bool repaint = False; 2700 int last = MAXCOLORS - start; 2701 2702 if (color >= 0 && color < last) { 2703 ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]); 2704 2705 if (res->mode) { 2706 /* a color has been allocated for this slot - test further... */ 2707 if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) { 2708 repaint = True; 2709 } 2710 } 2711 } 2712 return repaint; 2713} 2714 2715int 2716ResetAnsiColorRequest(XtermWidget xw, char *buf, int start) 2717{ 2718 int repaint = 0; 2719 int color; 2720 2721 TRACE(("ResetAnsiColorRequest(%s)\n", buf)); 2722 if (*buf != '\0') { 2723 /* reset specific colors */ 2724 while (!IsEmpty(buf)) { 2725 char *next; 2726 2727 color = (int) strtol(buf, &next, 10); 2728 if ((next == buf) || (color < 0)) 2729 break; /* no number at all */ 2730 if (next != 0) { 2731 if (strchr(";", *next) == 0) 2732 break; /* unexpected delimiter */ 2733 ++next; 2734 } 2735 2736 if (ResetOneAnsiColor(xw, color, start)) { 2737 ++repaint; 2738 } 2739 buf = next; 2740 } 2741 } else { 2742 TRACE(("...resetting all %d colors\n", MAXCOLORS)); 2743 for (color = 0; color < MAXCOLORS; ++color) { 2744 if (ResetOneAnsiColor(xw, color, start)) { 2745 ++repaint; 2746 } 2747 } 2748 } 2749 TRACE(("...ResetAnsiColorRequest ->%d\n", repaint)); 2750 return repaint; 2751} 2752#else 2753#define allocateClosestRGB(xw, cmap, def) 0 2754#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def) 2755#endif /* OPT_ISO_COLORS */ 2756 2757Boolean 2758allocateBestRGB(XtermWidget xw, XColor *def) 2759{ 2760 Colormap cmap = xw->core.colormap; 2761 2762 return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def); 2763} 2764 2765static Boolean 2766xtermAllocColor(XtermWidget xw, XColor *def, const char *spec) 2767{ 2768 Boolean result = False; 2769 TScreen *screen = TScreenOf(xw); 2770 Colormap cmap = xw->core.colormap; 2771 2772 if (XParseColor(screen->display, cmap, spec, def)) { 2773 XColor save_def = *def; 2774 if (resource.reportColors) { 2775 printf("color %04x/%04x/%04x = \"%s\"\n", 2776 def->red, def->green, def->blue, 2777 spec); 2778 } 2779 if (allocateBestRGB(xw, def)) { 2780 if (resource.reportColors) { 2781 if (def->red != save_def.red || 2782 def->green != save_def.green || 2783 def->blue != save_def.blue) { 2784 printf("color %04x/%04x/%04x ~ \"%s\"\n", 2785 def->red, def->green, def->blue, 2786 spec); 2787 } 2788 } 2789 TRACE(("xtermAllocColor -> %x/%x/%x\n", 2790 def->red, def->green, def->blue)); 2791 result = True; 2792 } 2793 } 2794 return result; 2795} 2796 2797/* 2798 * This provides an approximation (the closest color from xterm's palette) 2799 * rather than the "exact" color (whatever the display could provide, actually) 2800 * because of the context in which it is used. 2801 */ 2802#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given)) 2803int 2804xtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue) 2805{ 2806 int result = -1; 2807#if OPT_COLOR_RES && OPT_ISO_COLORS 2808 int n; 2809 int best_index = -1; 2810 unsigned long best_value = 0; 2811 unsigned long this_value; 2812 long diff_red, diff_green, diff_blue; 2813 2814 TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue)); 2815 2816 for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) { 2817 ColorRes *res = &(TScreenOf(xw)->Acolors[n]); 2818 2819 /* ensure that we have a value for each of the colors */ 2820 if (!res->mode) { 2821 (void) AllocateAnsiColor(xw, res, res->resource); 2822 } 2823 2824 /* find the closest match */ 2825 if (res->mode == True) { 2826 TRACE2(("...lookup %lx -> %x/%x/%x\n", 2827 res->value, res->red, res->green, res->blue)); 2828 diff_red = ColorDiff(find_red, res->red); 2829 diff_green = ColorDiff(find_green, res->green); 2830 diff_blue = ColorDiff(find_blue, res->blue); 2831 this_value = (unsigned long) ((diff_red * diff_red) 2832 + (diff_green * diff_green) 2833 + (diff_blue * diff_blue)); 2834 if (best_index < 0 || this_value < best_value) { 2835 best_index = n; 2836 best_value = this_value; 2837 } 2838 } 2839 } 2840 TRACE(("...best match at %d with diff %lx\n", best_index, best_value)); 2841 result = best_index; 2842#else 2843 (void) xw; 2844 (void) find_red; 2845 (void) find_green; 2846 (void) find_blue; 2847#endif 2848 return result; 2849} 2850 2851#if OPT_PASTE64 2852static void 2853ManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final) 2854{ 2855#define PDATA(a,b) { a, #b } 2856 static struct { 2857 char given; 2858 String result; 2859 } table[] = { 2860 PDATA('s', SELECT), 2861 PDATA('p', PRIMARY), 2862 PDATA('c', CLIPBOARD), 2863 PDATA('0', CUT_BUFFER0), 2864 PDATA('1', CUT_BUFFER1), 2865 PDATA('2', CUT_BUFFER2), 2866 PDATA('3', CUT_BUFFER3), 2867 PDATA('4', CUT_BUFFER4), 2868 PDATA('5', CUT_BUFFER5), 2869 PDATA('6', CUT_BUFFER6), 2870 PDATA('7', CUT_BUFFER7), 2871 }; 2872 2873 const char *base = buf; 2874 char *used; 2875 Cardinal j, n = 0; 2876 String *select_args; 2877 2878 TRACE(("Manipulate selection data\n")); 2879 2880 while (*buf != ';' && *buf != '\0') { 2881 ++buf; 2882 } 2883 2884 if (*buf == ';') { 2885 *buf++ = '\0'; 2886 2887 if (*base == '\0') 2888 base = "s0"; 2889 2890 if ((used = x_strdup(base)) != 0) { 2891 if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) { 2892 while (*base != '\0') { 2893 for (j = 0; j < XtNumber(table); ++j) { 2894 if (*base == table[j].given) { 2895 used[n] = *base; 2896 select_args[n++] = table[j].result; 2897 TRACE(("atom[%d] %s\n", n, table[j].result)); 2898 break; 2899 } 2900 } 2901 ++base; 2902 } 2903 used[n] = 0; 2904 2905 if (!strcmp(buf, "?")) { 2906 if (AllowWindowOps(xw, ewGetSelection)) { 2907 TRACE(("Getting selection\n")); 2908 unparseputc1(xw, ANSI_OSC); 2909 unparseputs(xw, "52"); 2910 unparseputc(xw, ';'); 2911 2912 unparseputs(xw, used); 2913 unparseputc(xw, ';'); 2914 2915 /* Tell xtermGetSelection data is base64 encoded */ 2916 screen->base64_paste = n; 2917 screen->base64_final = final; 2918 2919 /* terminator will be written in this call */ 2920 xtermGetSelection((Widget) xw, 2921 XtLastTimestampProcessed(TScreenOf(xw)->display), 2922 select_args, n, 2923 NULL); 2924 } 2925 } else { 2926 if (AllowWindowOps(xw, ewSetSelection)) { 2927 TRACE(("Setting selection with %s\n", buf)); 2928 ClearSelectionBuffer(screen); 2929 while (*buf != '\0') 2930 AppendToSelectionBuffer(screen, CharOf(*buf++)); 2931 CompleteSelection(xw, select_args, n); 2932 } 2933 } 2934 free(select_args); 2935 } 2936 free(used); 2937 } 2938 } 2939} 2940#endif /* OPT_PASTE64 */ 2941 2942/***====================================================================***/ 2943 2944#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) || (xw->screen.utf8_title)) 2945 2946static Bool 2947xtermIsPrintable(XtermWidget xw, Char **bufp, Char *last) 2948{ 2949 TScreen *screen = TScreenOf(xw); 2950 Bool result = False; 2951 Char *cp = *bufp; 2952 Char *next = cp; 2953 2954 (void) screen; 2955 (void) last; 2956 2957#if OPT_WIDE_CHARS 2958 if (xtermEnvUTF8() && IsSetUtf8Title(xw)) { 2959 PtyData data; 2960 2961 if (decodeUtf8(screen, fakePtyData(&data, cp, last))) { 2962 if (data.utf_data != UCS_REPL 2963 && (data.utf_data >= 128 || 2964 ansi_table[data.utf_data] == CASE_PRINT)) { 2965 next += (data.utf_size - 1); 2966 result = True; 2967 } else { 2968 result = False; 2969 } 2970 } else { 2971 result = False; 2972 } 2973 } else 2974#endif 2975#if OPT_C1_PRINT 2976 if (screen->c1_printable 2977 && (*cp >= 128 && *cp < 160)) { 2978 result = True; 2979 } else 2980#endif 2981 if (ansi_table[*cp] == CASE_PRINT) { 2982 result = True; 2983 } 2984 *bufp = next; 2985 return result; 2986} 2987 2988/***====================================================================***/ 2989 2990/* 2991 * Enum corresponding to the actual OSC codes rather than the internal 2992 * array indices. Compare with TermColors. 2993 */ 2994typedef enum { 2995 OSC_TEXT_FG = 10 2996 ,OSC_TEXT_BG 2997 ,OSC_TEXT_CURSOR 2998 ,OSC_MOUSE_FG 2999 ,OSC_MOUSE_BG 3000#if OPT_TEK4014 3001 ,OSC_TEK_FG = 15 3002 ,OSC_TEK_BG 3003#endif 3004#if OPT_HIGHLIGHT_COLOR 3005 ,OSC_HIGHLIGHT_BG = 17 3006#endif 3007#if OPT_TEK4014 3008 ,OSC_TEK_CURSOR = 18 3009#endif 3010#if OPT_HIGHLIGHT_COLOR 3011 ,OSC_HIGHLIGHT_FG = 19 3012#endif 3013 ,OSC_NCOLORS 3014} OscTextColors; 3015 3016/* 3017 * Map codes to OSC controls that can reset colors. 3018 */ 3019#define OSC_RESET 100 3020#define OSC_Reset(code) (code) + OSC_RESET 3021 3022static Bool 3023GetOldColors(XtermWidget xw) 3024{ 3025 int i; 3026 if (xw->work.oldColors == NULL) { 3027 xw->work.oldColors = TypeXtMalloc(ScrnColors); 3028 if (xw->work.oldColors == NULL) { 3029 xtermWarning("allocation failure in GetOldColors\n"); 3030 return (False); 3031 } 3032 xw->work.oldColors->which = 0; 3033 for (i = 0; i < NCOLORS; i++) { 3034 xw->work.oldColors->colors[i] = 0; 3035 xw->work.oldColors->names[i] = NULL; 3036 } 3037 GetColors(xw, xw->work.oldColors); 3038 } 3039 return (True); 3040} 3041 3042static int 3043oppositeColor(int n) 3044{ 3045 switch (n) { 3046 case TEXT_FG: 3047 n = TEXT_BG; 3048 break; 3049 case TEXT_BG: 3050 n = TEXT_FG; 3051 break; 3052 case MOUSE_FG: 3053 n = MOUSE_BG; 3054 break; 3055 case MOUSE_BG: 3056 n = MOUSE_FG; 3057 break; 3058#if OPT_TEK4014 3059 case TEK_FG: 3060 n = TEK_BG; 3061 break; 3062 case TEK_BG: 3063 n = TEK_FG; 3064 break; 3065#endif 3066#if OPT_HIGHLIGHT_COLOR 3067 case HIGHLIGHT_FG: 3068 n = HIGHLIGHT_BG; 3069 break; 3070 case HIGHLIGHT_BG: 3071 n = HIGHLIGHT_FG; 3072 break; 3073#endif 3074 default: 3075 break; 3076 } 3077 return n; 3078} 3079 3080static void 3081ReportColorRequest(XtermWidget xw, int ndx, int final) 3082{ 3083 if (AllowColorOps(xw, ecGetColor)) { 3084 XColor color; 3085 Colormap cmap = xw->core.colormap; 3086 char buffer[80]; 3087 3088 /* 3089 * ChangeColorsRequest() has "always" chosen the opposite color when 3090 * reverse-video is set. Report this as the original color index, but 3091 * reporting the opposite color which would be used. 3092 */ 3093 int i = (xw->misc.re_verse) ? oppositeColor(ndx) : ndx; 3094 3095 GetOldColors(xw); 3096 color.pixel = xw->work.oldColors->colors[ndx]; 3097 XQueryColor(TScreenOf(xw)->display, cmap, &color); 3098 sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10, 3099 color.red, 3100 color.green, 3101 color.blue); 3102 TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n", 3103 ndx, xw->work.oldColors->colors[ndx], buffer)); 3104 unparseputc1(xw, ANSI_OSC); 3105 unparseputs(xw, buffer); 3106 unparseputc1(xw, final); 3107 unparse_end(xw); 3108 } 3109} 3110 3111static Bool 3112UpdateOldColors(XtermWidget xw GCC_UNUSED, ScrnColors * pNew) 3113{ 3114 int i; 3115 3116 /* if we were going to free old colors, this would be the place to 3117 * do it. I've decided not to (for now), because it seems likely 3118 * that we'd have a small set of colors we use over and over, and that 3119 * we could save some overhead this way. The only case in which this 3120 * (clearly) fails is if someone is trying a boatload of colors, in 3121 * which case they can restart xterm 3122 */ 3123 for (i = 0; i < NCOLORS; i++) { 3124 if (COLOR_DEFINED(pNew, i)) { 3125 if (xw->work.oldColors->names[i] != NULL) { 3126 XtFree(xw->work.oldColors->names[i]); 3127 xw->work.oldColors->names[i] = NULL; 3128 } 3129 if (pNew->names[i]) { 3130 xw->work.oldColors->names[i] = pNew->names[i]; 3131 } 3132 xw->work.oldColors->colors[i] = pNew->colors[i]; 3133 } 3134 } 3135 return (True); 3136} 3137 3138/* 3139 * OSC codes are constant, but the indices for the color arrays depend on how 3140 * xterm is compiled. 3141 */ 3142static int 3143OscToColorIndex(OscTextColors mode) 3144{ 3145 int result = 0; 3146 3147#define CASE(name) case OSC_##name: result = name; break 3148 switch (mode) { 3149 CASE(TEXT_FG); 3150 CASE(TEXT_BG); 3151 CASE(TEXT_CURSOR); 3152 CASE(MOUSE_FG); 3153 CASE(MOUSE_BG); 3154#if OPT_TEK4014 3155 CASE(TEK_FG); 3156 CASE(TEK_BG); 3157#endif 3158#if OPT_HIGHLIGHT_COLOR 3159 CASE(HIGHLIGHT_BG); 3160 CASE(HIGHLIGHT_FG); 3161#endif 3162#if OPT_TEK4014 3163 CASE(TEK_CURSOR); 3164#endif 3165 case OSC_NCOLORS: 3166 break; 3167 } 3168 return result; 3169} 3170 3171static Bool 3172ChangeColorsRequest(XtermWidget xw, 3173 int start, 3174 char *names, 3175 int final) 3176{ 3177 Bool result = False; 3178 char *thisName; 3179 ScrnColors newColors; 3180 int i, ndx; 3181 3182 TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names)); 3183 3184 if (GetOldColors(xw)) { 3185 newColors.which = 0; 3186 for (i = 0; i < NCOLORS; i++) { 3187 newColors.names[i] = NULL; 3188 } 3189 for (i = start; i < OSC_NCOLORS; i++) { 3190 ndx = OscToColorIndex((OscTextColors) i); 3191 if (xw->misc.re_verse) 3192 ndx = oppositeColor(ndx); 3193 3194 if (IsEmpty(names)) { 3195 newColors.names[ndx] = NULL; 3196 } else { 3197 if (names[0] == ';') 3198 thisName = NULL; 3199 else 3200 thisName = names; 3201 names = strchr(names, ';'); 3202 if (names != NULL) { 3203 *names++ = '\0'; 3204 } 3205 if (thisName != 0) { 3206 if (!strcmp(thisName, "?")) { 3207 ReportColorRequest(xw, ndx, final); 3208 } else if (!xw->work.oldColors->names[ndx] 3209 || strcmp(thisName, xw->work.oldColors->names[ndx])) { 3210 AllocateTermColor(xw, &newColors, ndx, thisName, False); 3211 } 3212 } 3213 } 3214 } 3215 3216 if (newColors.which != 0) { 3217 ChangeColors(xw, &newColors); 3218 UpdateOldColors(xw, &newColors); 3219 } 3220 result = True; 3221 } 3222 return result; 3223} 3224 3225static Bool 3226ResetColorsRequest(XtermWidget xw, 3227 int code) 3228{ 3229 Bool result = False; 3230#if OPT_COLOR_RES 3231 const char *thisName; 3232 ScrnColors newColors; 3233 int ndx; 3234#endif 3235 3236 TRACE(("ResetColorsRequest code=%d\n", code)); 3237 3238#if OPT_COLOR_RES 3239 if (GetOldColors(xw)) { 3240 ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET)); 3241 if (xw->misc.re_verse) 3242 ndx = oppositeColor(ndx); 3243 3244 thisName = xw->screen.Tcolors[ndx].resource; 3245 3246 newColors.which = 0; 3247 newColors.names[ndx] = NULL; 3248 3249 if (thisName != 0 3250 && xw->work.oldColors->names[ndx] != 0 3251 && strcmp(thisName, xw->work.oldColors->names[ndx])) { 3252 AllocateTermColor(xw, &newColors, ndx, thisName, False); 3253 3254 if (newColors.which != 0) { 3255 ChangeColors(xw, &newColors); 3256 UpdateOldColors(xw, &newColors); 3257 } 3258 } 3259 result = True; 3260 } 3261#endif 3262 return result; 3263} 3264 3265#if OPT_SHIFT_FONTS 3266/* 3267 * Initially, 'source' points to '#' or '?'. 3268 * 3269 * Look for an optional sign and optional number. If those are found, lookup 3270 * the corresponding menu font entry. 3271 */ 3272static int 3273ParseShiftedFont(XtermWidget xw, String source, String *target) 3274{ 3275 TScreen *screen = TScreenOf(xw); 3276 int num = screen->menu_font_number; 3277 int rel = 0; 3278 3279 if (*++source == '+') { 3280 rel = 1; 3281 source++; 3282 } else if (*source == '-') { 3283 rel = -1; 3284 source++; 3285 } 3286 3287 if (isdigit(CharOf(*source))) { 3288 int val = atoi(source); 3289 if (rel > 0) 3290 rel = val; 3291 else if (rel < 0) 3292 rel = -val; 3293 else 3294 num = val; 3295 } 3296 3297 if (rel != 0) { 3298 num = lookupRelativeFontSize(xw, 3299 screen->menu_font_number, rel); 3300 3301 } 3302 TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source)); 3303 *target = source; 3304 return num; 3305} 3306 3307static void 3308QueryFontRequest(XtermWidget xw, String buf, int final) 3309{ 3310 if (AllowFontOps(xw, efGetFont)) { 3311 TScreen *screen = TScreenOf(xw); 3312 Bool success = True; 3313 int num; 3314 String base = buf + 1; 3315 const char *name = 0; 3316 char temp[10]; 3317 3318 num = ParseShiftedFont(xw, buf, &buf); 3319 if (num < 0 3320 || num > fontMenu_lastBuiltin) { 3321 Bell(xw, XkbBI_MinorError, 0); 3322 success = False; 3323 } else { 3324#if OPT_RENDERFONT 3325 if (UsingRenderFont(xw)) { 3326 name = getFaceName(xw, False); 3327 } else 3328#endif 3329 if ((name = screen->MenuFontName(num)) == 0) { 3330 success = False; 3331 } 3332 } 3333 3334 unparseputc1(xw, ANSI_OSC); 3335 unparseputs(xw, "50"); 3336 3337 if (success) { 3338 unparseputc(xw, ';'); 3339 if (buf >= base) { 3340 /* identify the font-entry, unless it is the current one */ 3341 if (*buf != '\0') { 3342 unparseputc(xw, '#'); 3343 sprintf(temp, "%d", num); 3344 unparseputs(xw, temp); 3345 if (*name != '\0') 3346 unparseputc(xw, ' '); 3347 } 3348 } 3349 unparseputs(xw, name); 3350 } 3351 3352 unparseputc1(xw, final); 3353 unparse_end(xw); 3354 } 3355} 3356 3357static void 3358ChangeFontRequest(XtermWidget xw, String buf) 3359{ 3360 if (AllowFontOps(xw, efSetFont)) { 3361 TScreen *screen = TScreenOf(xw); 3362 Bool success = True; 3363 int num; 3364 VTFontNames fonts; 3365 char *name; 3366 3367 /* 3368 * If the font specification is a "#", followed by an optional sign and 3369 * optional number, lookup the corresponding menu font entry. 3370 * 3371 * Further, if the "#", etc., is followed by a font name, use that 3372 * to load the font entry. 3373 */ 3374 if (*buf == '#') { 3375 num = ParseShiftedFont(xw, buf, &buf); 3376 3377 if (num < 0 3378 || num > fontMenu_lastBuiltin) { 3379 Bell(xw, XkbBI_MinorError, 0); 3380 success = False; 3381 } else { 3382 /* 3383 * Skip past the optional number, and any whitespace to look 3384 * for a font specification within the control. 3385 */ 3386 while (isdigit(CharOf(*buf))) { 3387 ++buf; 3388 } 3389 while (isspace(CharOf(*buf))) { 3390 ++buf; 3391 } 3392#if OPT_RENDERFONT 3393 if (UsingRenderFont(xw)) { 3394 /* EMPTY */ 3395 /* there is only one font entry to load */ 3396 ; 3397 } else 3398#endif 3399 { 3400 /* 3401 * Normally there is no font specified in the control. 3402 * But if there is, simply overwrite the font entry. 3403 */ 3404 if (*buf == '\0') { 3405 if ((buf = screen->MenuFontName(num)) == 0) { 3406 success = False; 3407 } 3408 } 3409 } 3410 } 3411 } else { 3412 num = screen->menu_font_number; 3413 } 3414 name = x_strtrim(buf); 3415 if (success && !IsEmpty(name)) { 3416#if OPT_RENDERFONT 3417 if (UsingRenderFont(xw)) { 3418 setFaceName(xw, name); 3419 xtermUpdateFontInfo(xw, True); 3420 } else 3421#endif 3422 { 3423 memset(&fonts, 0, sizeof(fonts)); 3424 fonts.f_n = name; 3425 SetVTFont(xw, num, True, &fonts); 3426 } 3427 } else { 3428 Bell(xw, XkbBI_MinorError, 0); 3429 } 3430 free(name); 3431 } 3432} 3433#endif /* OPT_SHIFT_FONTS */ 3434 3435/***====================================================================***/ 3436 3437void 3438do_osc(XtermWidget xw, Char *oscbuf, size_t len, int final) 3439{ 3440 TScreen *screen = TScreenOf(xw); 3441 int mode; 3442 Char *cp; 3443 int state = 0; 3444 char *buf = 0; 3445 char temp[2]; 3446#if OPT_ISO_COLORS 3447 int ansi_colors = 0; 3448#endif 3449 Bool need_data = True; 3450 Bool optional_data = False; 3451 3452 TRACE(("do_osc %s\n", oscbuf)); 3453 3454 (void) screen; 3455 3456 /* 3457 * Lines should be of the form <OSC> number ; string <ST>, however 3458 * older xterms can accept <BEL> as a final character. We will respond 3459 * with the same final character as the application sends to make this 3460 * work better with shell scripts, which may have trouble reading an 3461 * <ESC><backslash>, which is the 7-bit equivalent to <ST>. 3462 */ 3463 mode = 0; 3464 for (cp = oscbuf; *cp != '\0'; cp++) { 3465 switch (state) { 3466 case 0: 3467 if (isdigit(*cp)) { 3468 mode = 10 * mode + (*cp - '0'); 3469 if (mode > 65535) { 3470 TRACE(("do_osc found unknown mode %d\n", mode)); 3471 return; 3472 } 3473 break; 3474 } 3475 /* FALLTHRU */ 3476 case 1: 3477 if (*cp != ';') { 3478 TRACE(("do_osc did not find semicolon offset %d\n", 3479 (int) (cp - oscbuf))); 3480 return; 3481 } 3482 state = 2; 3483 break; 3484 case 2: 3485 buf = (char *) cp; 3486 state = 3; 3487 /* FALLTHRU */ 3488 default: 3489 if (!xtermIsPrintable(xw, &cp, oscbuf + len)) { 3490 switch (mode) { 3491 case 0: 3492 case 1: 3493 case 2: 3494 break; 3495 default: 3496 TRACE(("do_osc found nonprinting char %02X offset %d\n", 3497 CharOf(*cp), 3498 (int) (cp - oscbuf))); 3499 return; 3500 } 3501 } 3502 } 3503 } 3504 3505 /* 3506 * Check if the palette changed and there are no more immediate changes 3507 * that could be deferred to the next repaint. 3508 */ 3509 if (xw->misc.palette_changed) { 3510 switch (mode) { 3511 case 3: /* change X property */ 3512 case 30: /* Konsole (unused) */ 3513 case 31: /* Konsole (unused) */ 3514 case 50: /* font operations */ 3515 case 51: /* Emacs (unused) */ 3516#if OPT_PASTE64 3517 case 52: /* selection data */ 3518#endif 3519 TRACE(("forced repaint after palette changed\n")); 3520 xw->misc.palette_changed = False; 3521 xtermRepaint(xw); 3522 break; 3523 } 3524 } 3525 3526 /* 3527 * Most OSC controls other than resets require data. Handle the others as 3528 * a special case. 3529 */ 3530 switch (mode) { 3531#if OPT_ISO_COLORS 3532 case OSC_Reset(4): 3533 case OSC_Reset(5): 3534 need_data = False; 3535 optional_data = True; 3536 break; 3537 case OSC_Reset(OSC_TEXT_FG): 3538 case OSC_Reset(OSC_TEXT_BG): 3539 case OSC_Reset(OSC_TEXT_CURSOR): 3540 case OSC_Reset(OSC_MOUSE_FG): 3541 case OSC_Reset(OSC_MOUSE_BG): 3542#if OPT_HIGHLIGHT_COLOR 3543 case OSC_Reset(OSC_HIGHLIGHT_BG): 3544 case OSC_Reset(OSC_HIGHLIGHT_FG): 3545#endif 3546#if OPT_TEK4014 3547 case OSC_Reset(OSC_TEK_FG): 3548 case OSC_Reset(OSC_TEK_BG): 3549 case OSC_Reset(OSC_TEK_CURSOR): 3550#endif 3551 need_data = False; 3552 break; 3553#endif 3554 default: 3555 break; 3556 } 3557 3558 /* 3559 * Check if we have data when we want, and not when we do not want it. 3560 * Either way, that is a malformed control sequence, and will be ignored. 3561 */ 3562 if (IsEmpty(buf)) { 3563 if (need_data) { 3564 TRACE(("do_osc found no data\n")); 3565 return; 3566 } 3567 temp[0] = '\0'; 3568 buf = temp; 3569 } else if (!need_data && !optional_data) { 3570 TRACE(("do_osc found unwanted data\n")); 3571 return; 3572 } 3573 3574 switch (mode) { 3575 case 0: /* new icon name and title */ 3576 ChangeIconName(xw, buf); 3577 ChangeTitle(xw, buf); 3578 break; 3579 3580 case 1: /* new icon name only */ 3581 ChangeIconName(xw, buf); 3582 break; 3583 3584 case 2: /* new title only */ 3585 ChangeTitle(xw, buf); 3586 break; 3587 3588#ifdef notdef 3589 case 3: /* change X property */ 3590 if (AllowWindowOps(xw, ewSetXprop)) 3591 ChangeXprop(buf); 3592 break; 3593#endif 3594#if OPT_ISO_COLORS 3595 case 5: 3596 ansi_colors = NUM_ANSI_COLORS; 3597 /* FALLTHRU */ 3598 case 4: 3599 if (ChangeAnsiColorRequest(xw, buf, ansi_colors, final)) 3600 xw->misc.palette_changed = True; 3601 break; 3602 case OSC_Reset(5): 3603 ansi_colors = NUM_ANSI_COLORS; 3604 /* FALLTHRU */ 3605 case OSC_Reset(4): 3606 if (ResetAnsiColorRequest(xw, buf, ansi_colors)) 3607 xw->misc.palette_changed = True; 3608 break; 3609#endif 3610 case OSC_TEXT_FG: 3611 case OSC_TEXT_BG: 3612 case OSC_TEXT_CURSOR: 3613 case OSC_MOUSE_FG: 3614 case OSC_MOUSE_BG: 3615#if OPT_HIGHLIGHT_COLOR 3616 case OSC_HIGHLIGHT_BG: 3617 case OSC_HIGHLIGHT_FG: 3618#endif 3619#if OPT_TEK4014 3620 case OSC_TEK_FG: 3621 case OSC_TEK_BG: 3622 case OSC_TEK_CURSOR: 3623#endif 3624 if (xw->misc.dynamicColors) { 3625 ChangeColorsRequest(xw, mode, buf, final); 3626 } 3627 break; 3628 case OSC_Reset(OSC_TEXT_FG): 3629 case OSC_Reset(OSC_TEXT_BG): 3630 case OSC_Reset(OSC_TEXT_CURSOR): 3631 case OSC_Reset(OSC_MOUSE_FG): 3632 case OSC_Reset(OSC_MOUSE_BG): 3633#if OPT_HIGHLIGHT_COLOR 3634 case OSC_Reset(OSC_HIGHLIGHT_BG): 3635 case OSC_Reset(OSC_HIGHLIGHT_FG): 3636#endif 3637#if OPT_TEK4014 3638 case OSC_Reset(OSC_TEK_FG): 3639 case OSC_Reset(OSC_TEK_BG): 3640 case OSC_Reset(OSC_TEK_CURSOR): 3641#endif 3642 if (xw->misc.dynamicColors) { 3643 ResetColorsRequest(xw, mode); 3644 } 3645 break; 3646 3647 case 30: 3648 case 31: 3649 /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ 3650 break; 3651 3652#ifdef ALLOWLOGGING 3653 case 46: /* new log file */ 3654#ifdef ALLOWLOGFILECHANGES 3655 /* 3656 * Warning, enabling this feature allows people to overwrite 3657 * arbitrary files accessible to the person running xterm. 3658 */ 3659 if (strcmp(buf, "?") 3660 && (cp = CastMallocN(char, strlen(buf)) != NULL)) { 3661 strcpy(cp, buf); 3662 if (screen->logfile) 3663 free(screen->logfile); 3664 screen->logfile = cp; 3665 break; 3666 } 3667#endif 3668 Bell(xw, XkbBI_Info, 0); 3669 Bell(xw, XkbBI_Info, 0); 3670 break; 3671#endif /* ALLOWLOGGING */ 3672 3673 case 50: 3674#if OPT_SHIFT_FONTS 3675 if (*buf == '?') { 3676 QueryFontRequest(xw, buf, final); 3677 } else if (xw->misc.shift_fonts) { 3678 ChangeFontRequest(xw, buf); 3679 } 3680#endif /* OPT_SHIFT_FONTS */ 3681 break; 3682 case 51: 3683 /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ 3684 break; 3685 3686#if OPT_PASTE64 3687 case 52: 3688 ManipulateSelectionData(xw, screen, buf, final); 3689 break; 3690#endif 3691 /* 3692 * One could write code to send back the display and host names, 3693 * but that could potentially open a fairly nasty security hole. 3694 */ 3695 default: 3696 TRACE(("do_osc - unrecognized code\n")); 3697 break; 3698 } 3699 unparse_end(xw); 3700} 3701 3702/* 3703 * Parse one nibble of a hex byte from the OSC string. We have removed the 3704 * string-terminator (replacing it with a null), so the only other delimiter 3705 * that is expected is semicolon. Ignore other characters (Ray Neuman says 3706 * "real" terminals accept commas in the string definitions). 3707 */ 3708static int 3709udk_value(const char **cp) 3710{ 3711 int result = -1; 3712 int c; 3713 3714 for (;;) { 3715 if ((c = **cp) != '\0') 3716 *cp = *cp + 1; 3717 if (c == ';' || c == '\0') 3718 break; 3719 if ((result = x_hex2int(c)) >= 0) 3720 break; 3721 } 3722 3723 return result; 3724} 3725 3726void 3727reset_decudk(XtermWidget xw) 3728{ 3729 int n; 3730 for (n = 0; n < MAX_UDK; n++) { 3731 if (xw->work.user_keys[n].str != 0) { 3732 free(xw->work.user_keys[n].str); 3733 xw->work.user_keys[n].str = 0; 3734 xw->work.user_keys[n].len = 0; 3735 } 3736 } 3737} 3738 3739/* 3740 * Parse the data for DECUDK (user-defined keys). 3741 */ 3742static void 3743parse_decudk(XtermWidget xw, const char *cp) 3744{ 3745 while (*cp) { 3746 const char *base = cp; 3747 char *str = CastMallocN(char, strlen(cp) + 2); 3748 unsigned key = 0; 3749 int lo, hi; 3750 int len = 0; 3751 3752 while (isdigit(CharOf(*cp))) 3753 key = (key * 10) + (unsigned) (*cp++ - '0'); 3754 if (*cp == '/') { 3755 cp++; 3756 while ((hi = udk_value(&cp)) >= 0 3757 && (lo = udk_value(&cp)) >= 0) { 3758 str[len++] = (char) ((hi << 4) | lo); 3759 } 3760 } 3761 if (len > 0 && key < MAX_UDK) { 3762 str[len] = '\0'; 3763 if (xw->work.user_keys[key].str != 0) 3764 free(xw->work.user_keys[key].str); 3765 xw->work.user_keys[key].str = str; 3766 xw->work.user_keys[key].len = len; 3767 } else { 3768 free(str); 3769 } 3770 if (*cp == ';') 3771 cp++; 3772 if (cp == base) /* badly-formed sequence - bail out */ 3773 break; 3774 } 3775} 3776 3777/* 3778 * Parse numeric parameters. Normally we use a state machine to simplify 3779 * interspersing with control characters, but have the string already. 3780 */ 3781static void 3782parse_ansi_params(ANSI *params, const char **string) 3783{ 3784 const char *cp = *string; 3785 ParmType nparam = 0; 3786 int last_empty = 1; 3787 3788 memset(params, 0, sizeof(*params)); 3789 while (*cp != '\0') { 3790 Char ch = CharOf(*cp++); 3791 3792 if (isdigit(ch)) { 3793 last_empty = 0; 3794 if (nparam < NPARAM) { 3795 params->a_param[nparam] = 3796 (ParmType) ((params->a_param[nparam] * 10) 3797 + (ch - '0')); 3798 } 3799 } else if (ch == ';') { 3800 last_empty = 1; 3801 nparam++; 3802 } else if (ch < 32) { 3803 /* EMPTY */ ; 3804 } else { 3805 /* should be 0x30 to 0x7e */ 3806 params->a_final = ch; 3807 break; 3808 } 3809 } 3810 3811 *string = cp; 3812 if (!last_empty) 3813 nparam++; 3814 if (nparam > NPARAM) 3815 params->a_nparam = NPARAM; 3816 else 3817 params->a_nparam = nparam; 3818} 3819 3820#if OPT_TRACE 3821#define SOFT_WIDE 10 3822#define SOFT_HIGH 20 3823 3824static void 3825parse_decdld(ANSI *params, const char *string) 3826{ 3827 char DscsName[8]; 3828 int len; 3829 int Pfn = params->a_param[0]; 3830 int Pcn = params->a_param[1]; 3831 int Pe = params->a_param[2]; 3832 int Pcmw = params->a_param[3]; 3833 int Pw = params->a_param[4]; 3834 int Pt = params->a_param[5]; 3835 int Pcmh = params->a_param[6]; 3836 int Pcss = params->a_param[7]; 3837 3838 int start_char = Pcn + 0x20; 3839 int char_wide = ((Pcmw == 0) 3840 ? (Pcss ? 6 : 10) 3841 : (Pcmw > 4 3842 ? Pcmw 3843 : (Pcmw + 3))); 3844 int char_high = ((Pcmh == 0) 3845 ? ((Pcmw >= 2 && Pcmw <= 4) 3846 ? 10 3847 : 20) 3848 : Pcmh); 3849 Char ch; 3850 Char bits[SOFT_HIGH][SOFT_WIDE]; 3851 Bool first = True; 3852 Bool prior = False; 3853 int row = 0, col = 0; 3854 3855 TRACE(("Parsing DECDLD\n")); 3856 TRACE((" font number %d\n", Pfn)); 3857 TRACE((" starting char %d\n", Pcn)); 3858 TRACE((" erase control %d\n", Pe)); 3859 TRACE((" char-width %d\n", Pcmw)); 3860 TRACE((" font-width %d\n", Pw)); 3861 TRACE((" text/full %d\n", Pt)); 3862 TRACE((" char-height %d\n", Pcmh)); 3863 TRACE((" charset-size %d\n", Pcss)); 3864 3865 if (Pfn > 1 3866 || Pcn > 95 3867 || Pe > 2 3868 || Pcmw > 10 3869 || Pcmw == 1 3870 || Pt > 2 3871 || Pcmh > 20 3872 || Pcss > 1 3873 || char_wide > SOFT_WIDE 3874 || char_high > SOFT_HIGH) { 3875 TRACE(("DECDLD illegal parameter\n")); 3876 return; 3877 } 3878 3879 len = 0; 3880 while (*string != '\0') { 3881 ch = CharOf(*string++); 3882 if (ch >= ANSI_SPA && ch <= 0x2f) { 3883 if (len < 2) 3884 DscsName[len++] = (char) ch; 3885 } else if (ch >= 0x30 && ch <= 0x7e) { 3886 DscsName[len++] = (char) ch; 3887 break; 3888 } 3889 } 3890 DscsName[len] = 0; 3891 TRACE((" Dscs name '%s'\n", DscsName)); 3892 3893 TRACE((" character matrix %dx%d\n", char_high, char_wide)); 3894 while (*string != '\0') { 3895 if (first) { 3896 TRACE(("Char %d:\n", start_char)); 3897 if (prior) { 3898 for (row = 0; row < char_high; ++row) { 3899 TRACE(("%.*s\n", char_wide, bits[row])); 3900 } 3901 } 3902 prior = False; 3903 first = False; 3904 for (row = 0; row < char_high; ++row) { 3905 for (col = 0; col < char_wide; ++col) { 3906 bits[row][col] = '.'; 3907 } 3908 } 3909 row = col = 0; 3910 } 3911 ch = CharOf(*string++); 3912 if (ch >= 0x3f && ch <= 0x7e) { 3913 int n; 3914 3915 ch = CharOf(ch - 0x3f); 3916 for (n = 0; n < 6; ++n) { 3917 bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.'); 3918 } 3919 col += 1; 3920 prior = True; 3921 } else if (ch == '/') { 3922 row += 6; 3923 col = 0; 3924 } else if (ch == ';') { 3925 first = True; 3926 ++start_char; 3927 } 3928 } 3929} 3930#else 3931#define parse_decdld(p,q) /* nothing */ 3932#endif 3933 3934void 3935do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen) 3936{ 3937 TScreen *screen = TScreenOf(xw); 3938 char reply[BUFSIZ]; 3939 const char *cp = (const char *) dcsbuf; 3940 Bool okay; 3941 ANSI params; 3942 3943 TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen)); 3944 3945 if (dcslen != strlen(cp)) 3946 /* shouldn't have nulls in the string */ 3947 return; 3948 3949 switch (*cp) { /* intermediate character, or parameter */ 3950 case '$': /* DECRQSS */ 3951 okay = True; 3952 3953 cp++; 3954 if (*cp++ == 'q') { 3955 if (!strcmp(cp, "\"q")) { /* DECSCA */ 3956 sprintf(reply, "%d%s", 3957 (screen->protected_mode == DEC_PROTECT) 3958 && (xw->flags & PROTECTED) ? 1 : 0, 3959 cp); 3960 } else if (!strcmp(cp, "\"p")) { /* DECSCL */ 3961 if (screen->vtXX_level < 2) { 3962 /* actually none of DECRQSS is valid for vt100's */ 3963 break; 3964 } 3965 sprintf(reply, "%d%s%s", 3966 (screen->vtXX_level ? 3967 screen->vtXX_level : 1) + 60, 3968 (screen->vtXX_level >= 2) 3969 ? (screen->control_eight_bits 3970 ? ";0" : ";1") 3971 : "", 3972 cp); 3973 } else if (!strcmp(cp, "r")) { /* DECSTBM */ 3974 sprintf(reply, "%d;%dr", 3975 screen->top_marg + 1, 3976 screen->bot_marg + 1); 3977 } else if (!strcmp(cp, "s")) { /* DECSLRM */ 3978 if (screen->vtXX_level >= 4) { /* VT420 */ 3979 sprintf(reply, "%d;%ds", 3980 screen->lft_marg + 1, 3981 screen->rgt_marg + 1); 3982 } 3983 } else if (!strcmp(cp, "m")) { /* SGR */ 3984 strcpy(reply, "0"); 3985 if (xw->flags & BOLD) 3986 strcat(reply, ";1"); 3987 if (xw->flags & UNDERLINE) 3988 strcat(reply, ";4"); 3989 if (xw->flags & BLINK) 3990 strcat(reply, ";5"); 3991 if (xw->flags & INVERSE) 3992 strcat(reply, ";7"); 3993 if (xw->flags & INVISIBLE) 3994 strcat(reply, ";8"); 3995#if OPT_256_COLORS || OPT_88_COLORS 3996 if_OPT_ISO_COLORS(screen, { 3997 if (xw->flags & FG_COLOR) { 3998 if (xw->cur_foreground >= 16) 3999 sprintf(reply + strlen(reply), 4000 ";38;5;%d", xw->cur_foreground); 4001 else 4002 sprintf(reply + strlen(reply), 4003 ";%d%d", 4004 xw->cur_foreground >= 8 ? 9 : 3, 4005 xw->cur_foreground >= 8 ? 4006 xw->cur_foreground - 8 : 4007 xw->cur_foreground); 4008 } 4009 if (xw->flags & BG_COLOR) { 4010 if (xw->cur_background >= 16) 4011 sprintf(reply + strlen(reply), 4012 ";48;5;%d", xw->cur_foreground); 4013 else 4014 sprintf(reply + strlen(reply), 4015 ";%d%d", 4016 xw->cur_background >= 8 ? 10 : 4, 4017 xw->cur_background >= 8 ? 4018 xw->cur_background - 8 : 4019 xw->cur_background); 4020 } 4021 }); 4022#elif OPT_ISO_COLORS 4023 if_OPT_ISO_COLORS(screen, { 4024 if (xw->flags & FG_COLOR) 4025 sprintf(reply + strlen(reply), 4026 ";%d%d", 4027 xw->cur_foreground >= 8 ? 9 : 3, 4028 xw->cur_foreground >= 8 ? 4029 xw->cur_foreground - 8 : 4030 xw->cur_foreground); 4031 if (xw->flags & BG_COLOR) 4032 sprintf(reply + strlen(reply), 4033 ";%d%d", 4034 xw->cur_background >= 8 ? 10 : 4, 4035 xw->cur_background >= 8 ? 4036 xw->cur_background - 8 : 4037 xw->cur_background); 4038 }); 4039#endif 4040 strcat(reply, "m"); 4041 } else if (!strcmp(cp, " q")) { /* DECSCUSR */ 4042 int code = STEADY_BLOCK; 4043 if (isCursorUnderline(screen)) 4044 code = STEADY_UNDERLINE; 4045 else if (isCursorBar(screen)) 4046 code = STEADY_BAR; 4047#if OPT_BLINK_CURS 4048 if (screen->cursor_blink_esc == 0) 4049 code -= 1; 4050#endif 4051 sprintf(reply, "%d%s", code, cp); 4052 } else 4053 okay = False; 4054 4055 if (okay) { 4056 unparseputc1(xw, ANSI_DCS); 4057 unparseputc(xw, '1'); 4058 unparseputc(xw, '$'); 4059 unparseputc(xw, 'r'); 4060 cp = reply; 4061 unparseputs(xw, cp); 4062 unparseputc1(xw, ANSI_ST); 4063 } else { 4064 unparseputc(xw, ANSI_CAN); 4065 } 4066 } else { 4067 unparseputc(xw, ANSI_CAN); 4068 } 4069 break; 4070#if OPT_TCAP_QUERY 4071 case '+': 4072 cp++; 4073 switch (*cp) { 4074 case 'p': 4075 if (AllowTcapOps(xw, etSetTcap)) { 4076 set_termcap(xw, cp + 1); 4077 } 4078 break; 4079 case 'q': 4080 if (AllowTcapOps(xw, etGetTcap)) { 4081 Bool fkey; 4082 unsigned state; 4083 int code; 4084 const char *tmp; 4085 const char *parsed = ++cp; 4086 4087 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4088 4089 unparseputc1(xw, ANSI_DCS); 4090 4091 unparseputc(xw, code >= 0 ? '1' : '0'); 4092 4093 unparseputc(xw, '+'); 4094 unparseputc(xw, 'r'); 4095 4096 while (*cp != 0 && (code >= -1)) { 4097 if (cp == parsed) 4098 break; /* no data found, error */ 4099 4100 for (tmp = cp; tmp != parsed; ++tmp) 4101 unparseputc(xw, *tmp); 4102 4103 if (code >= 0) { 4104 unparseputc(xw, '='); 4105 screen->tc_query_code = code; 4106 screen->tc_query_fkey = fkey; 4107#if OPT_ISO_COLORS 4108 /* XK_COLORS is a fake code for the "Co" entry (maximum 4109 * number of colors) */ 4110 if (code == XK_COLORS) { 4111 unparseputn(xw, NUM_ANSI_COLORS); 4112 } else 4113#endif 4114 if (code == XK_TCAPNAME) { 4115 unparseputs(xw, resource.term_name); 4116 } else { 4117 XKeyEvent event; 4118 event.state = state; 4119 Input(xw, &event, False); 4120 } 4121 screen->tc_query_code = -1; 4122 } else { 4123 break; /* no match found, error */ 4124 } 4125 4126 cp = parsed; 4127 if (*parsed == ';') { 4128 unparseputc(xw, *parsed++); 4129 cp = parsed; 4130 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4131 } 4132 } 4133 unparseputc1(xw, ANSI_ST); 4134 } 4135 break; 4136 } 4137 break; 4138#endif 4139 default: 4140 if (screen->terminal_id == 125 || 4141 screen->vtXX_level >= 2) { /* VT220 */ 4142 parse_ansi_params(¶ms, &cp); 4143 switch (params.a_final) { 4144 case 'p': 4145#if OPT_REGIS_GRAPHICS 4146 if (screen->terminal_id == 125 || 4147 screen->terminal_id == 240 || 4148 screen->terminal_id == 241 || 4149 screen->terminal_id == 330 || 4150 screen->terminal_id == 340) { 4151 parse_regis(xw, ¶ms, cp); 4152 } 4153#else 4154 TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n")); 4155#endif 4156 break; 4157 case 'q': 4158#if OPT_SIXEL_GRAPHICS 4159 if (screen->terminal_id == 125 || 4160 screen->terminal_id == 240 || 4161 screen->terminal_id == 241 || 4162 screen->terminal_id == 330 || 4163 screen->terminal_id == 340 || 4164 screen->terminal_id == 382) { 4165 parse_sixel(xw, ¶ms, cp); 4166 } 4167#else 4168 TRACE(("ignoring sixel graphic (compilation flag not enabled)\n")); 4169#endif 4170 break; 4171 case '|': /* DECUDK */ 4172 if (screen->vtXX_level >= 2) { /* VT220 */ 4173 if (params.a_param[0] == 0) 4174 reset_decudk(xw); 4175 parse_decudk(xw, cp); 4176 } 4177 break; 4178 case '{': /* DECDLD (no '}' case though) */ 4179 if (screen->vtXX_level >= 2) { /* VT220 */ 4180 parse_decdld(¶ms, cp); 4181 } 4182 break; 4183 } 4184 } 4185 break; 4186 } 4187 unparse_end(xw); 4188} 4189 4190#if OPT_DEC_RECTOPS 4191enum { 4192 mdUnknown = 0, 4193 mdMaybeSet = 1, 4194 mdMaybeReset = 2, 4195 mdAlwaysSet = 3, 4196 mdAlwaysReset = 4 4197}; 4198 4199#define MdBool(bool) ((bool) ? mdMaybeSet : mdMaybeReset) 4200#define MdFlag(mode,flag) MdBool((mode) & (flag)) 4201 4202/* 4203 * Reply is the same format as the query, with pair of mode/value: 4204 * 0 - not recognized 4205 * 1 - set 4206 * 2 - reset 4207 * 3 - permanently set 4208 * 4 - permanently reset 4209 * Only one mode can be reported at a time. 4210 */ 4211void 4212do_rpm(XtermWidget xw, int nparams, int *params) 4213{ 4214 ANSI reply; 4215 int result = 0; 4216 int count = 0; 4217 4218 TRACE(("do_rpm %d:%d\n", nparams, params[0])); 4219 memset(&reply, 0, sizeof(reply)); 4220 if (nparams >= 1) { 4221 switch (params[0]) { 4222 case 1: /* GATM */ 4223 result = mdAlwaysReset; 4224 break; 4225 case 2: 4226 result = MdFlag(xw->keyboard.flags, MODE_KAM); 4227 break; 4228 case 3: /* CRM */ 4229 result = mdMaybeReset; 4230 break; 4231 case 4: 4232 result = MdFlag(xw->flags, INSERT); 4233 break; 4234 case 5: /* SRTM */ 4235 case 7: /* VEM */ 4236 case 10: /* HEM */ 4237 case 11: /* PUM */ 4238 result = mdAlwaysReset; 4239 break; 4240 case 12: 4241 result = MdFlag(xw->keyboard.flags, MODE_SRM); 4242 break; 4243 case 13: /* FEAM */ 4244 case 14: /* FETM */ 4245 case 15: /* MATM */ 4246 case 16: /* TTM */ 4247 case 17: /* SATM */ 4248 case 18: /* TSM */ 4249 case 19: /* EBM */ 4250 result = mdAlwaysReset; 4251 break; 4252 case 20: 4253 result = MdFlag(xw->flags, LINEFEED); 4254 break; 4255 } 4256 reply.a_param[count++] = (ParmType) params[0]; 4257 reply.a_param[count++] = (ParmType) result; 4258 } 4259 reply.a_type = ANSI_CSI; 4260 reply.a_nparam = (ParmType) count; 4261 reply.a_inters = '$'; 4262 reply.a_final = 'y'; 4263 unparseseq(xw, &reply); 4264} 4265 4266void 4267do_decrpm(XtermWidget xw, int nparams, int *params) 4268{ 4269 ANSI reply; 4270 int result = 0; 4271 int count = 0; 4272 4273 TRACE(("do_decrpm %d:%d\n", nparams, params[0])); 4274 memset(&reply, 0, sizeof(reply)); 4275 if (nparams >= 1) { 4276 TScreen *screen = TScreenOf(xw); 4277 4278 switch (params[0]) { 4279 case srm_DECCKM: 4280 result = MdFlag(xw->keyboard.flags, MODE_DECCKM); 4281 break; 4282 case srm_DECANM: /* ANSI/VT52 mode */ 4283#if OPT_VT52_MODE 4284 result = MdBool(screen->vtXX_level >= 1); 4285#else 4286 result = mdMaybeSet; 4287#endif 4288 break; 4289 case srm_DECCOLM: 4290 result = MdFlag(xw->flags, IN132COLUMNS); 4291 break; 4292 case srm_DECSCLM: /* (slow scroll) */ 4293 result = MdFlag(xw->flags, SMOOTHSCROLL); 4294 break; 4295 case srm_DECSCNM: 4296 result = MdFlag(xw->flags, REVERSE_VIDEO); 4297 break; 4298 case srm_DECOM: 4299 result = MdFlag(xw->flags, ORIGIN); 4300 break; 4301 case srm_DECAWM: 4302 result = MdFlag(xw->flags, WRAPAROUND); 4303 break; 4304 case srm_DECARM: 4305 result = mdAlwaysReset; 4306 break; 4307 case srm_X10_MOUSE: /* X10 mouse */ 4308 result = MdBool(screen->send_mouse_pos == X10_MOUSE); 4309 break; 4310#if OPT_TOOLBAR 4311 case srm_RXVT_TOOLBAR: 4312 result = MdBool(resource.toolBar); 4313 break; 4314#endif 4315#if OPT_BLINK_CURS 4316 case srm_ATT610_BLINK: /* att610: Start/stop blinking cursor */ 4317 result = MdBool(screen->cursor_blink_res); 4318 break; 4319#endif 4320 case srm_DECPFF: /* print form feed */ 4321 result = MdBool(PrinterOf(screen).printer_formfeed); 4322 break; 4323 case srm_DECPEX: /* print extent */ 4324 result = MdBool(PrinterOf(screen).printer_extent); 4325 break; 4326 case srm_DECTCEM: /* Show/hide cursor (VT200) */ 4327 result = MdBool(screen->cursor_set); 4328 break; 4329 case srm_RXVT_SCROLLBAR: 4330 result = MdBool(screen->fullVwin.sb_info.width != OFF); 4331 break; 4332#if OPT_SHIFT_FONTS 4333 case srm_RXVT_FONTSIZE: 4334 result = MdBool(xw->misc.shift_fonts); 4335 break; 4336#endif 4337#if OPT_TEK4014 4338 case srm_DECTEK: 4339 result = MdBool(TEK4014_ACTIVE(xw)); 4340 break; 4341#endif 4342 case srm_132COLS: 4343 result = MdBool(screen->c132); 4344 break; 4345 case srm_CURSES_HACK: 4346 result = MdBool(screen->curses); 4347 break; 4348 case srm_DECNRCM: /* national charset (VT220) */ 4349 result = MdFlag(xw->flags, NATIONAL); 4350 break; 4351 case srm_MARGIN_BELL: /* margin bell */ 4352 result = MdBool(screen->marginbell); 4353 break; 4354 case srm_REVERSEWRAP: /* reverse wraparound */ 4355 result = MdFlag(xw->flags, REVERSEWRAP); 4356 break; 4357#ifdef ALLOWLOGGING 4358 case srm_ALLOWLOGGING: /* logging */ 4359#ifdef ALLOWLOGFILEONOFF 4360 result = MdBool(screen->logging); 4361#endif /* ALLOWLOGFILEONOFF */ 4362 break; 4363#endif 4364 case srm_OPT_ALTBUF_CURSOR: /* alternate buffer & cursor */ 4365 /* FALLTHRU */ 4366 case srm_OPT_ALTBUF: 4367 /* FALLTHRU */ 4368 case srm_ALTBUF: 4369 result = MdBool(screen->whichBuf); 4370 break; 4371 case srm_DECNKM: 4372 result = MdFlag(xw->keyboard.flags, MODE_DECKPAM); 4373 break; 4374 case srm_DECBKM: 4375 result = MdFlag(xw->keyboard.flags, MODE_DECBKM); 4376 break; 4377 case srm_DECLRMM: 4378 result = MdFlag(xw->flags, LEFT_RIGHT); 4379 break; 4380#if OPT_SIXEL_GRAPHICS 4381 case srm_DECSDM: 4382 result = MdFlag(xw->keyboard.flags, MODE_DECSDM); 4383 break; 4384#endif 4385 case srm_DECNCSM: 4386 result = MdFlag(xw->flags, NOCLEAR_COLM); 4387 break; 4388 case srm_VT200_MOUSE: /* xterm bogus sequence */ 4389 result = MdBool(screen->send_mouse_pos == VT200_MOUSE); 4390 break; 4391 case srm_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */ 4392 result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE); 4393 break; 4394 case srm_BTN_EVENT_MOUSE: 4395 result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE); 4396 break; 4397 case srm_ANY_EVENT_MOUSE: 4398 result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE); 4399 break; 4400#if OPT_FOCUS_EVENT 4401 case srm_FOCUS_EVENT_MOUSE: 4402 result = MdBool(screen->send_focus_pos); 4403 break; 4404#endif 4405 case srm_EXT_MODE_MOUSE: 4406 /* FALLTHRU */ 4407 case srm_SGR_EXT_MODE_MOUSE: 4408 /* FALLTHRU */ 4409 case srm_URXVT_EXT_MODE_MOUSE: 4410 result = MdBool(screen->extend_coords == params[0]); 4411 break; 4412 case srm_ALTERNATE_SCROLL: 4413 result = MdBool(screen->alternateScroll); 4414 break; 4415 case srm_RXVT_SCROLL_TTY_OUTPUT: 4416 result = MdBool(screen->scrollttyoutput); 4417 break; 4418 case srm_RXVT_SCROLL_TTY_KEYPRESS: 4419 result = MdBool(screen->scrollkey); 4420 break; 4421 case srm_EIGHT_BIT_META: 4422 result = MdBool(screen->eight_bit_meta); 4423 break; 4424#if OPT_NUM_LOCK 4425 case srm_REAL_NUMLOCK: 4426 result = MdBool(xw->misc.real_NumLock); 4427 break; 4428 case srm_META_SENDS_ESC: 4429 result = MdBool(screen->meta_sends_esc); 4430 break; 4431#endif 4432 case srm_DELETE_IS_DEL: 4433 result = MdBool(screen->delete_is_del); 4434 break; 4435#if OPT_NUM_LOCK 4436 case srm_ALT_SENDS_ESC: 4437 result = MdBool(screen->alt_sends_esc); 4438 break; 4439#endif 4440 case srm_KEEP_SELECTION: 4441 result = MdBool(screen->keepSelection); 4442 break; 4443 case srm_SELECT_TO_CLIPBOARD: 4444 result = MdBool(screen->selectToClipboard); 4445 break; 4446 case srm_BELL_IS_URGENT: 4447 result = MdBool(screen->bellIsUrgent); 4448 break; 4449 case srm_POP_ON_BELL: 4450 result = MdBool(screen->poponbell); 4451 break; 4452 case srm_TITE_INHIBIT: 4453 result = MdBool(screen->sc[screen->whichBuf].saved); 4454 break; 4455#if OPT_TCAP_FKEYS 4456 case srm_TCAP_FKEYS: 4457 result = MdBool(xw->keyboard.type == keyboardIsTermcap); 4458 break; 4459#endif 4460#if OPT_SUN_FUNC_KEYS 4461 case srm_SUN_FKEYS: 4462 result = MdBool(xw->keyboard.type == keyboardIsSun); 4463 break; 4464#endif 4465#if OPT_HP_FUNC_KEYS 4466 case srm_HP_FKEYS: 4467 result = MdBool(xw->keyboard.type == keyboardIsHP); 4468 break; 4469#endif 4470#if OPT_SCO_FUNC_KEYS 4471 case srm_SCO_FKEYS: 4472 result = MdBool(xw->keyboard.type == keyboardIsSCO); 4473 break; 4474#endif 4475 case srm_LEGACY_FKEYS: 4476 result = MdBool(xw->keyboard.type == keyboardIsLegacy); 4477 break; 4478#if OPT_SUNPC_KBD 4479 case srm_VT220_FKEYS: 4480 result = MdBool(xw->keyboard.type == keyboardIsVT220); 4481 break; 4482#endif 4483#if OPT_READLINE 4484 case srm_BUTTON1_MOVE_POINT: 4485 result = MdBool(screen->click1_moves); 4486 break; 4487 case srm_BUTTON2_MOVE_POINT: 4488 result = MdBool(screen->paste_moves); 4489 break; 4490 case srm_DBUTTON3_DELETE: 4491 result = MdBool(screen->dclick3_deletes); 4492 break; 4493 case srm_PASTE_IN_BRACKET: 4494 result = MdBool(screen->paste_brackets); 4495 break; 4496 case srm_PASTE_QUOTE: 4497 result = MdBool(screen->paste_quotes); 4498 break; 4499 case srm_PASTE_LITERAL_NL: 4500 result = MdBool(screen->paste_literal_nl); 4501 break; 4502#endif /* OPT_READLINE */ 4503#if OPT_SIXEL_GRAPHICS 4504 case srm_PRIVATE_COLOR_REGISTERS: 4505 result = MdBool(screen->privatecolorregisters); 4506 break; 4507#endif 4508#if OPT_SIXEL_GRAPHICS 4509 case srm_SIXEL_SCROLLS_RIGHT: 4510 result = MdBool(screen->sixel_scrolls_right); 4511 break; 4512#endif 4513 default: 4514 TRACE(("DATA_ERROR: requested report for unknown private mode %d\n", 4515 params[0])); 4516 } 4517 reply.a_param[count++] = (ParmType) params[0]; 4518 reply.a_param[count++] = (ParmType) result; 4519 } 4520 reply.a_type = ANSI_CSI; 4521 reply.a_pintro = '?'; 4522 reply.a_nparam = (ParmType) count; 4523 reply.a_inters = '$'; 4524 reply.a_final = 'y'; 4525 unparseseq(xw, &reply); 4526} 4527#endif /* OPT_DEC_RECTOPS */ 4528 4529char * 4530udk_lookup(XtermWidget xw, int keycode, int *len) 4531{ 4532 if (keycode >= 0 && keycode < MAX_UDK) { 4533 *len = xw->work.user_keys[keycode].len; 4534 return xw->work.user_keys[keycode].str; 4535 } 4536 return 0; 4537} 4538 4539#ifdef HAVE_LIBXPM 4540 4541#ifndef PIXMAP_ROOTDIR 4542#define PIXMAP_ROOTDIR "/usr/share/pixmaps/" 4543#endif 4544 4545typedef struct { 4546 const char *name; 4547 const char *const *data; 4548} XPM_DATA; 4549 4550static char * 4551x_find_icon(char **work, int *state, const char *suffix) 4552{ 4553 const char *filename = resource.icon_hint; 4554 const char *prefix = PIXMAP_ROOTDIR; 4555 const char *larger = "_48x48"; 4556 char *result = 0; 4557 size_t length; 4558 4559 if (*state >= 0) { 4560 if ((*state & 1) == 0) 4561 suffix = ""; 4562 if ((*state & 2) == 0) 4563 larger = ""; 4564 if ((*state & 4) == 0) { 4565 prefix = ""; 4566 } else if (!strncmp(filename, "/", (size_t) 1) || 4567 !strncmp(filename, "./", (size_t) 2) || 4568 !strncmp(filename, "../", (size_t) 3)) { 4569 *state = -1; 4570 } else if (*state >= 8) { 4571 *state = -1; 4572 } 4573 } 4574 4575 if (*state >= 0) { 4576 if (*work) { 4577 free(*work); 4578 *work = 0; 4579 } 4580 length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) + 4581 strlen(suffix); 4582 if ((result = malloc(length)) != 0) { 4583 sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix); 4584 *work = result; 4585 } 4586 *state += 1; 4587 TRACE(("x_find_icon %d:%s\n", *state, result)); 4588 } 4589 return result; 4590} 4591 4592#if OPT_BUILTIN_XPMS 4593static const XPM_DATA * 4594BuiltInXPM(const XPM_DATA * table, Cardinal length) 4595{ 4596 const char *find = resource.icon_hint; 4597 const XPM_DATA *result = 0; 4598 if (!IsEmpty(find)) { 4599 Cardinal n; 4600 for (n = 0; n < length; ++n) { 4601 if (!x_strcasecmp(find, table[n].name)) { 4602 result = table + n; 4603 break; 4604 } 4605 } 4606 4607 /* 4608 * As a fallback, check if the icon name matches without the lengths, 4609 * which are all _HHxWW format. 4610 */ 4611 if (result == 0) { 4612 const char *base = table[0].name; 4613 const char *last = strchr(base, '_'); 4614 if (last != 0 4615 && !x_strncasecmp(find, base, (unsigned) (last - base))) { 4616 result = table + length - 1; 4617 } 4618 } 4619 } 4620 return result; 4621} 4622#endif /* OPT_BUILTIN_XPMS */ 4623 4624typedef enum { 4625 eHintDefault = 0 /* use the largest builtin-icon */ 4626 ,eHintNone 4627 ,eHintSearch 4628} ICON_HINT; 4629 4630static ICON_HINT 4631which_icon_hint(void) 4632{ 4633 ICON_HINT result = eHintDefault; 4634 if (!IsEmpty(resource.icon_hint)) { 4635 if (!x_strcasecmp(resource.icon_hint, "none")) { 4636 result = eHintNone; 4637 } else { 4638 result = eHintSearch; 4639 } 4640 } 4641 return result; 4642} 4643#endif /* HAVE_LIBXPM */ 4644 4645int 4646getVisualDepth(XtermWidget xw) 4647{ 4648 int result = 0; 4649 4650 if (getVisualInfo(xw)) { 4651 result = xw->visInfo->depth; 4652 } 4653 return result; 4654} 4655 4656/* 4657 * WM_ICON_SIZE should be honored if possible. 4658 */ 4659void 4660xtermLoadIcon(XtermWidget xw) 4661{ 4662#ifdef HAVE_LIBXPM 4663 Display *dpy = XtDisplay(xw); 4664 Pixmap myIcon = 0; 4665 Pixmap myMask = 0; 4666 char *workname = 0; 4667 ICON_HINT hint = which_icon_hint(); 4668#include <builtin_icons.h> 4669 4670 TRACE(("xtermLoadIcon %p:%s\n", (void *) xw, NonNull(resource.icon_hint))); 4671 4672 if (hint == eHintSearch) { 4673 int state = 0; 4674 while (x_find_icon(&workname, &state, ".xpm") != 0) { 4675 Pixmap resIcon = 0; 4676 Pixmap shapemask = 0; 4677 XpmAttributes attributes; 4678 4679 attributes.depth = (unsigned) getVisualDepth(xw); 4680 attributes.valuemask = XpmDepth; 4681 4682 if (XpmReadFileToPixmap(dpy, 4683 DefaultRootWindow(dpy), 4684 workname, 4685 &resIcon, 4686 &shapemask, 4687 &attributes) == XpmSuccess) { 4688 myIcon = resIcon; 4689 myMask = shapemask; 4690 TRACE(("...success\n")); 4691 break; 4692 } 4693 } 4694 } 4695 4696 /* 4697 * If no external file was found, look for the name in the built-in table. 4698 * If that fails, just use the biggest mini-icon. 4699 */ 4700 if (myIcon == 0 && hint != eHintNone) { 4701 char **data; 4702#if OPT_BUILTIN_XPMS 4703 const XPM_DATA *myData = 0; 4704 myData = BuiltInXPM(mini_xterm_xpms, XtNumber(mini_xterm_xpms)); 4705 if (myData == 0) 4706 myData = BuiltInXPM(filled_xterm_xpms, XtNumber(filled_xterm_xpms)); 4707 if (myData == 0) 4708 myData = BuiltInXPM(xterm_color_xpms, XtNumber(xterm_color_xpms)); 4709 if (myData == 0) 4710 myData = BuiltInXPM(xterm_xpms, XtNumber(xterm_xpms)); 4711 if (myData == 0) 4712 myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1]; 4713 data = (char **) myData->data, 4714#else 4715 data = (char **) &mini_xterm_48x48_xpm; 4716#endif 4717 if (XpmCreatePixmapFromData(dpy, 4718 DefaultRootWindow(dpy), 4719 data, 4720 &myIcon, &myMask, 0) != 0) { 4721 myIcon = 0; 4722 myMask = 0; 4723 } 4724 } 4725 4726 if (myIcon != 0) { 4727 XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw)); 4728 if (!hints) 4729 hints = XAllocWMHints(); 4730 4731 if (hints) { 4732 hints->flags |= IconPixmapHint; 4733 hints->icon_pixmap = myIcon; 4734 if (myMask) { 4735 hints->flags |= IconMaskHint; 4736 hints->icon_mask = myMask; 4737 } 4738 4739 XSetWMHints(dpy, VShellWindow(xw), hints); 4740 XFree(hints); 4741 TRACE(("...loaded icon\n")); 4742 } 4743 } 4744 4745 if (workname != 0) 4746 free(workname); 4747 4748#else 4749 (void) xw; 4750#endif 4751} 4752 4753void 4754ChangeGroup(XtermWidget xw, const char *attribute, char *value) 4755{ 4756#if OPT_WIDE_CHARS 4757 static Char *converted; /* NO_LEAKS */ 4758#endif 4759 4760 Arg args[1]; 4761 Boolean changed = True; 4762 Widget w = CURRENT_EMU(); 4763 Widget top = SHELL_OF(w); 4764 4765 char *my_attr; 4766 char *name; 4767 size_t limit; 4768 Char *c1; 4769 Char *cp; 4770 4771 if (!AllowTitleOps(xw)) 4772 return; 4773 4774 if (value == 0) 4775 value = emptyString; 4776 if (IsTitleMode(xw, tmSetBase16)) { 4777 const char *temp; 4778 char *test; 4779 4780 value = x_decode_hex(value, &temp); 4781 if (*temp != '\0') { 4782 free(value); 4783 return; 4784 } 4785 for (test = value; *test != '\0'; ++test) { 4786 if (CharOf(*test) < 32) { 4787 *test = '\0'; 4788 break; 4789 } 4790 } 4791 } 4792 4793 c1 = (Char *) value; 4794 name = value; 4795 limit = strlen(name); 4796 my_attr = x_strdup(attribute); 4797 4798 TRACE(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, name)); 4799 4800 /* 4801 * Ignore titles that are too long to be plausible requests. 4802 */ 4803 if (limit > 0 && limit < 1024) { 4804 4805 /* 4806 * After all decoding, overwrite nonprintable characters with '?'. 4807 */ 4808 for (cp = c1; *cp != 0; ++cp) { 4809 Char *c2 = cp; 4810 if (!xtermIsPrintable(xw, &cp, c1 + limit)) { 4811 memset(c2, '?', (size_t) (cp + 1 - c2)); 4812 } 4813 } 4814 4815#if OPT_WIDE_CHARS 4816 /* 4817 * If we're running in UTF-8 mode, and have not been told that the 4818 * title string is in UTF-8, it is likely that non-ASCII text in the 4819 * string will be rejected because it is not printable in the current 4820 * locale. So we convert it to UTF-8, allowing the X library to 4821 * convert it back. 4822 */ 4823 if (xtermEnvUTF8() && !IsSetUtf8Title(xw)) { 4824 int n; 4825 4826 for (n = 0; name[n] != '\0'; ++n) { 4827 if (CharOf(name[n]) > 127) { 4828 if (converted != 0) 4829 free(converted); 4830 if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) { 4831 Char *temp = converted; 4832 while (*name != 0) { 4833 temp = convertToUTF8(temp, CharOf(*name)); 4834 ++name; 4835 } 4836 *temp = 0; 4837 name = (char *) converted; 4838 TRACE(("...converted{%s}\n", name)); 4839 } 4840 break; 4841 } 4842 } 4843 } 4844#endif 4845 4846#if OPT_SAME_NAME 4847 /* If the attribute isn't going to change, then don't bother... */ 4848 4849 if (resource.sameName) { 4850 char *buf = 0; 4851 XtSetArg(args[0], my_attr, &buf); 4852 XtGetValues(top, args, 1); 4853 TRACE(("...comparing{%s}\n", buf)); 4854 if (buf != 0 && strcmp(name, buf) == 0) 4855 changed = False; 4856 } 4857#endif /* OPT_SAME_NAME */ 4858 4859 if (changed) { 4860 TRACE(("...updating %s\n", my_attr)); 4861 TRACE(("...value is %s\n", name)); 4862 XtSetArg(args[0], my_attr, name); 4863 XtSetValues(top, args, 1); 4864 4865#if OPT_WIDE_CHARS 4866 if (xtermEnvUTF8()) { 4867 Display *dpy = XtDisplay(xw); 4868 Atom my_atom; 4869 4870 const char *propname = (!strcmp(my_attr, XtNtitle) 4871 ? "_NET_WM_NAME" 4872 : "_NET_WM_ICON_NAME"); 4873 if ((my_atom = XInternAtom(dpy, propname, False)) != None) { 4874 if (IsSetUtf8Title(xw)) { 4875 TRACE(("...updating %s\n", propname)); 4876 TRACE(("...value is %s\n", value)); 4877 XChangeProperty(dpy, VShellWindow(xw), my_atom, 4878 XA_UTF8_STRING(dpy), 8, 4879 PropModeReplace, 4880 (Char *) value, 4881 (int) strlen(value)); 4882 } else { 4883 TRACE(("...deleting %s\n", propname)); 4884 XDeleteProperty(dpy, VShellWindow(xw), my_atom); 4885 } 4886 } 4887 } 4888#endif 4889 } 4890 } 4891 if (IsTitleMode(xw, tmSetBase16)) { 4892 free(value); 4893 } 4894 free(my_attr); 4895 4896 return; 4897} 4898 4899void 4900ChangeIconName(XtermWidget xw, char *name) 4901{ 4902 if (name == 0) { 4903 name = emptyString; 4904 } 4905 if (!showZIconBeep(xw, name)) 4906 ChangeGroup(xw, XtNiconName, name); 4907} 4908 4909void 4910ChangeTitle(XtermWidget xw, char *name) 4911{ 4912 ChangeGroup(xw, XtNtitle, name); 4913} 4914 4915#define Strlen(s) strlen((const char *)(s)) 4916 4917void 4918ChangeXprop(char *buf) 4919{ 4920 Display *dpy = XtDisplay(toplevel); 4921 Window w = XtWindow(toplevel); 4922 XTextProperty text_prop; 4923 Atom aprop; 4924 Char *pchEndPropName = (Char *) strchr(buf, '='); 4925 4926 if (pchEndPropName) 4927 *pchEndPropName = '\0'; 4928 aprop = XInternAtom(dpy, buf, False); 4929 if (pchEndPropName == NULL) { 4930 /* no "=value" given, so delete the property */ 4931 XDeleteProperty(dpy, w, aprop); 4932 } else { 4933 text_prop.value = pchEndPropName + 1; 4934 text_prop.encoding = XA_STRING; 4935 text_prop.format = 8; 4936 text_prop.nitems = Strlen(text_prop.value); 4937 XSetTextProperty(dpy, w, &text_prop, aprop); 4938 } 4939} 4940 4941/***====================================================================***/ 4942 4943/* 4944 * This is part of ReverseVideo(). It reverses the data stored for the old 4945 * "dynamic" colors that might have been retrieved using OSC 10-18. 4946 */ 4947void 4948ReverseOldColors(XtermWidget xw) 4949{ 4950 ScrnColors *pOld = xw->work.oldColors; 4951 Pixel tmpPix; 4952 char *tmpName; 4953 4954 if (pOld) { 4955 /* change text cursor, if necesary */ 4956 if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { 4957 pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; 4958 if (pOld->names[TEXT_CURSOR]) { 4959 XtFree(xw->work.oldColors->names[TEXT_CURSOR]); 4960 pOld->names[TEXT_CURSOR] = NULL; 4961 } 4962 if (pOld->names[TEXT_BG]) { 4963 if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { 4964 pOld->names[TEXT_CURSOR] = tmpName; 4965 } 4966 } 4967 } 4968 4969 EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); 4970 EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); 4971 4972 EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); 4973 EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); 4974 4975#if OPT_TEK4014 4976 EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); 4977 EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); 4978#endif 4979 } 4980 return; 4981} 4982 4983Bool 4984AllocateTermColor(XtermWidget xw, 4985 ScrnColors * pNew, 4986 int ndx, 4987 const char *name, 4988 Bool always) 4989{ 4990 Bool result = False; 4991 4992 if (always || AllowColorOps(xw, ecSetColor)) { 4993 XColor def; 4994 char *newName; 4995 4996 result = True; 4997 if (!x_strcasecmp(name, XtDefaultForeground)) { 4998 def.pixel = xw->old_foreground; 4999 } else if (!x_strcasecmp(name, XtDefaultBackground)) { 5000 def.pixel = xw->old_background; 5001 } else if (!xtermAllocColor(xw, &def, name)) { 5002 result = False; 5003 } 5004 5005 if (result 5006 && (newName = x_strdup(name)) != 0) { 5007 if (COLOR_DEFINED(pNew, ndx)) { 5008 free(pNew->names[ndx]); 5009 } 5010 SET_COLOR_VALUE(pNew, ndx, def.pixel); 5011 SET_COLOR_NAME(pNew, ndx, newName); 5012 TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n", 5013 ndx, newName, def.pixel)); 5014 } else { 5015 TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); 5016 result = False; 5017 } 5018 } 5019 return result; 5020} 5021/***====================================================================***/ 5022 5023/* ARGSUSED */ 5024void 5025Panic(const char *s GCC_UNUSED, int a GCC_UNUSED) 5026{ 5027 if_DEBUG({ 5028 xtermWarning(s, a); 5029 }); 5030} 5031 5032const char * 5033SysErrorMsg(int code) 5034{ 5035 static char unknown[] = "unknown error"; 5036 char *s = strerror(code); 5037 return s ? s : unknown; 5038} 5039 5040const char * 5041SysReasonMsg(int code) 5042{ 5043 /* *INDENT-OFF* */ 5044 static const struct { 5045 int code; 5046 const char *name; 5047 } table[] = { 5048 { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" }, 5049 { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" }, 5050 { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", }, 5051 { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", }, 5052 { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", }, 5053 { ERROR_PTSNAME, "spawn: ptsname() failed", }, 5054 { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", }, 5055 { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" }, 5056 { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" }, 5057 { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" }, 5058 { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" }, 5059 { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" }, 5060 { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" }, 5061 { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" }, 5062 { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" }, 5063 { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" }, 5064 { ERROR_INIGROUPS, "spawn: initgroups() failed" }, 5065 { ERROR_FORK, "spawn: fork() failed" }, 5066 { ERROR_EXEC, "spawn: exec() failed" }, 5067 { ERROR_PTYS, "get_pty: not enough ptys" }, 5068 { ERROR_PTY_EXEC, "waiting for initial map" }, 5069 { ERROR_SETUID, "spawn: setuid() failed" }, 5070 { ERROR_INIT, "spawn: can't initialize window" }, 5071 { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" }, 5072 { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" }, 5073 { ERROR_LUMALLOC, "luit: command-line malloc failed" }, 5074 { ERROR_SELECT, "in_put: select() failed" }, 5075 { ERROR_VINIT, "VTInit: can't initialize window" }, 5076 { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" }, 5077 { ERROR_TSELECT, "Tinput: select() failed" }, 5078 { ERROR_TINIT, "TekInit: can't initialize window" }, 5079 { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" }, 5080 { ERROR_LOGEXEC, "StartLog: exec() failed" }, 5081 { ERROR_XERROR, "xerror: XError event" }, 5082 { ERROR_XIOERROR, "xioerror: X I/O error" }, 5083 { ERROR_SCALLOC, "Alloc: calloc() failed on base" }, 5084 { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" }, 5085 { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" }, 5086 }; 5087 /* *INDENT-ON* */ 5088 5089 Cardinal n; 5090 const char *result = "?"; 5091 5092 for (n = 0; n < XtNumber(table); ++n) { 5093 if (code == table[n].code) { 5094 result = table[n].name; 5095 break; 5096 } 5097 } 5098 return result; 5099} 5100 5101void 5102SysError(int code) 5103{ 5104 int oerrno = errno; 5105 5106 fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno); 5107 fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); 5108 fprintf(stderr, "Reason: %s\n", SysReasonMsg(code)); 5109 5110 Cleanup(code); 5111} 5112 5113void 5114NormalExit(void) 5115{ 5116 static Bool cleaning; 5117 5118 /* 5119 * Process "-hold" and session cleanup only for a normal exit. 5120 */ 5121 if (cleaning) { 5122 hold_screen = 0; 5123 return; 5124 } 5125 5126 cleaning = True; 5127 need_cleanup = False; 5128 5129 if (hold_screen) { 5130 hold_screen = 2; 5131 while (hold_screen) { 5132 xevents(); 5133 Sleep(10); 5134 } 5135 } 5136#if OPT_SESSION_MGT 5137 if (resource.sessionMgt) { 5138 XtVaSetValues(toplevel, 5139 XtNjoinSession, False, 5140 (void *) 0); 5141 } 5142#endif 5143 Cleanup(0); 5144} 5145 5146/* 5147 * cleanup by sending SIGHUP to client processes 5148 */ 5149void 5150Cleanup(int code) 5151{ 5152 TScreen *screen = TScreenOf(term); 5153 5154 TRACE(("Cleanup %d\n", code)); 5155 5156 if (screen->pid > 1) { 5157 (void) kill_process_group(screen->pid, SIGHUP); 5158 } 5159 Exit(code); 5160} 5161 5162#ifndef S_IXOTH 5163#define S_IXOTH 1 5164#endif 5165 5166Boolean 5167validProgram(const char *pathname) 5168{ 5169 Boolean result = False; 5170 struct stat sb; 5171 5172 if (!IsEmpty(pathname) 5173 && *pathname == '/' 5174 && strstr(pathname, "/..") == 0 5175 && stat(pathname, &sb) == 0 5176 && (sb.st_mode & S_IFMT) == S_IFREG 5177 && (sb.st_mode & S_IXOTH) != 0) { 5178 result = True; 5179 } 5180 return result; 5181} 5182 5183#ifndef VMS 5184#ifndef PATH_MAX 5185#define PATH_MAX 512 /* ... is not defined consistently in Xos.h */ 5186#endif 5187char * 5188xtermFindShell(char *leaf, Bool warning) 5189{ 5190 char *s0; 5191 char *s; 5192 char *d; 5193 char *tmp; 5194 char *result = leaf; 5195 Bool allocated = False; 5196 5197 TRACE(("xtermFindShell(%s)\n", leaf)); 5198 5199 if (!strncmp("./", result, (size_t) 2) 5200 || !strncmp("../", result, (size_t) 3)) { 5201 size_t need = PATH_MAX; 5202 size_t used = strlen(result) + 2; 5203 char *buffer = malloc(used + need); 5204 if (buffer != 0) { 5205 if (getcwd(buffer, need) != 0) { 5206 sprintf(buffer + strlen(buffer), "/%s", result); 5207 result = buffer; 5208 allocated = True; 5209 } else { 5210 free(buffer); 5211 } 5212 } 5213 } else if (*result != '\0' && strchr("+/-", *result) == 0) { 5214 /* find it in $PATH */ 5215 if ((s = s0 = x_getenv("PATH")) != 0) { 5216 if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) { 5217 Bool found = False; 5218 while (*s != '\0') { 5219 strcpy(tmp, s); 5220 for (d = tmp;; ++d) { 5221 if (*d == ':' || *d == '\0') { 5222 int skip = (*d != '\0'); 5223 *d = '/'; 5224 strcpy(d + 1, leaf); 5225 if (skip) 5226 ++d; 5227 s += (d - tmp); 5228 if (validProgram(tmp)) { 5229 result = x_strdup(tmp); 5230 found = True; 5231 allocated = True; 5232 } 5233 break; 5234 } 5235 } 5236 if (found) 5237 break; 5238 } 5239 free(tmp); 5240 } 5241 free(s0); 5242 } 5243 } 5244 TRACE(("...xtermFindShell(%s)\n", result)); 5245 if (!validProgram(result)) { 5246 if (warning) 5247 xtermWarning("No absolute path found for shell: %s\n", result); 5248 if (allocated) 5249 free(result); 5250 result = 0; 5251 } 5252 /* be consistent, so that caller can always free the result */ 5253 if (result != 0 && !allocated) 5254 result = x_strdup(result); 5255 return result; 5256} 5257#endif /* VMS */ 5258 5259#define ENV_HUNK(n) (unsigned) ((((n) + 1) | 31) + 1) 5260 5261/* 5262 * If we do not have unsetenv(), make consistent updates for environ[]. 5263 * This could happen on some older machines due to the uneven standardization 5264 * process for the two functions. 5265 * 5266 * That is, putenv() makes a copy of environ, and some implementations do not 5267 * update the environ pointer, so the fallback when unsetenv() is missing would 5268 * not work as intended. Likewise, the reverse could be true, i.e., unsetenv 5269 * could copy environ. 5270 */ 5271#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV) 5272#undef HAVE_PUTENV 5273#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV) 5274#undef HAVE_UNSETENV 5275#endif 5276 5277/* 5278 * copy the environment before Setenv'ing. 5279 */ 5280void 5281xtermCopyEnv(char **oldenv) 5282{ 5283#ifdef HAVE_PUTENV 5284 (void) oldenv; 5285#else 5286 unsigned size; 5287 char **newenv; 5288 5289 for (size = 0; oldenv[size] != NULL; size++) { 5290 ; 5291 } 5292 5293 newenv = TypeCallocN(char *, ENV_HUNK(size)); 5294 memmove(newenv, oldenv, size * sizeof(char *)); 5295 environ = newenv; 5296#endif 5297} 5298 5299#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV) 5300static int 5301findEnv(const char *var, int *lengthp) 5302{ 5303 char *test; 5304 int envindex = 0; 5305 size_t len = strlen(var); 5306 int found = -1; 5307 5308 TRACE(("findEnv(%s=..)\n", var)); 5309 5310 while ((test = environ[envindex]) != NULL) { 5311 if (strncmp(test, var, len) == 0 && test[len] == '=') { 5312 found = envindex; 5313 break; 5314 } 5315 envindex++; 5316 } 5317 *lengthp = envindex; 5318 return found; 5319} 5320#endif 5321 5322/* 5323 * sets the value of var to be arg in the Unix 4.2 BSD environment env. 5324 * Var should end with '=' (bindings are of the form "var=value"). 5325 * This procedure assumes the memory for the first level of environ 5326 * was allocated using calloc, with enough extra room at the end so not 5327 * to have to do a realloc(). 5328 */ 5329void 5330xtermSetenv(const char *var, const char *value) 5331{ 5332 if (value != 0) { 5333#ifdef HAVE_PUTENV 5334 char *both = malloc(2 + strlen(var) + strlen(value)); 5335 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 5336 if (both) { 5337 sprintf(both, "%s=%s", var, value); 5338 putenv(both); 5339 } 5340#else 5341 size_t len = strlen(var); 5342 int envindex; 5343 int found = findEnv(var, &envindex); 5344 5345 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 5346 5347 if (found < 0) { 5348 unsigned need = ENV_HUNK(envindex + 1); 5349 unsigned have = ENV_HUNK(envindex); 5350 5351 if (need > have) { 5352 char **newenv; 5353 newenv = TypeMallocN(char *, need); 5354 if (newenv == 0) { 5355 xtermWarning("Cannot increase environment\n"); 5356 return; 5357 } 5358 memmove(newenv, environ, have * sizeof(*newenv)); 5359 free(environ); 5360 environ = newenv; 5361 } 5362 5363 found = envindex; 5364 environ[found + 1] = NULL; 5365 environ = environ; 5366 } 5367 5368 environ[found] = CastMallocN(char, 1 + len + strlen(value)); 5369 if (environ[found] == 0) { 5370 xtermWarning("Cannot allocate environment %s\n", var); 5371 return; 5372 } 5373 sprintf(environ[found], "%s=%s", var, value); 5374#endif 5375 } 5376} 5377 5378void 5379xtermUnsetenv(const char *var) 5380{ 5381 TRACE(("xtermUnsetenv(%s)\n", var)); 5382#ifdef HAVE_UNSETENV 5383 unsetenv(var); 5384#else 5385 { 5386 int ignore; 5387 int item = findEnv(var, &ignore); 5388 if (item >= 0) { 5389 while ((environ[item] = environ[item + 1]) != 0) { 5390 ++item; 5391 } 5392 } 5393 } 5394#endif 5395} 5396 5397/*ARGSUSED*/ 5398int 5399xerror(Display *d, XErrorEvent *ev) 5400{ 5401 xtermWarning("warning, error event received:\n"); 5402 (void) XmuPrintDefaultErrorMessage(d, ev, stderr); 5403 Exit(ERROR_XERROR); 5404 return 0; /* appease the compiler */ 5405} 5406 5407void 5408ice_error(IceConn iceConn) 5409{ 5410 (void) iceConn; 5411 5412 xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n", 5413 (long) getpid(), errno); 5414 5415 Exit(ERROR_ICEERROR); 5416} 5417 5418/*ARGSUSED*/ 5419int 5420xioerror(Display *dpy) 5421{ 5422 int the_error = errno; 5423 5424 xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", 5425 the_error, SysErrorMsg(the_error), 5426 DisplayString(dpy)); 5427 5428 Exit(ERROR_XIOERROR); 5429 return 0; /* appease the compiler */ 5430} 5431 5432void 5433xt_error(String message) 5434{ 5435 xtermWarning("Xt error: %s\n", message); 5436 5437 /* 5438 * Check for the obvious - Xt does a poor job of reporting this. 5439 */ 5440 if (x_getenv("DISPLAY") == 0) { 5441 xtermWarning("DISPLAY is not set\n"); 5442 } 5443 exit(1); 5444} 5445 5446int 5447XStrCmp(char *s1, char *s2) 5448{ 5449 if (s1 && s2) 5450 return (strcmp(s1, s2)); 5451 if (s1 && *s1) 5452 return (1); 5453 if (s2 && *s2) 5454 return (-1); 5455 return (0); 5456} 5457 5458#if OPT_TEK4014 5459static void 5460withdraw_window(Display *dpy, Window w, int scr) 5461{ 5462 TRACE(("withdraw_window %#lx\n", (long) w)); 5463 (void) XmuUpdateMapHints(dpy, w, NULL); 5464 XWithdrawWindow(dpy, w, scr); 5465 return; 5466} 5467#endif 5468 5469void 5470set_vt_visibility(Bool on) 5471{ 5472 XtermWidget xw = term; 5473 TScreen *screen = TScreenOf(xw); 5474 5475 TRACE(("set_vt_visibility(%d)\n", on)); 5476 if (on) { 5477 if (!screen->Vshow && xw) { 5478 VTInit(xw); 5479 XtMapWidget(XtParent(xw)); 5480#if OPT_TOOLBAR 5481 /* we need both of these during initialization */ 5482 XtMapWidget(SHELL_OF(xw)); 5483 ShowToolbar(resource.toolBar); 5484#endif 5485 screen->Vshow = True; 5486 } 5487 } 5488#if OPT_TEK4014 5489 else { 5490 if (screen->Vshow && xw) { 5491 withdraw_window(XtDisplay(xw), 5492 VShellWindow(xw), 5493 XScreenNumberOfScreen(XtScreen(xw))); 5494 screen->Vshow = False; 5495 } 5496 } 5497 set_vthide_sensitivity(); 5498 set_tekhide_sensitivity(); 5499 update_vttekmode(); 5500 update_tekshow(); 5501 update_vtshow(); 5502#endif 5503 return; 5504} 5505 5506#if OPT_TEK4014 5507void 5508set_tek_visibility(Bool on) 5509{ 5510 TRACE(("set_tek_visibility(%d)\n", on)); 5511 5512 if (on) { 5513 if (!TEK4014_SHOWN(term)) { 5514 if (tekWidget == 0) { 5515 TekInit(); /* will exit on failure */ 5516 } 5517 if (tekWidget != 0) { 5518 Widget tekParent = SHELL_OF(tekWidget); 5519 XtRealizeWidget(tekParent); 5520 XtMapWidget(XtParent(tekWidget)); 5521#if OPT_TOOLBAR 5522 /* we need both of these during initialization */ 5523 XtMapWidget(tekParent); 5524 XtMapWidget(tekWidget); 5525#endif 5526 XtOverrideTranslations(tekParent, 5527 XtParseTranslationTable 5528 ("<Message>WM_PROTOCOLS: DeleteWindow()")); 5529 (void) XSetWMProtocols(XtDisplay(tekParent), 5530 XtWindow(tekParent), 5531 &wm_delete_window, 1); 5532 TEK4014_SHOWN(term) = True; 5533 } 5534 } 5535 } else { 5536 if (TEK4014_SHOWN(term) && tekWidget) { 5537 withdraw_window(XtDisplay(tekWidget), 5538 TShellWindow, 5539 XScreenNumberOfScreen(XtScreen(tekWidget))); 5540 TEK4014_SHOWN(term) = False; 5541 } 5542 } 5543 set_tekhide_sensitivity(); 5544 set_vthide_sensitivity(); 5545 update_vtshow(); 5546 update_tekshow(); 5547 update_vttekmode(); 5548 return; 5549} 5550 5551void 5552end_tek_mode(void) 5553{ 5554 XtermWidget xw = term; 5555 5556 if (TEK4014_ACTIVE(xw)) { 5557 FlushLog(xw); 5558 longjmp(Tekend, 1); 5559 } 5560 return; 5561} 5562 5563void 5564end_vt_mode(void) 5565{ 5566 XtermWidget xw = term; 5567 5568 if (!TEK4014_ACTIVE(xw)) { 5569 FlushLog(xw); 5570 TEK4014_ACTIVE(xw) = True; 5571 longjmp(VTend, 1); 5572 } 5573 return; 5574} 5575 5576void 5577switch_modes(Bool tovt) /* if true, then become vt mode */ 5578{ 5579 if (tovt) { 5580 if (tekRefreshList) 5581 TekRefresh(tekWidget); 5582 end_tek_mode(); /* WARNING: this does a longjmp... */ 5583 } else { 5584 end_vt_mode(); /* WARNING: this does a longjmp... */ 5585 } 5586} 5587 5588void 5589hide_vt_window(void) 5590{ 5591 set_vt_visibility(False); 5592 if (!TEK4014_ACTIVE(term)) 5593 switch_modes(False); /* switch to tek mode */ 5594} 5595 5596void 5597hide_tek_window(void) 5598{ 5599 set_tek_visibility(False); 5600 tekRefreshList = (TekLink *) 0; 5601 if (TEK4014_ACTIVE(term)) 5602 switch_modes(True); /* does longjmp to vt mode */ 5603} 5604#endif /* OPT_TEK4014 */ 5605 5606static const char * 5607skip_punct(const char *s) 5608{ 5609 while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { 5610 ++s; 5611 } 5612 return s; 5613} 5614 5615static int 5616cmp_options(const void *a, const void *b) 5617{ 5618 const char *s1 = skip_punct(((const OptionHelp *) a)->opt); 5619 const char *s2 = skip_punct(((const OptionHelp *) b)->opt); 5620 return strcmp(s1, s2); 5621} 5622 5623static int 5624cmp_resources(const void *a, const void *b) 5625{ 5626 return strcmp(((const XrmOptionDescRec *) a)->option, 5627 ((const XrmOptionDescRec *) b)->option); 5628} 5629 5630XrmOptionDescRec * 5631sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) 5632{ 5633 static XrmOptionDescRec *res_array = 0; 5634 5635#ifdef NO_LEAKS 5636 if (descs == 0) { 5637 if (res_array != 0) { 5638 free(res_array); 5639 res_array = 0; 5640 } 5641 } else 5642#endif 5643 if (res_array == 0) { 5644 Cardinal j; 5645 5646 /* make a sorted index to 'resources' */ 5647 res_array = TypeCallocN(XrmOptionDescRec, res_count); 5648 if (res_array != 0) { 5649 for (j = 0; j < res_count; j++) 5650 res_array[j] = descs[j]; 5651 qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources); 5652 } 5653 } 5654 return res_array; 5655} 5656 5657/* 5658 * The first time this is called, construct sorted index to the main program's 5659 * list of options, taking into account the on/off options which will be 5660 * compressed into one token. It's a lot simpler to do it this way than 5661 * maintain the list in sorted form with lots of ifdef's. 5662 */ 5663OptionHelp * 5664sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) 5665{ 5666 static OptionHelp *opt_array = 0; 5667 5668#ifdef NO_LEAKS 5669 if (descs == 0 && opt_array != 0) { 5670 sortedOptDescs(descs, numDescs); 5671 free(opt_array); 5672 opt_array = 0; 5673 return 0; 5674 } else if (options == 0 || descs == 0) { 5675 return 0; 5676 } 5677#endif 5678 5679 if (opt_array == 0) { 5680 size_t opt_count, j; 5681#if OPT_TRACE 5682 Cardinal k; 5683 XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); 5684 int code; 5685 const char *mesg; 5686#else 5687 (void) descs; 5688 (void) numDescs; 5689#endif 5690 5691 /* count 'options' and make a sorted index to it */ 5692 for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { 5693 ; 5694 } 5695 opt_array = TypeCallocN(OptionHelp, opt_count + 1); 5696 for (j = 0; j < opt_count; j++) 5697 opt_array[j] = options[j]; 5698 qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); 5699 5700 /* supply the "turn on/off" strings if needed */ 5701#if OPT_TRACE 5702 for (j = 0; j < opt_count; j++) { 5703 if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) { 5704 char temp[80]; 5705 const char *name = opt_array[j].opt + 3; 5706 for (k = 0; k < numDescs; ++k) { 5707 const char *value = res_array[k].value; 5708 if (res_array[k].option[0] == '-') { 5709 code = -1; 5710 } else if (res_array[k].option[0] == '+') { 5711 code = 1; 5712 } else { 5713 code = 0; 5714 } 5715 sprintf(temp, "%.*s", 5716 (int) sizeof(temp) - 2, 5717 opt_array[j].desc); 5718 if (x_strindex(temp, "inhibit") != 0) 5719 code = -code; 5720 if (code != 0 5721 && res_array[k].value != 0 5722 && !strcmp(name, res_array[k].option + 1)) { 5723 if (((code < 0) && !strcmp(value, "on")) 5724 || ((code > 0) && !strcmp(value, "off")) 5725 || ((code > 0) && !strcmp(value, "0"))) { 5726 mesg = "turn on/off"; 5727 } else { 5728 mesg = "turn off/on"; 5729 } 5730 if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) { 5731 if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) { 5732 char *s = CastMallocN(char, 5733 strlen(mesg) 5734 + 1 5735 + strlen(opt_array[j].desc)); 5736 if (s != 0) { 5737 sprintf(s, "%s %s", mesg, opt_array[j].desc); 5738 opt_array[j].desc = s; 5739 } 5740 } else { 5741 TRACE(("OOPS ")); 5742 } 5743 } 5744 TRACE(("%s: %s %s: %s (%s)\n", 5745 mesg, 5746 res_array[k].option, 5747 res_array[k].value, 5748 opt_array[j].opt, 5749 opt_array[j].desc)); 5750 break; 5751 } 5752 } 5753 } 5754 } 5755#endif 5756 } 5757 return opt_array; 5758} 5759 5760/* 5761 * Report the character-type locale that xterm was started in. 5762 */ 5763String 5764xtermEnvLocale(void) 5765{ 5766 static String result; 5767 5768 if (result == 0) { 5769 if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) { 5770 result = x_strdup("C"); 5771 } else { 5772 result = x_strdup(result); 5773 } 5774 TRACE(("xtermEnvLocale ->%s\n", result)); 5775 } 5776 return result; 5777} 5778 5779char * 5780xtermEnvEncoding(void) 5781{ 5782 static char *result; 5783 5784 if (result == 0) { 5785#ifdef HAVE_LANGINFO_CODESET 5786 result = nl_langinfo(CODESET); 5787#else 5788 char *locale = xtermEnvLocale(); 5789 if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { 5790 result = "ASCII"; 5791 } else { 5792 result = "ISO-8859-1"; 5793 } 5794#endif 5795 TRACE(("xtermEnvEncoding ->%s\n", result)); 5796 } 5797 return result; 5798} 5799 5800#if OPT_WIDE_CHARS 5801/* 5802 * Tell whether xterm was started in a locale that uses UTF-8 encoding for 5803 * characters. That environment is inherited by subprocesses and used in 5804 * various library calls. 5805 */ 5806Bool 5807xtermEnvUTF8(void) 5808{ 5809 static Bool init = False; 5810 static Bool result = False; 5811 5812 if (!init) { 5813 init = True; 5814#ifdef HAVE_LANGINFO_CODESET 5815 result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); 5816#else 5817 { 5818 char *locale = x_strdup(xtermEnvLocale()); 5819 int n; 5820 for (n = 0; locale[n] != 0; ++n) { 5821 locale[n] = x_toupper(locale[n]); 5822 } 5823 if (strstr(locale, "UTF-8") != 0) 5824 result = True; 5825 else if (strstr(locale, "UTF8") != 0) 5826 result = True; 5827 free(locale); 5828 } 5829#endif 5830 TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); 5831 } 5832 return result; 5833} 5834#endif /* OPT_WIDE_CHARS */ 5835 5836/* 5837 * Check if the current widget, or any parent, is the VT100 "xterm" widget. 5838 */ 5839XtermWidget 5840getXtermWidget(Widget w) 5841{ 5842 XtermWidget xw; 5843 5844 if (w == 0) { 5845 xw = (XtermWidget) CURRENT_EMU(); 5846 if (!IsXtermWidget(xw)) { 5847 xw = 0; 5848 } 5849 } else if (IsXtermWidget(w)) { 5850 xw = (XtermWidget) w; 5851 } else { 5852 xw = getXtermWidget(XtParent(w)); 5853 } 5854 TRACE2(("getXtermWidget %p -> %p\n", w, xw)); 5855 return xw; 5856} 5857 5858#if OPT_SESSION_MGT 5859static void 5860die_callback(Widget w GCC_UNUSED, 5861 XtPointer client_data GCC_UNUSED, 5862 XtPointer call_data GCC_UNUSED) 5863{ 5864 NormalExit(); 5865} 5866 5867static void 5868save_callback(Widget w GCC_UNUSED, 5869 XtPointer client_data GCC_UNUSED, 5870 XtPointer call_data) 5871{ 5872 XtCheckpointToken token = (XtCheckpointToken) call_data; 5873 /* we have nothing to save */ 5874 token->save_success = True; 5875} 5876 5877static void 5878icewatch(IceConn iceConn, 5879 IcePointer clientData GCC_UNUSED, 5880 Bool opening, 5881 IcePointer * watchData GCC_UNUSED) 5882{ 5883 if (opening) { 5884 ice_fd = IceConnectionNumber(iceConn); 5885 TRACE(("got IceConnectionNumber %d\n", ice_fd)); 5886 } else { 5887 ice_fd = -1; 5888 TRACE(("reset IceConnectionNumber\n")); 5889 } 5890} 5891 5892void 5893xtermOpenSession(void) 5894{ 5895 if (resource.sessionMgt) { 5896 TRACE(("Enabling session-management callbacks\n")); 5897 XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL); 5898 XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL); 5899 } 5900} 5901 5902void 5903xtermCloseSession(void) 5904{ 5905 IceRemoveConnectionWatch(icewatch, NULL); 5906} 5907#endif /* OPT_SESSION_MGT */ 5908 5909Widget 5910xtermOpenApplication(XtAppContext * app_context_return, 5911 String my_class, 5912 XrmOptionDescRec * options, 5913 Cardinal num_options, 5914 int *argc_in_out, 5915 String *argv_in_out, 5916 String *fallback_resources, 5917 WidgetClass widget_class, 5918 ArgList args, 5919 Cardinal num_args) 5920{ 5921 Widget result; 5922 5923 XtSetErrorHandler(xt_error); 5924#if OPT_SESSION_MGT 5925 result = XtOpenApplication(app_context_return, 5926 my_class, 5927 options, 5928 num_options, 5929 argc_in_out, 5930 argv_in_out, 5931 fallback_resources, 5932 widget_class, 5933 args, 5934 num_args); 5935 IceAddConnectionWatch(icewatch, NULL); 5936#else 5937 (void) widget_class; 5938 (void) args; 5939 (void) num_args; 5940 result = XtAppInitialize(app_context_return, 5941 my_class, 5942 options, 5943 num_options, 5944 argc_in_out, 5945 argv_in_out, 5946 fallback_resources, 5947 NULL, 0); 5948#endif /* OPT_SESSION_MGT */ 5949 XtSetErrorHandler((XtErrorHandler) 0); 5950 5951 return result; 5952} 5953 5954static int x11_errors; 5955 5956static int 5957catch_x11_error(Display *display, XErrorEvent *error_event) 5958{ 5959 (void) display; 5960 (void) error_event; 5961 ++x11_errors; 5962 return 0; 5963} 5964 5965Boolean 5966xtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs) 5967{ 5968 Boolean result = False; 5969 Status code; 5970 5971 memset(attrs, 0, sizeof(*attrs)); 5972 if (win != None) { 5973 XErrorHandler save = XSetErrorHandler(catch_x11_error); 5974 x11_errors = 0; 5975 code = XGetWindowAttributes(dpy, win, attrs); 5976 XSetErrorHandler(save); 5977 result = (Boolean) ((code != 0) && !x11_errors); 5978 if (result) { 5979 TRACE_WIN_ATTRS(attrs); 5980 } else { 5981 xtermWarning("invalid window-id %ld\n", (long) win); 5982 } 5983 } 5984 return result; 5985} 5986 5987Boolean 5988xtermGetWinProp(Display *display, 5989 Window win, 5990 Atom property, 5991 long long_offset, 5992 long long_length, 5993 Atom req_type, 5994 Atom *actual_type_return, 5995 int *actual_format_return, 5996 unsigned long *nitems_return, 5997 unsigned long *bytes_after_return, 5998 unsigned char **prop_return) 5999{ 6000 Boolean result = True; 6001 6002 if (win != None) { 6003 XErrorHandler save = XSetErrorHandler(catch_x11_error); 6004 x11_errors = 0; 6005 if (XGetWindowProperty(display, 6006 win, 6007 property, 6008 long_offset, 6009 long_length, 6010 False, 6011 req_type, 6012 actual_type_return, 6013 actual_format_return, 6014 nitems_return, 6015 bytes_after_return, 6016 prop_return) == Success 6017 && x11_errors == 0) { 6018 result = True; 6019 } 6020 XSetErrorHandler(save); 6021 } 6022 return result; 6023} 6024 6025void 6026xtermEmbedWindow(Window winToEmbedInto) 6027{ 6028 Display *dpy = XtDisplay(toplevel); 6029 XWindowAttributes attrs; 6030 6031 TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto)); 6032 if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) { 6033 XtermWidget xw = term; 6034 TScreen *screen = TScreenOf(xw); 6035 6036 XtRealizeWidget(toplevel); 6037 6038 TRACE(("...reparenting toplevel %#lx into %#lx\n", 6039 XtWindow(toplevel), 6040 winToEmbedInto)); 6041 XReparentWindow(dpy, 6042 XtWindow(toplevel), 6043 winToEmbedInto, 0, 0); 6044 6045 screen->embed_high = (Dimension) attrs.height; 6046 screen->embed_wide = (Dimension) attrs.width; 6047 } 6048} 6049