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