misc.c revision 636d5e9f
1/* $XTermId: misc.c,v 1.988 2021/06/07 23:19:42 tom Exp $ */ 2 3/* 4 * Copyright 1999-2020,2021 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#include <xterm_io.h> 59 60#include <sys/stat.h> 61#include <stdio.h> 62#include <stdarg.h> 63#include <signal.h> 64#include <ctype.h> 65#include <pwd.h> 66#include <sys/wait.h> 67 68#include <X11/keysym.h> 69#include <X11/Xatom.h> 70 71#include <X11/Xmu/Error.h> 72#include <X11/Xmu/SysUtil.h> 73#include <X11/Xmu/WinUtil.h> 74#include <X11/Xmu/Xmu.h> 75#if HAVE_X11_SUNKEYSYM_H 76#include <X11/Sunkeysym.h> 77#endif 78 79#ifdef HAVE_LIBXPM 80#include <X11/xpm.h> 81#endif 82 83#ifdef HAVE_LANGINFO_CODESET 84#include <langinfo.h> 85#endif 86 87#include <xutf8.h> 88 89#include <data.h> 90#include <error.h> 91#include <menu.h> 92#include <fontutils.h> 93#include <xstrings.h> 94#include <xtermcap.h> 95#include <VTparse.h> 96#include <graphics.h> 97#include <graphics_regis.h> 98#include <graphics_sixel.h> 99 100#include <assert.h> 101 102#ifdef VMS 103#define XTERM_VMS_LOGFILE "SYS$SCRATCH:XTERM_LOG.TXT" 104#ifdef ALLOWLOGFILEEXEC 105#undef ALLOWLOGFILEEXEC 106#endif 107#endif /* VMS */ 108 109#if USE_DOUBLE_BUFFER 110#include <X11/extensions/Xdbe.h> 111#endif 112 113#if OPT_WIDE_CHARS 114#include <wctype.h> 115#endif 116 117#if OPT_TEK4014 118#define OUR_EVENT(event,Type) \ 119 (event.type == Type && \ 120 (event.xcrossing.window == XtWindow(XtParent(xw)) || \ 121 (tekWidget && \ 122 event.xcrossing.window == XtWindow(XtParent(tekWidget))))) 123#else 124#define OUR_EVENT(event,Type) \ 125 (event.type == Type && \ 126 (event.xcrossing.window == XtWindow(XtParent(xw)))) 127#endif 128 129#define VB_DELAY screen->visualBellDelay 130#define EVENT_DELAY TScreenOf(term)->nextEventDelay 131 132static Boolean xtermAllocColor(XtermWidget, XColor *, const char *); 133static Cursor make_hidden_cursor(XtermWidget); 134 135static char emptyString[] = ""; 136 137#if OPT_EXEC_XTERM 138/* Like readlink(2), but returns a malloc()ed buffer, or NULL on 139 error; adapted from libc docs */ 140static char * 141Readlink(const char *filename) 142{ 143 char *buf = NULL; 144 size_t size = 100; 145 146 for (;;) { 147 int n; 148 char *tmp = TypeRealloc(char, size, buf); 149 if (tmp == NULL) { 150 free(buf); 151 return NULL; 152 } 153 buf = tmp; 154 memset(buf, 0, size); 155 156 n = (int) readlink(filename, buf, size); 157 if (n < 0) { 158 free(buf); 159 return NULL; 160 } 161 162 if ((unsigned) n < size) { 163 return buf; 164 } 165 166 size *= 2; 167 } 168} 169#endif /* OPT_EXEC_XTERM */ 170 171static void 172Sleep(int msec) 173{ 174 static struct timeval select_timeout; 175 176 select_timeout.tv_sec = 0; 177 select_timeout.tv_usec = msec * 1000; 178 select(0, 0, 0, 0, &select_timeout); 179} 180 181static void 182selectwindow(XtermWidget xw, int flag) 183{ 184 TScreen *screen = TScreenOf(xw); 185 186 TRACE(("selectwindow(%d) flag=%d\n", screen->select, flag)); 187 188#if OPT_TEK4014 189 if (TEK4014_ACTIVE(xw)) { 190 if (!Ttoggled) 191 TCursorToggle(tekWidget, TOGGLE); 192 screen->select |= flag; 193 if (!Ttoggled) 194 TCursorToggle(tekWidget, TOGGLE); 195 } else 196#endif 197 { 198#if OPT_INPUT_METHOD 199 TInput *input = lookupTInput(xw, (Widget) xw); 200 if (input && input->xic) 201 XSetICFocus(input->xic); 202#endif 203 204 if (screen->cursor_state && CursorMoved(screen)) 205 HideCursor(xw); 206 screen->select |= flag; 207 if (screen->cursor_state) 208 ShowCursor(xw); 209 } 210 GetScrollLock(screen); 211} 212 213static void 214unselectwindow(XtermWidget xw, int flag) 215{ 216 TScreen *screen = TScreenOf(xw); 217 218 TRACE(("unselectwindow(%d) flag=%d\n", screen->select, flag)); 219 220 if (screen->hide_pointer && screen->pointer_mode < pFocused) { 221 screen->hide_pointer = False; 222 xtermDisplayPointer(xw); 223 } 224 225 screen->select &= ~flag; 226 227 if (!screen->always_highlight) { 228#if OPT_TEK4014 229 if (TEK4014_ACTIVE(xw)) { 230 if (!Ttoggled) 231 TCursorToggle(tekWidget, TOGGLE); 232 if (!Ttoggled) 233 TCursorToggle(tekWidget, TOGGLE); 234 } else 235#endif 236 { 237#if OPT_INPUT_METHOD 238 TInput *input = lookupTInput(xw, (Widget) xw); 239 if (input && input->xic) 240 XUnsetICFocus(input->xic); 241#endif 242 243 if (screen->cursor_state && CursorMoved(screen)) 244 HideCursor(xw); 245 if (screen->cursor_state) 246 ShowCursor(xw); 247 } 248 } 249} 250 251static void 252DoSpecialEnterNotify(XtermWidget xw, XEnterWindowEvent *ev) 253{ 254 TScreen *screen = TScreenOf(xw); 255 256 TRACE(("DoSpecialEnterNotify(%d)\n", screen->select)); 257 TRACE_FOCUS(xw, ev); 258 if (((ev->detail) != NotifyInferior) && 259 ev->focus && 260 !(screen->select & FOCUS)) 261 selectwindow(xw, INWINDOW); 262} 263 264static void 265DoSpecialLeaveNotify(XtermWidget xw, XEnterWindowEvent *ev) 266{ 267 TScreen *screen = TScreenOf(xw); 268 269 TRACE(("DoSpecialLeaveNotify(%d)\n", screen->select)); 270 TRACE_FOCUS(xw, ev); 271 if (((ev->detail) != NotifyInferior) && 272 ev->focus && 273 !(screen->select & FOCUS)) 274 unselectwindow(xw, INWINDOW); 275} 276 277#ifndef XUrgencyHint 278#define XUrgencyHint (1L << 8) /* X11R5 does not define */ 279#endif 280 281static void 282setXUrgency(XtermWidget xw, Bool enable) 283{ 284 TScreen *screen = TScreenOf(xw); 285 286 if (screen->bellIsUrgent) { 287 XWMHints *h = XGetWMHints(screen->display, VShellWindow(xw)); 288 if (h != 0) { 289 if (enable && !(screen->select & FOCUS)) { 290 h->flags |= XUrgencyHint; 291 } else { 292 h->flags &= ~XUrgencyHint; 293 } 294 XSetWMHints(screen->display, VShellWindow(xw), h); 295 } 296 } 297} 298 299void 300do_xevents(XtermWidget xw) 301{ 302 TScreen *screen = TScreenOf(xw); 303 304 if (xtermAppPending() 305 || 306#if defined(VMS) || defined(__VMS) 307 screen->display->qlen > 0 308#else 309 GetBytesAvailable(ConnectionNumber(screen->display)) > 0 310#endif 311 ) 312 xevents(xw); 313} 314 315void 316xtermDisplayPointer(XtermWidget xw) 317{ 318 TScreen *screen = TScreenOf(xw); 319 320 if (screen->Vshow) { 321 if (screen->hide_pointer) { 322 TRACE(("Display text pointer (hidden)\n")); 323 XDefineCursor(screen->display, VWindow(screen), screen->hidden_cursor); 324 } else { 325 TRACE(("Display text pointer (visible)\n")); 326 recolor_cursor(screen, 327 screen->pointer_cursor, 328 T_COLOR(screen, MOUSE_FG), 329 T_COLOR(screen, MOUSE_BG)); 330 XDefineCursor(screen->display, VWindow(screen), screen->pointer_cursor); 331 } 332 } 333} 334 335void 336xtermShowPointer(XtermWidget xw, Bool enable) 337{ 338 static int tried = -1; 339 TScreen *screen = TScreenOf(xw); 340 341#if OPT_TEK4014 342 if (TEK4014_SHOWN(xw)) 343 enable = True; 344#endif 345 346 /* 347 * Whether we actually hide the pointer depends on the pointer-mode and 348 * the mouse-mode: 349 */ 350 if (!enable) { 351 switch (screen->pointer_mode) { 352 case pNever: 353 enable = True; 354 break; 355 case pNoMouse: 356 if (screen->send_mouse_pos != MOUSE_OFF) 357 enable = True; 358 break; 359 case pAlways: 360 case pFocused: 361 break; 362 } 363 } 364 365 if (enable) { 366 if (screen->hide_pointer) { 367 screen->hide_pointer = False; 368 xtermDisplayPointer(xw); 369 switch (screen->send_mouse_pos) { 370 case ANY_EVENT_MOUSE: 371 break; 372 default: 373 MotionOff(screen, xw); 374 break; 375 } 376 } 377 } else if (!(screen->hide_pointer) && (tried <= 0)) { 378 if (screen->hidden_cursor == 0) { 379 screen->hidden_cursor = make_hidden_cursor(xw); 380 } 381 if (screen->hidden_cursor == 0) { 382 tried = 1; 383 } else { 384 tried = 0; 385 screen->hide_pointer = True; 386 xtermDisplayPointer(xw); 387 MotionOn(screen, xw); 388 } 389 } 390} 391 392/* true if p contains q */ 393#define ExposeContains(p,q) \ 394 ((p)->y <= (q)->y \ 395 && (p)->x <= (q)->x \ 396 && ((p)->y + (p)->height) >= ((q)->y + (q)->height) \ 397 && ((p)->x + (p)->width) >= ((q)->x + (q)->width)) 398 399static XtInputMask 400mergeExposeEvents(XEvent *target) 401{ 402 XEvent next_event; 403 XExposeEvent *p; 404 405 XtAppNextEvent(app_con, target); 406 p = (XExposeEvent *) target; 407 408 while (XtAppPending(app_con) 409 && XtAppPeekEvent(app_con, &next_event) 410 && next_event.type == Expose) { 411 Boolean merge_this = False; 412 XExposeEvent *q = (XExposeEvent *) (&next_event); 413 414 XtAppNextEvent(app_con, &next_event); 415 TRACE_EVENT("pending", &next_event, (String *) 0, 0); 416 417 /* 418 * If either window is contained within the other, merge the events. 419 * The traces show that there are also cases where a full repaint of 420 * a window is broken into 3 or more rectangles, which do not arrive 421 * in the same instant. We could merge those if xterm were modified 422 * to skim several events ahead. 423 */ 424 if (p->window == q->window) { 425 if (ExposeContains(p, q)) { 426 TRACE(("pending Expose...merged forward\n")); 427 merge_this = True; 428 next_event = *target; 429 } else if (ExposeContains(q, p)) { 430 TRACE(("pending Expose...merged backward\n")); 431 merge_this = True; 432 } 433 } 434 if (!merge_this) { 435 XtDispatchEvent(target); 436 } 437 *target = next_event; 438 } 439 XtDispatchEvent(target); 440 return XtAppPending(app_con); 441} 442 443/* 444 * On entry, we have peeked at the event queue and see a configure-notify 445 * event. Remove that from the queue so we can look further. 446 * 447 * Then, as long as there is a configure-notify event in the queue, remove 448 * that. If the adjacent events are for different windows, process the older 449 * event and update the event used for comparing windows. If they are for the 450 * same window, only the newer event is of interest. 451 * 452 * Finally, process the (remaining) configure-notify event. 453 */ 454static XtInputMask 455mergeConfigureEvents(XEvent *target) 456{ 457 XEvent next_event; 458 XConfigureEvent *p; 459 460 XtAppNextEvent(app_con, target); 461 p = (XConfigureEvent *) target; 462 463 if (XtAppPending(app_con) 464 && XtAppPeekEvent(app_con, &next_event) 465 && next_event.type == ConfigureNotify) { 466 Boolean merge_this = False; 467 XConfigureEvent *q = (XConfigureEvent *) (&next_event); 468 469 XtAppNextEvent(app_con, &next_event); 470 TRACE_EVENT("pending", &next_event, (String *) 0, 0); 471 472 if (p->window == q->window) { 473 TRACE(("pending Configure...merged\n")); 474 merge_this = True; 475 } 476 if (!merge_this) { 477 TRACE(("pending Configure...skipped\n")); 478 XtDispatchEvent(target); 479 } 480 *target = next_event; 481 } 482 XtDispatchEvent(target); 483 return XtAppPending(app_con); 484} 485 486#define SAME(a,b,name) ((a)->xbutton.name == (b)->xbutton.name) 487#define SameButtonEvent(a,b) ( \ 488 SAME(a,b,type) && \ 489 SAME(a,b,serial) && \ 490 SAME(a,b,send_event) && \ 491 SAME(a,b,display) && \ 492 SAME(a,b,window) && \ 493 SAME(a,b,root) && \ 494 SAME(a,b,subwindow) && \ 495 SAME(a,b,time) && \ 496 SAME(a,b,x) && \ 497 SAME(a,b,y) && \ 498 SAME(a,b,x_root) && \ 499 SAME(a,b,y_root) && \ 500 SAME(a,b,state) && \ 501 SAME(a,b,button) && \ 502 SAME(a,b,same_screen)) 503 504/* 505 * Work around a bug in the X mouse code, which delivers duplicate events. 506 */ 507static XtInputMask 508mergeButtonEvents(XEvent *target) 509{ 510 XEvent next_event; 511 XButtonEvent *p; 512 513 XtAppNextEvent(app_con, target); 514 p = (XButtonEvent *) target; 515 516 if (XtAppPending(app_con) 517 && XtAppPeekEvent(app_con, &next_event) 518 && SameButtonEvent(target, &next_event)) { 519 Boolean merge_this = False; 520 XButtonEvent *q = (XButtonEvent *) (&next_event); 521 522 XtAppNextEvent(app_con, &next_event); 523 TRACE_EVENT("pending", &next_event, (String *) 0, 0); 524 525 if (p->window == q->window) { 526 TRACE(("pending ButtonEvent...merged\n")); 527 merge_this = True; 528 } 529 if (!merge_this) { 530 TRACE(("pending ButtonEvent...skipped\n")); 531 XtDispatchEvent(target); 532 } 533 *target = next_event; 534 } 535 XtDispatchEvent(target); 536 return XtAppPending(app_con); 537} 538 539/* 540 * Filter redundant Expose- and ConfigureNotify-events. This is limited to 541 * adjacent events because there could be other event-loop processing. Absent 542 * that limitation, it might be possible to scan ahead to find when the screen 543 * would be completely updated, skipping unnecessary re-repainting before that 544 * point. 545 * 546 * Note: all cases should allow doing XtAppNextEvent if result is true. 547 */ 548XtInputMask 549xtermAppPending(void) 550{ 551 XtInputMask result = XtAppPending(app_con); 552 XEvent this_event; 553 Boolean found = False; 554 555 while (result && XtAppPeekEvent(app_con, &this_event)) { 556 found = True; 557 TRACE_EVENT("pending", &this_event, (String *) 0, 0); 558 if (this_event.type == Expose) { 559 result = mergeExposeEvents(&this_event); 560 } else if (this_event.type == ConfigureNotify) { 561 result = mergeConfigureEvents(&this_event); 562 } else if (this_event.type == ButtonPress || 563 this_event.type == ButtonRelease) { 564 result = mergeButtonEvents(&this_event); 565 } else { 566 break; 567 } 568 } 569 570 /* 571 * With NetBSD, closing a shell results in closing the X input event 572 * stream, which interferes with the "-hold" option. Wait a short time in 573 * this case, to avoid max'ing the CPU. 574 */ 575 if (hold_screen && caught_intr && !found) { 576 Sleep(EVENT_DELAY); 577 } 578 return result; 579} 580 581void 582xevents(XtermWidget xw) 583{ 584 TScreen *screen = TScreenOf(xw); 585 XEvent event; 586 XtInputMask input_mask; 587 588 if (need_cleanup) 589 NormalExit(); 590 591 if (screen->scroll_amt) 592 FlushScroll(xw); 593 /* 594 * process timeouts, relying on the fact that XtAppProcessEvent 595 * will process the timeout and return without blockng on the 596 * XEvent queue. Other sources i.e., the pty are handled elsewhere 597 * with select(). 598 */ 599 while ((input_mask = xtermAppPending()) != 0) { 600 if (input_mask & XtIMTimer) 601 XtAppProcessEvent(app_con, (XtInputMask) XtIMTimer); 602#if OPT_SESSION_MGT 603 /* 604 * Session management events are alternative input events. Deal with 605 * them in the same way. 606 */ 607 else if (input_mask & XtIMAlternateInput) 608 XtAppProcessEvent(app_con, (XtInputMask) XtIMAlternateInput); 609#endif 610 else 611 break; 612 } 613 614 /* 615 * If there are no XEvents, don't wait around... 616 */ 617 if ((input_mask & XtIMXEvent) != XtIMXEvent) 618 return; 619 do { 620 /* 621 * This check makes xterm hang when in mouse hilite tracking mode. 622 * We simply ignore all events except for those not passed down to 623 * this function, e.g., those handled in in_put(). 624 */ 625 if (screen->waitingForTrackInfo) { 626 Sleep(EVENT_DELAY); 627 return; 628 } 629 XtAppNextEvent(app_con, &event); 630 /* 631 * Hack to get around problems with the toolkit throwing away 632 * eventing during the exclusive grab of the menu popup. By 633 * looking at the event ourselves we make sure that we can 634 * do the right thing. 635 */ 636 if (OUR_EVENT(event, EnterNotify)) { 637 DoSpecialEnterNotify(xw, &event.xcrossing); 638 } else if (OUR_EVENT(event, LeaveNotify)) { 639 DoSpecialLeaveNotify(xw, &event.xcrossing); 640 } else if (event.xany.type == MotionNotify 641 && event.xcrossing.window == XtWindow(xw)) { 642 switch (screen->send_mouse_pos) { 643 case ANY_EVENT_MOUSE: 644#if OPT_DEC_LOCATOR 645 case DEC_LOCATOR: 646#endif /* OPT_DEC_LOCATOR */ 647 SendMousePosition(xw, &event); 648 xtermShowPointer(xw, True); 649 continue; 650 case BTN_EVENT_MOUSE: 651 SendMousePosition(xw, &event); 652 xtermShowPointer(xw, True); 653 } 654 } 655 656 /* 657 * If the event is interesting (and not a keyboard event), turn the 658 * mouse pointer back on. 659 */ 660 if (screen->hide_pointer) { 661 if (screen->pointer_mode >= pFocused) { 662 switch (event.xany.type) { 663 case MotionNotify: 664 xtermShowPointer(xw, True); 665 break; 666 } 667 } else { 668 switch (event.xany.type) { 669 case KeyPress: 670 case KeyRelease: 671 case ButtonPress: 672 case ButtonRelease: 673 /* also these... */ 674 case Expose: 675 case GraphicsExpose: 676 case NoExpose: 677 case PropertyNotify: 678 case ClientMessage: 679 break; 680 default: 681 xtermShowPointer(xw, True); 682 break; 683 } 684 } 685 } 686 687 if (!event.xany.send_event || 688 screen->allowSendEvents || 689 ((event.xany.type != KeyPress) && 690 (event.xany.type != KeyRelease) && 691 (event.xany.type != ButtonPress) && 692 (event.xany.type != ButtonRelease))) { 693 694 if (event.xany.type == MappingNotify) { 695 XRefreshKeyboardMapping(&(event.xmapping)); 696 VTInitModifiers(xw); 697 } 698 XtDispatchEvent(&event); 699 } 700 } while (xtermAppPending() & XtIMXEvent); 701} 702 703static Cursor 704make_hidden_cursor(XtermWidget xw) 705{ 706 TScreen *screen = TScreenOf(xw); 707 Cursor c; 708 Display *dpy = screen->display; 709 XFontStruct *fn; 710 711 static XColor dummy; 712 713 /* 714 * Prefer nil2 (which is normally available) to "fixed" (which is supposed 715 * to be "always" available), since it's a smaller glyph in case the 716 * server insists on drawing _something_. 717 */ 718 TRACE(("Ask for nil2 font\n")); 719 if ((fn = xtermLoadQueryFont(xw, "nil2")) == 0) { 720 TRACE(("...Ask for fixed font\n")); 721 fn = xtermLoadQueryFont(xw, DEFFONT); 722 } 723 724 if (fn != None) { 725 /* a space character seems to work as a cursor (dots are not needed) */ 726 c = XCreateGlyphCursor(dpy, fn->fid, fn->fid, 'X', ' ', &dummy, &dummy); 727 XFreeFont(dpy, fn); 728 } else { 729 c = None; 730 } 731 TRACE(("XCreateGlyphCursor ->%#lx\n", c)); 732 return c; 733} 734 735/* 736 * Xlib uses Xcursor to customize cursor coloring, which interferes with 737 * xterm's pointerColor resource. Work around this by providing our own 738 * default theme. Testing seems to show that we only have to provide this 739 * until the window is initialized. 740 */ 741#ifdef HAVE_LIB_XCURSOR 742void 743init_colored_cursor(Display *dpy) 744{ 745 static const char theme[] = "index.theme"; 746 static const char pattern[] = "xtermXXXXXX"; 747 char *env = getenv("XCURSOR_THEME"); 748 749 xterm_cursor_theme = 0; 750 /* 751 * The environment variable overrides a (possible) resource Xcursor.theme 752 */ 753 if (IsEmpty(env)) { 754 env = XGetDefault(dpy, "Xcursor", "theme"); 755 TRACE(("XGetDefault Xcursor theme \"%s\"\n", NonNull(env))); 756 } else { 757 TRACE(("getenv(XCURSOR_THEME) \"%s\"\n", NonNull(env))); 758 } 759 760 /* 761 * If neither found, provide our own default theme. 762 */ 763 if (IsEmpty(env)) { 764 const char *tmp_dir; 765 char *filename; 766 size_t needed; 767 768 TRACE(("init_colored_cursor will make an empty Xcursor theme\n")); 769 770 if ((tmp_dir = getenv("TMPDIR")) == 0) { 771 tmp_dir = P_tmpdir; 772 } 773 needed = strlen(tmp_dir) + 4 + strlen(theme) + strlen(pattern); 774 if ((filename = malloc(needed)) != 0) { 775 sprintf(filename, "%s/%s", tmp_dir, pattern); 776 777#ifdef HAVE_MKDTEMP 778 xterm_cursor_theme = mkdtemp(filename); 779#else 780 if (mktemp(filename) != 0 781 && mkdir(filename, 0700) == 0) { 782 xterm_cursor_theme = filename; 783 } 784#endif 785 if (xterm_cursor_theme != filename) 786 free(filename); 787 /* 788 * Actually, Xcursor does what _we_ want just by steering its 789 * search path away from home. We are setting up the complete 790 * theme just in case the library ever acquires a maintainer. 791 */ 792 if (xterm_cursor_theme != 0) { 793 char *leaf = xterm_cursor_theme + strlen(xterm_cursor_theme); 794 FILE *fp; 795 796 strcat(leaf, "/"); 797 strcat(leaf, theme); 798 799 if ((fp = fopen(xterm_cursor_theme, "w")) != 0) { 800 fprintf(fp, "[Icon Theme]\n"); 801 fclose(fp); 802 *leaf = '\0'; 803 xtermSetenv("XCURSOR_PATH", xterm_cursor_theme); 804 *leaf = '/'; 805 806 TRACE(("...initialized xterm_cursor_theme \"%s\"\n", 807 xterm_cursor_theme)); 808 atexit(cleanup_colored_cursor); 809 } else { 810 FreeAndNull(xterm_cursor_theme); 811 } 812 } 813 } 814 } 815} 816#endif /* HAVE_LIB_XCURSOR */ 817 818/* 819 * Once done, discard the file and directory holding it. 820 */ 821void 822cleanup_colored_cursor(void) 823{ 824#ifdef HAVE_LIB_XCURSOR 825 if (xterm_cursor_theme != 0) { 826 char *my_path = getenv("XCURSOR_PATH"); 827 struct stat sb; 828 if (!IsEmpty(my_path) 829 && stat(my_path, &sb) == 0 830 && (sb.st_mode & S_IFMT) == S_IFDIR) { 831 unlink(xterm_cursor_theme); 832 rmdir(my_path); 833 } 834 FreeAndNull(xterm_cursor_theme); 835 } 836#endif /* HAVE_LIB_XCURSOR */ 837} 838 839Cursor 840make_colored_cursor(unsigned c_index, /* index into font */ 841 unsigned long fg, /* pixel value */ 842 unsigned long bg) /* pixel value */ 843{ 844 TScreen *screen = TScreenOf(term); 845 Cursor c = None; 846 Display *dpy = screen->display; 847 848 TRACE(("alternate cursor font is \"%s\"\n", screen->cursor_font_name)); 849 if (!IsEmpty(screen->cursor_font_name)) { 850 static XTermFonts myFont; 851 852 /* adapted from XCreateFontCursor(), which hardcodes the font name */ 853 TRACE(("loading cursor from alternate cursor font\n")); 854 myFont.fs = xtermLoadQueryFont(term, screen->cursor_font_name); 855 if (myFont.fs != NULL) { 856 if (!xtermMissingChar(c_index, &myFont) 857 && !xtermMissingChar(c_index + 1, &myFont)) { 858#define DATA(c) { 0UL, c, c, c, 0, 0 } 859 static XColor foreground = DATA(0); 860 static XColor background = DATA(65535); 861#undef DATA 862 863 /* 864 * Cursor fonts follow each shape glyph with a mask glyph; so 865 * that character position 0 contains a shape, 1 the mask for 866 * 0, 2 a shape, 3 a mask for 2, etc. <X11/cursorfont.h> 867 * contains defined names for each shape. 868 */ 869 c = XCreateGlyphCursor(dpy, 870 myFont.fs->fid, /* source_font */ 871 myFont.fs->fid, /* mask_font */ 872 c_index + 0, /* source_char */ 873 c_index + 1, /* mask_char */ 874 &foreground, 875 &background); 876 } 877 XFreeFont(dpy, myFont.fs); 878 } 879 if (c == None) { 880 xtermWarning("cannot load cursor %u from alternate cursor font \"%s\"\n", 881 c_index, screen->cursor_font_name); 882 } 883 } 884 if (c == None) 885 c = XCreateFontCursor(dpy, c_index); 886 887 if (c != None) { 888 recolor_cursor(screen, c, fg, bg); 889 } 890 return c; 891} 892 893/* adapted from <X11/cursorfont.h> */ 894static int 895LookupCursorShape(const char *name) 896{ 897#define DATA(name) { XC_##name, #name } 898 static struct { 899 int code; 900 const char name[25]; 901 } table[] = { 902 DATA(X_cursor), 903 DATA(arrow), 904 DATA(based_arrow_down), 905 DATA(based_arrow_up), 906 DATA(boat), 907 DATA(bogosity), 908 DATA(bottom_left_corner), 909 DATA(bottom_right_corner), 910 DATA(bottom_side), 911 DATA(bottom_tee), 912 DATA(box_spiral), 913 DATA(center_ptr), 914 DATA(circle), 915 DATA(clock), 916 DATA(coffee_mug), 917 DATA(cross), 918 DATA(cross_reverse), 919 DATA(crosshair), 920 DATA(diamond_cross), 921 DATA(dot), 922 DATA(dotbox), 923 DATA(double_arrow), 924 DATA(draft_large), 925 DATA(draft_small), 926 DATA(draped_box), 927 DATA(exchange), 928 DATA(fleur), 929 DATA(gobbler), 930 DATA(gumby), 931 DATA(hand1), 932 DATA(hand2), 933 DATA(heart), 934 DATA(icon), 935 DATA(iron_cross), 936 DATA(left_ptr), 937 DATA(left_side), 938 DATA(left_tee), 939 DATA(leftbutton), 940 DATA(ll_angle), 941 DATA(lr_angle), 942 DATA(man), 943 DATA(middlebutton), 944 DATA(mouse), 945 DATA(pencil), 946 DATA(pirate), 947 DATA(plus), 948 DATA(question_arrow), 949 DATA(right_ptr), 950 DATA(right_side), 951 DATA(right_tee), 952 DATA(rightbutton), 953 DATA(rtl_logo), 954 DATA(sailboat), 955 DATA(sb_down_arrow), 956 DATA(sb_h_double_arrow), 957 DATA(sb_left_arrow), 958 DATA(sb_right_arrow), 959 DATA(sb_up_arrow), 960 DATA(sb_v_double_arrow), 961 DATA(shuttle), 962 DATA(sizing), 963 DATA(spider), 964 DATA(spraycan), 965 DATA(star), 966 DATA(target), 967 DATA(tcross), 968 DATA(top_left_arrow), 969 DATA(top_left_corner), 970 DATA(top_right_corner), 971 DATA(top_side), 972 DATA(top_tee), 973 DATA(trek), 974 DATA(ul_angle), 975 DATA(umbrella), 976 DATA(ur_angle), 977 DATA(watch), 978 DATA(xterm), 979 }; 980#undef DATA 981 Cardinal j; 982 int result = -1; 983 if (!IsEmpty(name)) { 984 for (j = 0; j < XtNumber(table); ++j) { 985 if (!strcmp(name, table[j].name)) { 986 result = table[j].code; 987 break; 988 } 989 } 990 } 991 return result; 992} 993 994void 995xtermSetupPointer(XtermWidget xw, const char *theShape) 996{ 997 TScreen *screen = TScreenOf(xw); 998 unsigned shape = XC_xterm; 999 int other = LookupCursorShape(theShape); 1000 unsigned which; 1001 1002 if (other >= 0 && other < XC_num_glyphs) 1003 shape = (unsigned) other; 1004 1005 TRACE(("looked up shape index %d from shape name \"%s\"\n", other, 1006 NonNull(theShape))); 1007 1008 which = (unsigned) (shape / 2); 1009 if (xw->work.pointer_cursors[which] == None) { 1010 TRACE(("creating text pointer cursor from shape %d\n", shape)); 1011 xw->work.pointer_cursors[which] = 1012 make_colored_cursor(shape, 1013 T_COLOR(screen, MOUSE_FG), 1014 T_COLOR(screen, MOUSE_BG)); 1015 } else { 1016 TRACE(("updating text pointer cursor for shape %d\n", shape)); 1017 recolor_cursor(screen, 1018 screen->pointer_cursor, 1019 T_COLOR(screen, MOUSE_FG), 1020 T_COLOR(screen, MOUSE_BG)); 1021 } 1022 if (screen->pointer_cursor != xw->work.pointer_cursors[which]) { 1023 screen->pointer_cursor = xw->work.pointer_cursors[which]; 1024 TRACE(("defining text pointer cursor with shape %d\n", shape)); 1025 XDefineCursor(screen->display, VShellWindow(xw), screen->pointer_cursor); 1026 if (XtIsRealized((Widget) xw)) { 1027 /* briefly override pointerMode after changing the pointer */ 1028 if (screen->pointer_mode != pNever) 1029 screen->hide_pointer = True; 1030 xtermShowPointer(xw, True); 1031 } 1032 } 1033} 1034 1035/* ARGSUSED */ 1036void 1037HandleKeyPressed(Widget w GCC_UNUSED, 1038 XEvent *event, 1039 String *params GCC_UNUSED, 1040 Cardinal *nparams GCC_UNUSED) 1041{ 1042 TRACE(("Handle insert-seven-bit for %p\n", (void *) w)); 1043 Input(term, &event->xkey, False); 1044} 1045 1046/* ARGSUSED */ 1047void 1048HandleEightBitKeyPressed(Widget w GCC_UNUSED, 1049 XEvent *event, 1050 String *params GCC_UNUSED, 1051 Cardinal *nparams GCC_UNUSED) 1052{ 1053 TRACE(("Handle insert-eight-bit for %p\n", (void *) w)); 1054 Input(term, &event->xkey, True); 1055} 1056 1057/* ARGSUSED */ 1058void 1059HandleStringEvent(Widget w GCC_UNUSED, 1060 XEvent *event GCC_UNUSED, 1061 String *params, 1062 Cardinal *nparams) 1063{ 1064 1065 if (*nparams != 1) 1066 return; 1067 1068 if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') { 1069 const char *abcdef = "ABCDEF"; 1070 const char *xxxxxx; 1071 Char c; 1072 UString p; 1073 unsigned value = 0; 1074 1075 for (p = (UString) (*params + 2); (c = CharOf(x_toupper(*p))) != 1076 '\0'; p++) { 1077 value *= 16; 1078 if (c >= '0' && c <= '9') 1079 value += (unsigned) (c - '0'); 1080 else if ((xxxxxx = (strchr) (abcdef, c)) != 0) 1081 value += (unsigned) (xxxxxx - abcdef) + 10; 1082 else 1083 break; 1084 } 1085 if (c == '\0') { 1086 Char hexval[2]; 1087 hexval[0] = (Char) value; 1088 hexval[1] = 0; 1089 StringInput(term, hexval, (size_t) 1); 1090 } 1091 } else { 1092 StringInput(term, (const Char *) *params, strlen(*params)); 1093 } 1094} 1095 1096#if OPT_EXEC_XTERM 1097 1098#ifndef PROCFS_ROOT 1099#define PROCFS_ROOT "/proc" 1100#endif 1101 1102/* 1103 * Determine the current working directory of the child so that we can 1104 * spawn a new terminal in the same directory. 1105 * 1106 * If we cannot get the CWD of the child, just use our own. 1107 */ 1108char * 1109ProcGetCWD(pid_t pid) 1110{ 1111 char *child_cwd = NULL; 1112 1113 if (pid) { 1114 char child_cwd_link[sizeof(PROCFS_ROOT) + 80]; 1115 sprintf(child_cwd_link, PROCFS_ROOT "/%lu/cwd", (unsigned long) pid); 1116 child_cwd = Readlink(child_cwd_link); 1117 } 1118 return child_cwd; 1119} 1120 1121/* ARGSUSED */ 1122void 1123HandleSpawnTerminal(Widget w GCC_UNUSED, 1124 XEvent *event GCC_UNUSED, 1125 String *params, 1126 Cardinal *nparams) 1127{ 1128 TScreen *screen = TScreenOf(term); 1129 char *child_cwd = NULL; 1130 char *child_exe; 1131 pid_t pid; 1132 1133 /* 1134 * Try to find the actual program which is running in the child process. 1135 * This works for Linux. If we cannot find the program, fall back to the 1136 * xterm program (which is usually adequate). Give up if we are given only 1137 * a relative path to xterm, since that would not always match $PATH. 1138 */ 1139 child_exe = Readlink(PROCFS_ROOT "/self/exe"); 1140 if (!child_exe) { 1141 if (strncmp(ProgramName, "./", (size_t) 2) 1142 && strncmp(ProgramName, "../", (size_t) 3)) { 1143 child_exe = xtermFindShell(ProgramName, True); 1144 } else { 1145 xtermWarning("Cannot exec-xterm given \"%s\"\n", ProgramName); 1146 } 1147 if (child_exe == 0) 1148 return; 1149 } 1150 1151 child_cwd = ProcGetCWD(screen->pid); 1152 1153 /* The reaper will take care of cleaning up the child */ 1154 pid = fork(); 1155 if (pid == -1) { 1156 xtermWarning("Could not fork: %s\n", SysErrorMsg(errno)); 1157 } else if (!pid) { 1158 /* We are the child */ 1159 if (child_cwd) { 1160 IGNORE_RC(chdir(child_cwd)); /* We don't care if this fails */ 1161 } 1162 1163 if (setuid(screen->uid) == -1 1164 || setgid(screen->gid) == -1) { 1165 xtermWarning("Cannot reset uid/gid\n"); 1166 } else { 1167 unsigned myargc = *nparams + 1; 1168 char **myargv = TypeMallocN(char *, myargc + 1); 1169 1170 if (myargv != 0) { 1171 unsigned n = 0; 1172 1173 myargv[n++] = child_exe; 1174 1175 while (n < myargc) { 1176 myargv[n++] = (char *) *params++; 1177 } 1178 1179 myargv[n] = 0; 1180 execv(child_exe, myargv); 1181 } 1182 1183 /* If we get here, we've failed */ 1184 xtermWarning("exec of '%s': %s\n", child_exe, SysErrorMsg(errno)); 1185 } 1186 _exit(0); 1187 } 1188 1189 /* We are the parent; clean up */ 1190 free(child_cwd); 1191 free(child_exe); 1192} 1193#endif /* OPT_EXEC_XTERM */ 1194 1195/* 1196 * Rather than sending characters to the host, put them directly into our 1197 * input queue. That lets a user have access to any of the control sequences 1198 * for a key binding. This is the equivalent of local function key support. 1199 * 1200 * NOTE: This code does not support the hexadecimal kludge used in 1201 * HandleStringEvent because it prevents us from sending an arbitrary string 1202 * (but it appears in a lot of examples - so we are stuck with it). The 1203 * standard string converter does recognize "\" for newline ("\n") and for 1204 * octal constants (e.g., "\007" for BEL). So we assume the user can make do 1205 * without a specialized converter. (Don't try to use \000, though). 1206 */ 1207/* ARGSUSED */ 1208void 1209HandleInterpret(Widget w GCC_UNUSED, 1210 XEvent *event GCC_UNUSED, 1211 String *params, 1212 Cardinal *param_count) 1213{ 1214 if (*param_count == 1) { 1215 const char *value = params[0]; 1216 int need = (int) strlen(value); 1217 int used = (int) (VTbuffer->next - VTbuffer->buffer); 1218 int have = (int) (VTbuffer->last - VTbuffer->buffer); 1219 1220 if (have - used + need < BUF_SIZE) { 1221 1222 fillPtyData(term, VTbuffer, value, (int) strlen(value)); 1223 1224 TRACE(("Interpret %s\n", value)); 1225 VTbuffer->update++; 1226 } 1227 } 1228} 1229 1230/*ARGSUSED*/ 1231void 1232HandleEnterWindow(Widget w GCC_UNUSED, 1233 XtPointer eventdata GCC_UNUSED, 1234 XEvent *event GCC_UNUSED, 1235 Boolean *cont GCC_UNUSED) 1236{ 1237 /* NOP since we handled it above */ 1238 TRACE(("HandleEnterWindow ignored\n")); 1239 TRACE_FOCUS(w, event); 1240} 1241 1242/*ARGSUSED*/ 1243void 1244HandleLeaveWindow(Widget w GCC_UNUSED, 1245 XtPointer eventdata GCC_UNUSED, 1246 XEvent *event GCC_UNUSED, 1247 Boolean *cont GCC_UNUSED) 1248{ 1249 /* NOP since we handled it above */ 1250 TRACE(("HandleLeaveWindow ignored\n")); 1251 TRACE_FOCUS(w, event); 1252} 1253 1254/*ARGSUSED*/ 1255void 1256HandleFocusChange(Widget w GCC_UNUSED, 1257 XtPointer eventdata GCC_UNUSED, 1258 XEvent *ev, 1259 Boolean *cont GCC_UNUSED) 1260{ 1261 XFocusChangeEvent *event = (XFocusChangeEvent *) ev; 1262 XtermWidget xw = term; 1263 TScreen *screen = TScreenOf(xw); 1264 1265 TRACE(("HandleFocusChange type=%s, mode=%s, detail=%s\n", 1266 visibleEventType(event->type), 1267 visibleNotifyMode(event->mode), 1268 visibleNotifyDetail(event->detail))); 1269 TRACE_FOCUS(xw, event); 1270 1271 if (screen->quiet_grab 1272 && (event->mode == NotifyGrab || event->mode == NotifyUngrab)) { 1273 /* EMPTY */ ; 1274 } else if (event->type == FocusIn) { 1275 if (event->detail != NotifyPointer) { 1276 setXUrgency(xw, False); 1277 } 1278 1279 /* 1280 * NotifyNonlinear only happens (on FocusIn) if the pointer was not in 1281 * one of our windows. Use this to reset a case where one xterm is 1282 * partly obscuring another, and X gets (us) confused about whether the 1283 * pointer was in the window. In particular, this can happen if the 1284 * user is resizing the obscuring window, causing some events to not be 1285 * delivered to the obscured window. 1286 */ 1287 if (event->detail == NotifyNonlinear 1288 && (screen->select & INWINDOW) != 0) { 1289 unselectwindow(xw, INWINDOW); 1290 } 1291 selectwindow(xw, 1292 ((event->detail == NotifyPointer) 1293 ? INWINDOW 1294 : FOCUS)); 1295 SendFocusButton(xw, event); 1296 } else { 1297#if OPT_FOCUS_EVENT 1298 if (event->type == FocusOut) { 1299 SendFocusButton(xw, event); 1300 } 1301#endif 1302 /* 1303 * XGrabKeyboard() will generate NotifyGrab event that we want to 1304 * ignore. 1305 */ 1306 if (event->mode != NotifyGrab) { 1307 unselectwindow(xw, 1308 ((event->detail == NotifyPointer) 1309 ? INWINDOW 1310 : FOCUS)); 1311 } 1312 if (screen->grabbedKbd && (event->mode == NotifyUngrab)) { 1313 Bell(xw, XkbBI_Info, 100); 1314 ReverseVideo(xw); 1315 screen->grabbedKbd = False; 1316 update_securekbd(); 1317 } 1318 } 1319} 1320 1321static long lastBellTime; /* in milliseconds */ 1322 1323#if defined(HAVE_XKB_BELL_EXT) 1324static Atom 1325AtomBell(XtermWidget xw, int which) 1326{ 1327#define DATA(name) { XkbBI_##name, XkbBN_##name } 1328 static struct { 1329 int value; 1330 const char *name; 1331 } table[] = { 1332 DATA(Info), 1333 DATA(MarginBell), 1334 DATA(MinorError), 1335 DATA(TerminalBell) 1336 }; 1337#undef DATA 1338 Cardinal n; 1339 Atom result = None; 1340 1341 for (n = 0; n < XtNumber(table); ++n) { 1342 if (table[n].value == which) { 1343 result = XInternAtom(XtDisplay(xw), table[n].name, False); 1344 break; 1345 } 1346 } 1347 return result; 1348} 1349#endif 1350 1351void 1352xtermBell(XtermWidget xw, int which, int percent) 1353{ 1354 TScreen *screen = TScreenOf(xw); 1355#if defined(HAVE_XKB_BELL_EXT) 1356 Atom tony = AtomBell(xw, which); 1357#endif 1358 1359 switch (which) { 1360 case XkbBI_Info: 1361 case XkbBI_MinorError: 1362 case XkbBI_MajorError: 1363 case XkbBI_TerminalBell: 1364 switch (screen->warningVolume) { 1365 case bvOff: 1366 percent = -100; 1367 break; 1368 case bvLow: 1369 break; 1370 case bvHigh: 1371 percent = 100; 1372 break; 1373 } 1374 break; 1375 case XkbBI_MarginBell: 1376 switch (screen->marginVolume) { 1377 case bvOff: 1378 percent = -100; 1379 break; 1380 case bvLow: 1381 break; 1382 case bvHigh: 1383 percent = 100; 1384 break; 1385 } 1386 break; 1387 default: 1388 break; 1389 } 1390 1391#if defined(HAVE_XKB_BELL_EXT) 1392 if (tony != None) { 1393 XkbBell(screen->display, VShellWindow(xw), percent, tony); 1394 } else 1395#endif 1396 XBell(screen->display, percent); 1397} 1398 1399void 1400Bell(XtermWidget xw, int which, int percent) 1401{ 1402 TScreen *screen = TScreenOf(xw); 1403 struct timeval curtime; 1404 1405 TRACE(("BELL %d %d%%\n", which, percent)); 1406 if (!XtIsRealized((Widget) xw)) { 1407 return; 1408 } 1409 1410 setXUrgency(xw, True); 1411 1412 /* has enough time gone by that we are allowed to ring 1413 the bell again? */ 1414 if (screen->bellSuppressTime) { 1415 long now_msecs; 1416 1417 if (screen->bellInProgress) { 1418 do_xevents(xw); 1419 if (screen->bellInProgress) { /* even after new events? */ 1420 return; 1421 } 1422 } 1423 X_GETTIMEOFDAY(&curtime); 1424 now_msecs = 1000 * curtime.tv_sec + curtime.tv_usec / 1000; 1425 if (lastBellTime != 0 && now_msecs - lastBellTime >= 0 && 1426 now_msecs - lastBellTime < screen->bellSuppressTime) { 1427 return; 1428 } 1429 lastBellTime = now_msecs; 1430 } 1431 1432 if (screen->visualbell) { 1433 VisualBell(); 1434 } else { 1435 xtermBell(xw, which, percent); 1436 } 1437 1438 if (screen->poponbell) 1439 XRaiseWindow(screen->display, VShellWindow(xw)); 1440 1441 if (screen->bellSuppressTime) { 1442 /* now we change a property and wait for the notify event to come 1443 back. If the server is suspending operations while the bell 1444 is being emitted (problematic for audio bell), this lets us 1445 know when the previous bell has finished */ 1446 Widget w = CURRENT_EMU(); 1447 XChangeProperty(XtDisplay(w), XtWindow(w), 1448 XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0); 1449 screen->bellInProgress = True; 1450 } 1451} 1452 1453static void 1454flashWindow(TScreen *screen, Window window, GC visualGC, unsigned width, unsigned height) 1455{ 1456 int y = 0; 1457 int x = 0; 1458 1459 if (screen->flash_line) { 1460 y = CursorY(screen, screen->cur_row); 1461 height = (unsigned) FontHeight(screen); 1462 } 1463 XFillRectangle(screen->display, window, visualGC, x, y, width, height); 1464 XFlush(screen->display); 1465 Sleep(VB_DELAY); 1466 XFillRectangle(screen->display, window, visualGC, x, y, width, height); 1467} 1468 1469void 1470VisualBell(void) 1471{ 1472 XtermWidget xw = term; 1473 TScreen *screen = TScreenOf(xw); 1474 1475 if (VB_DELAY > 0) { 1476 Pixel xorPixel = (T_COLOR(screen, TEXT_FG) ^ 1477 T_COLOR(screen, TEXT_BG)); 1478 XGCValues gcval; 1479 GC visualGC; 1480 1481 gcval.function = GXxor; 1482 gcval.foreground = xorPixel; 1483 visualGC = XtGetGC((Widget) xw, GCFunction + GCForeground, &gcval); 1484#if OPT_TEK4014 1485 if (TEK4014_ACTIVE(xw)) { 1486 TekScreen *tekscr = TekScreenOf(tekWidget); 1487 flashWindow(screen, TWindow(tekscr), visualGC, 1488 TFullWidth(tekscr), 1489 TFullHeight(tekscr)); 1490 } else 1491#endif 1492 { 1493 flashWindow(screen, VWindow(screen), visualGC, 1494 FullWidth(screen), 1495 FullHeight(screen)); 1496 } 1497 XtReleaseGC((Widget) xw, visualGC); 1498 } 1499} 1500 1501/* ARGSUSED */ 1502void 1503HandleBellPropertyChange(Widget w GCC_UNUSED, 1504 XtPointer data GCC_UNUSED, 1505 XEvent *ev, 1506 Boolean *more GCC_UNUSED) 1507{ 1508 TScreen *screen = TScreenOf(term); 1509 1510 if (ev->xproperty.atom == XA_NOTICE) { 1511 screen->bellInProgress = False; 1512 } 1513} 1514 1515void 1516xtermWarning(const char *fmt, ...) 1517{ 1518 int save_err = errno; 1519 va_list ap; 1520 1521 fflush(stdout); 1522 1523#if OPT_TRACE 1524 va_start(ap, fmt); 1525 Trace("xtermWarning: "); 1526 TraceVA(fmt, ap); 1527 va_end(ap); 1528#endif 1529 1530 fprintf(stderr, "%s: ", ProgramName); 1531 va_start(ap, fmt); 1532 vfprintf(stderr, fmt, ap); 1533 (void) fflush(stderr); 1534 1535 va_end(ap); 1536 errno = save_err; 1537} 1538 1539void 1540xtermPerror(const char *fmt, ...) 1541{ 1542 int save_err = errno; 1543 const char *msg = strerror(errno); 1544 va_list ap; 1545 1546 fflush(stdout); 1547 1548#if OPT_TRACE 1549 va_start(ap, fmt); 1550 Trace("xtermPerror: "); 1551 TraceVA(fmt, ap); 1552 va_end(ap); 1553#endif 1554 1555 fprintf(stderr, "%s: ", ProgramName); 1556 va_start(ap, fmt); 1557 vfprintf(stderr, fmt, ap); 1558 fprintf(stderr, ": %s\n", msg); 1559 (void) fflush(stderr); 1560 1561 va_end(ap); 1562 errno = save_err; 1563} 1564 1565Window 1566WMFrameWindow(XtermWidget xw) 1567{ 1568 Window win_root, win_current, *children; 1569 Window win_parent = 0; 1570 unsigned int nchildren; 1571 1572 win_current = XtWindow(xw); 1573 1574 /* find the parent which is child of root */ 1575 do { 1576 if (win_parent) 1577 win_current = win_parent; 1578 XQueryTree(TScreenOf(xw)->display, 1579 win_current, 1580 &win_root, 1581 &win_parent, 1582 &children, 1583 &nchildren); 1584 XFree(children); 1585 } while (win_root != win_parent); 1586 1587 return win_current; 1588} 1589 1590#if OPT_DABBREV 1591/* 1592 * The following code implements `dynamic abbreviation' expansion a la 1593 * Emacs. It looks in the preceding visible screen and its scrollback 1594 * to find expansions of a typed word. It compares consecutive 1595 * expansions and ignores one of them if they are identical. 1596 * (Tomasz J. Cholewo, t.cholewo@ieee.org) 1597 */ 1598 1599#define IS_WORD_CONSTITUENT(x) ((x) != ' ' && (x) != '\0') 1600 1601static int 1602dabbrev_prev_char(TScreen *screen, CELL *cell, LineData **ld) 1603{ 1604 int result = -1; 1605 int firstLine = -(screen->savedlines); 1606 1607 *ld = getLineData(screen, cell->row); 1608 while (cell->row >= firstLine) { 1609 if (--(cell->col) >= 0) { 1610 result = (int) (*ld)->charData[cell->col]; 1611 break; 1612 } 1613 if (--(cell->row) < firstLine) 1614 break; /* ...there is no previous line */ 1615 *ld = getLineData(screen, cell->row); 1616 cell->col = MaxCols(screen); 1617 if (!LineTstWrapped(*ld)) { 1618 result = ' '; /* treat lines as separate */ 1619 break; 1620 } 1621 } 1622 return result; 1623} 1624 1625static char * 1626dabbrev_prev_word(XtermWidget xw, CELL *cell, LineData **ld) 1627{ 1628 TScreen *screen = TScreenOf(xw); 1629 char *abword; 1630 int c; 1631 char *ab_end = (xw->work.dabbrev_data + MAX_DABBREV - 1); 1632 char *result = 0; 1633 1634 abword = ab_end; 1635 *abword = '\0'; /* end of string marker */ 1636 1637 while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 && 1638 IS_WORD_CONSTITUENT(c)) { 1639 if (abword > xw->work.dabbrev_data) /* store only the last chars */ 1640 *(--abword) = (char) c; 1641 } 1642 1643 if (c >= 0) { 1644 result = abword; 1645 } else if (abword != ab_end) { 1646 result = abword; 1647 } 1648 1649 if (result != 0) { 1650 while ((c = dabbrev_prev_char(screen, cell, ld)) >= 0 && 1651 !IS_WORD_CONSTITUENT(c)) { 1652 ; /* skip preceding spaces */ 1653 } 1654 (cell->col)++; /* can be | > screen->max_col| */ 1655 } 1656 return result; 1657} 1658 1659static int 1660dabbrev_expand(XtermWidget xw) 1661{ 1662 TScreen *screen = TScreenOf(xw); 1663 int pty = screen->respond; /* file descriptor of pty */ 1664 1665 static CELL cell; 1666 static char *dabbrev_hint = 0, *lastexpansion = 0; 1667 static unsigned int expansions; 1668 1669 char *expansion; 1670 size_t hint_len; 1671 int result = 0; 1672 LineData *ld; 1673 1674 if (!screen->dabbrev_working) { /* initialize */ 1675 expansions = 0; 1676 cell.col = screen->cur_col; 1677 cell.row = screen->cur_row; 1678 1679 free(dabbrev_hint); 1680 1681 if ((dabbrev_hint = dabbrev_prev_word(xw, &cell, &ld)) != 0) { 1682 1683 free(lastexpansion); 1684 1685 if ((lastexpansion = strdup(dabbrev_hint)) != 0) { 1686 1687 /* make own copy */ 1688 if ((dabbrev_hint = strdup(dabbrev_hint)) != 0) { 1689 screen->dabbrev_working = True; 1690 /* we are in the middle of dabbrev process */ 1691 } 1692 } else { 1693 return result; 1694 } 1695 } else { 1696 return result; 1697 } 1698 if (!screen->dabbrev_working) { 1699 free(lastexpansion); 1700 lastexpansion = 0; 1701 return result; 1702 } 1703 } 1704 1705 if (dabbrev_hint == 0) 1706 return result; 1707 1708 hint_len = strlen(dabbrev_hint); 1709 for (;;) { 1710 if ((expansion = dabbrev_prev_word(xw, &cell, &ld)) == 0) { 1711 if (expansions >= 2) { 1712 expansions = 0; 1713 cell.col = screen->cur_col; 1714 cell.row = screen->cur_row; 1715 continue; 1716 } 1717 break; 1718 } 1719 if (!strncmp(dabbrev_hint, expansion, hint_len) && /* empty hint matches everything */ 1720 strlen(expansion) > hint_len && /* trivial expansion disallowed */ 1721 strcmp(expansion, lastexpansion)) /* different from previous */ 1722 break; 1723 } 1724 1725 if (expansion != 0) { 1726 Char *copybuffer; 1727 size_t del_cnt = strlen(lastexpansion) - hint_len; 1728 size_t buf_cnt = del_cnt + strlen(expansion) - hint_len; 1729 1730 if ((copybuffer = TypeMallocN(Char, buf_cnt)) != 0) { 1731 /* delete previous expansion */ 1732 memset(copybuffer, screen->dabbrev_erase_char, del_cnt); 1733 memmove(copybuffer + del_cnt, 1734 expansion + hint_len, 1735 strlen(expansion) - hint_len); 1736 v_write(pty, copybuffer, (unsigned) buf_cnt); 1737 /* v_write() just reset our flag */ 1738 screen->dabbrev_working = True; 1739 free(copybuffer); 1740 1741 free(lastexpansion); 1742 1743 if ((lastexpansion = strdup(expansion)) != 0) { 1744 result = 1; 1745 expansions++; 1746 } 1747 } 1748 } 1749 1750 return result; 1751} 1752 1753/*ARGSUSED*/ 1754void 1755HandleDabbrevExpand(Widget w, 1756 XEvent *event GCC_UNUSED, 1757 String *params GCC_UNUSED, 1758 Cardinal *nparams GCC_UNUSED) 1759{ 1760 XtermWidget xw; 1761 1762 TRACE(("Handle dabbrev-expand for %p\n", (void *) w)); 1763 if ((xw = getXtermWidget(w)) != 0) { 1764 if (!dabbrev_expand(xw)) 1765 Bell(xw, XkbBI_TerminalBell, 0); 1766 } 1767} 1768#endif /* OPT_DABBREV */ 1769 1770void 1771xtermDeiconify(XtermWidget xw) 1772{ 1773 TScreen *screen = TScreenOf(xw); 1774 Display *dpy = screen->display; 1775 Window target = VShellWindow(xw); 1776 XEvent e; 1777 Atom atom_state = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); 1778 1779 if (xtermIsIconified(xw)) { 1780 TRACE(("...de-iconify window %#lx\n", target)); 1781 XMapWindow(dpy, target); 1782 1783 memset(&e, 0, sizeof(e)); 1784 e.xclient.type = ClientMessage; 1785 e.xclient.message_type = atom_state; 1786 e.xclient.display = dpy; 1787 e.xclient.window = target; 1788 e.xclient.format = 32; 1789 e.xclient.data.l[0] = 1; 1790 e.xclient.data.l[1] = CurrentTime; 1791 1792 XSendEvent(dpy, DefaultRootWindow(dpy), False, 1793 SubstructureRedirectMask | SubstructureNotifyMask, &e); 1794 xevents(xw); 1795 } 1796} 1797 1798void 1799xtermIconify(XtermWidget xw) 1800{ 1801 TScreen *screen = TScreenOf(xw); 1802 Window target = VShellWindow(xw); 1803 1804 if (!xtermIsIconified(xw)) { 1805 TRACE(("...iconify window %#lx\n", target)); 1806 XIconifyWindow(screen->display, 1807 target, 1808 DefaultScreen(screen->display)); 1809 xevents(xw); 1810 } 1811} 1812 1813Boolean 1814xtermIsIconified(XtermWidget xw) 1815{ 1816 XWindowAttributes win_attrs; 1817 TScreen *screen = TScreenOf(xw); 1818 Window target = VShellWindow(xw); 1819 Display *dpy = screen->display; 1820 Boolean result = False; 1821 1822 if (xtermGetWinAttrs(dpy, target, &win_attrs)) { 1823 Atom actual_return_type; 1824 int actual_format_return = 0; 1825 unsigned long nitems_return = 0; 1826 unsigned long bytes_after_return = 0; 1827 unsigned char *prop_return = 0; 1828 long long_length = 1024; 1829 Atom requested_type = XA_ATOM; 1830 Atom is_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False); 1831 Atom wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); 1832 1833 /* this works with non-EWMH */ 1834 result = (win_attrs.map_state != IsViewable) ? True : False; 1835 1836 /* this is a convention used by some EWMH applications */ 1837 if (xtermGetWinProp(dpy, 1838 target, 1839 wm_state, 1840 0L, 1841 long_length, 1842 requested_type, 1843 &actual_return_type, 1844 &actual_format_return, 1845 &nitems_return, 1846 &bytes_after_return, 1847 &prop_return) 1848 && prop_return != 0 1849 && actual_return_type == requested_type 1850 && actual_format_return == 32) { 1851 unsigned long n; 1852 for (n = 0; n < nitems_return; ++n) { 1853 unsigned long check = (((unsigned long *) 1854 (void *) prop_return)[n]); 1855 if (check == is_hidden) { 1856 result = True; 1857 break; 1858 } 1859 } 1860 } 1861 } 1862 TRACE(("...window %#lx is%s iconified\n", 1863 target, 1864 result ? "" : " not")); 1865 return result; 1866} 1867 1868#if OPT_MAXIMIZE 1869/*ARGSUSED*/ 1870void 1871HandleDeIconify(Widget w, 1872 XEvent *event GCC_UNUSED, 1873 String *params GCC_UNUSED, 1874 Cardinal *nparams GCC_UNUSED) 1875{ 1876 XtermWidget xw; 1877 1878 if ((xw = getXtermWidget(w)) != 0) { 1879 xtermDeiconify(xw); 1880 } 1881} 1882 1883/*ARGSUSED*/ 1884void 1885HandleIconify(Widget w, 1886 XEvent *event GCC_UNUSED, 1887 String *params GCC_UNUSED, 1888 Cardinal *nparams GCC_UNUSED) 1889{ 1890 XtermWidget xw; 1891 1892 if ((xw = getXtermWidget(w)) != 0) { 1893 xtermIconify(xw); 1894 } 1895} 1896 1897int 1898QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height) 1899{ 1900 TScreen *screen = TScreenOf(xw); 1901 XSizeHints hints; 1902 long supp = 0; 1903 Window root_win; 1904 int root_x = -1; /* saved co-ordinates */ 1905 int root_y = -1; 1906 unsigned root_border; 1907 unsigned root_depth; 1908 int code; 1909 1910 if (XGetGeometry(screen->display, 1911 RootWindowOfScreen(XtScreen(xw)), 1912 &root_win, 1913 &root_x, 1914 &root_y, 1915 width, 1916 height, 1917 &root_border, 1918 &root_depth)) { 1919 TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n", 1920 root_x, 1921 root_y, 1922 *width, 1923 *height, 1924 root_border)); 1925 1926 *width -= (root_border * 2); 1927 *height -= (root_border * 2); 1928 1929 hints.flags = PMaxSize; 1930 if (XGetWMNormalHints(screen->display, 1931 VShellWindow(xw), 1932 &hints, 1933 &supp) 1934 && (hints.flags & PMaxSize) != 0) { 1935 1936 TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n", 1937 hints.max_width, 1938 hints.max_height)); 1939 1940 if ((unsigned) hints.max_width < *width) 1941 *width = (unsigned) hints.max_width; 1942 if ((unsigned) hints.max_height < *height) 1943 *height = (unsigned) hints.max_height; 1944 } 1945 code = 1; 1946 } else { 1947 *width = 0; 1948 *height = 0; 1949 code = 0; 1950 } 1951 return code; 1952} 1953 1954void 1955RequestMaximize(XtermWidget xw, int maximize) 1956{ 1957 TScreen *screen = TScreenOf(xw); 1958 XWindowAttributes wm_attrs, vshell_attrs; 1959 unsigned root_width = 0, root_height = 0; 1960 Boolean success = False; 1961 1962 TRACE(("RequestMaximize %d:%s\n", 1963 maximize, 1964 (maximize 1965 ? "maximize" 1966 : "restore"))); 1967 1968 /* 1969 * Before any maximize, ensure that we can capture the current screensize 1970 * as well as the estimated root-window size. 1971 */ 1972 if (maximize 1973 && QueryMaximize(xw, &root_width, &root_height) 1974 && xtermGetWinAttrs(screen->display, 1975 WMFrameWindow(xw), 1976 &wm_attrs) 1977 && xtermGetWinAttrs(screen->display, 1978 VShellWindow(xw), 1979 &vshell_attrs)) { 1980 1981 if (screen->restore_data != True 1982 || screen->restore_width != root_width 1983 || screen->restore_height != root_height) { 1984 screen->restore_data = True; 1985 screen->restore_x = wm_attrs.x; 1986 screen->restore_y = wm_attrs.y; 1987 screen->restore_width = (unsigned) vshell_attrs.width; 1988 screen->restore_height = (unsigned) vshell_attrs.height; 1989 TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n", 1990 screen->restore_x, 1991 screen->restore_y, 1992 screen->restore_width, 1993 screen->restore_height)); 1994 } 1995 1996 /* subtract wm decoration dimensions */ 1997 root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width); 1998 root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height); 1999 success = True; 2000 } else if (screen->restore_data) { 2001 success = True; 2002 maximize = 0; 2003 } 2004 2005 if (success) { 2006 switch (maximize) { 2007 case 3: 2008 FullScreen(xw, 3); /* depends on EWMH */ 2009 break; 2010 case 2: 2011 FullScreen(xw, 2); /* depends on EWMH */ 2012 break; 2013 case 1: 2014 FullScreen(xw, 0); /* overrides any EWMH hint */ 2015 TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n", 2016 0, 2017 0, 2018 root_width, 2019 root_height)); 2020 XMoveResizeWindow(screen->display, VShellWindow(xw), 2021 0, /* x */ 2022 0, /* y */ 2023 root_width, 2024 root_height); 2025 break; 2026 2027 default: 2028 FullScreen(xw, 0); /* reset any EWMH hint */ 2029 if (screen->restore_data) { 2030 screen->restore_data = False; 2031 2032 TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n", 2033 screen->restore_x, 2034 screen->restore_y, 2035 screen->restore_width, 2036 screen->restore_height)); 2037 2038 XMoveResizeWindow(screen->display, 2039 VShellWindow(xw), 2040 screen->restore_x, 2041 screen->restore_y, 2042 screen->restore_width, 2043 screen->restore_height); 2044 } 2045 break; 2046 } 2047 } 2048} 2049 2050/*ARGSUSED*/ 2051void 2052HandleMaximize(Widget w, 2053 XEvent *event GCC_UNUSED, 2054 String *params GCC_UNUSED, 2055 Cardinal *nparams GCC_UNUSED) 2056{ 2057 XtermWidget xw; 2058 2059 if ((xw = getXtermWidget(w)) != 0) { 2060 RequestMaximize(xw, 1); 2061 } 2062} 2063 2064/*ARGSUSED*/ 2065void 2066HandleRestoreSize(Widget w, 2067 XEvent *event GCC_UNUSED, 2068 String *params GCC_UNUSED, 2069 Cardinal *nparams GCC_UNUSED) 2070{ 2071 XtermWidget xw; 2072 2073 if ((xw = getXtermWidget(w)) != 0) { 2074 RequestMaximize(xw, 0); 2075 } 2076} 2077#endif /* OPT_MAXIMIZE */ 2078 2079void 2080Redraw(void) 2081{ 2082 XtermWidget xw = term; 2083 TScreen *screen = TScreenOf(xw); 2084 XExposeEvent event; 2085 2086 TRACE(("Redraw\n")); 2087 2088 event.type = Expose; 2089 event.display = screen->display; 2090 event.x = 0; 2091 event.y = 0; 2092 event.count = 0; 2093 2094 if (VWindow(screen)) { 2095 event.window = VWindow(screen); 2096 event.width = xw->core.width; 2097 event.height = xw->core.height; 2098 (*xw->core.widget_class->core_class.expose) ((Widget) xw, 2099 (XEvent *) &event, 2100 NULL); 2101 if (ScrollbarWidth(screen)) { 2102 (screen->scrollWidget->core.widget_class->core_class.expose) 2103 (screen->scrollWidget, (XEvent *) &event, NULL); 2104 } 2105 } 2106#if OPT_TEK4014 2107 if (TEK4014_SHOWN(xw)) { 2108 TekScreen *tekscr = TekScreenOf(tekWidget); 2109 event.window = TWindow(tekscr); 2110 event.width = tekWidget->core.width; 2111 event.height = tekWidget->core.height; 2112 TekExpose((Widget) tekWidget, (XEvent *) &event, NULL); 2113 } 2114#endif 2115} 2116 2117#ifdef VMS 2118#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d" 2119#else 2120#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d" 2121#endif 2122 2123void 2124timestamp_filename(char *dst, const char *src) 2125{ 2126 time_t tstamp; 2127 struct tm *tstruct; 2128 2129 tstamp = time((time_t *) 0); 2130 tstruct = localtime(&tstamp); 2131 sprintf(dst, TIMESTAMP_FMT, 2132 src, 2133 (int) tstruct->tm_year + 1900, 2134 tstruct->tm_mon + 1, 2135 tstruct->tm_mday, 2136 tstruct->tm_hour, 2137 tstruct->tm_min, 2138 tstruct->tm_sec); 2139} 2140 2141FILE * 2142create_printfile(XtermWidget xw, const char *suffix) 2143{ 2144 TScreen *screen = TScreenOf(xw); 2145 char fname[1024]; 2146 int fd; 2147 FILE *fp; 2148 2149#ifdef VMS 2150 sprintf(fname, "sys$scratch:xterm%s", suffix); 2151#elif defined(HAVE_STRFTIME) 2152 { 2153 char format[1024]; 2154 time_t now; 2155 struct tm *ltm; 2156 2157 now = time((time_t *) 0); 2158 ltm = localtime(&now); 2159 2160 sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix); 2161 if (strftime(fname, sizeof fname, format, ltm) == 0) { 2162 sprintf(fname, "xterm%s", suffix); 2163 } 2164 } 2165#else 2166 sprintf(fname, "xterm%s", suffix); 2167#endif 2168 fd = open_userfile(screen->uid, screen->gid, fname, False); 2169 fp = (fd >= 0) ? fdopen(fd, "wb") : NULL; 2170 return fp; 2171} 2172 2173int 2174open_userfile(uid_t uid, gid_t gid, char *path, Bool append) 2175{ 2176 int fd; 2177 struct stat sb; 2178 2179#ifdef VMS 2180 if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { 2181 int the_error = errno; 2182 xtermWarning("cannot open %s: %d:%s\n", 2183 path, 2184 the_error, 2185 SysErrorMsg(the_error)); 2186 return -1; 2187 } 2188 chown(path, uid, gid); 2189#else 2190 if ((access(path, F_OK) != 0 && (errno != ENOENT)) 2191 || (creat_as(uid, gid, append, path, 0644) <= 0) 2192 || ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) { 2193 int the_error = errno; 2194 xtermWarning("cannot open %s: %d:%s\n", 2195 path, 2196 the_error, 2197 SysErrorMsg(the_error)); 2198 return -1; 2199 } 2200#endif 2201 2202 /* 2203 * Doublecheck that the user really owns the file that we've opened before 2204 * we do any damage, and that it is not world-writable. 2205 */ 2206 if (fstat(fd, &sb) < 0 2207 || sb.st_uid != uid 2208 || (sb.st_mode & 022) != 0) { 2209 xtermWarning("you do not own %s\n", path); 2210 close(fd); 2211 return -1; 2212 } 2213 return fd; 2214} 2215 2216#ifndef VMS 2217/* 2218 * Create a file only if we could with the permissions of the real user id. 2219 * We could emulate this with careful use of access() and following 2220 * symbolic links, but that is messy and has race conditions. 2221 * Forking is messy, too, but we can't count on setreuid() or saved set-uids 2222 * being available. 2223 * 2224 * Note: When called for user logging, we have ensured that the real and 2225 * effective user ids are the same, so this remains as a convenience function 2226 * for the debug logs. 2227 * 2228 * Returns 2229 * 1 if we can proceed to open the file in relative safety, 2230 * -1 on error, e.g., cannot fork 2231 * 0 otherwise. 2232 */ 2233int 2234creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode) 2235{ 2236 int fd; 2237 pid_t pid; 2238 int retval = 0; 2239 int childstat = 0; 2240#ifndef HAVE_WAITPID 2241 int waited; 2242 void (*chldfunc) (int); 2243 2244 chldfunc = signal(SIGCHLD, SIG_DFL); 2245#endif /* HAVE_WAITPID */ 2246 2247 TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n", 2248 (int) uid, (int) geteuid(), 2249 (int) gid, (int) getegid(), 2250 append, 2251 pathname, 2252 mode)); 2253 2254 if (uid == geteuid() && gid == getegid()) { 2255 fd = open(pathname, 2256 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 2257 mode); 2258 if (fd >= 0) 2259 close(fd); 2260 return (fd >= 0); 2261 } 2262 2263 pid = fork(); 2264 switch (pid) { 2265 case 0: /* child */ 2266 if (setgid(gid) == -1 2267 || setuid(uid) == -1) { 2268 /* we cannot report an error here via stderr, just quit */ 2269 retval = 1; 2270 } else { 2271 fd = open(pathname, 2272 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 2273 mode); 2274 if (fd >= 0) { 2275 close(fd); 2276 retval = 0; 2277 } else { 2278 retval = 1; 2279 } 2280 } 2281 _exit(retval); 2282 /* NOTREACHED */ 2283 case -1: /* error */ 2284 return retval; 2285 default: /* parent */ 2286#ifdef HAVE_WAITPID 2287 while (waitpid(pid, &childstat, 0) < 0) { 2288#ifdef EINTR 2289 if (errno == EINTR) 2290 continue; 2291#endif /* EINTR */ 2292#ifdef ERESTARTSYS 2293 if (errno == ERESTARTSYS) 2294 continue; 2295#endif /* ERESTARTSYS */ 2296 break; 2297 } 2298#else /* HAVE_WAITPID */ 2299 waited = wait(&childstat); 2300 signal(SIGCHLD, chldfunc); 2301 /* 2302 Since we had the signal handler uninstalled for a while, 2303 we might have missed the termination of our screen child. 2304 If we can check for this possibility without hanging, do so. 2305 */ 2306 do 2307 if (waited == TScreenOf(term)->pid) 2308 NormalExit(); 2309 while ((waited = nonblocking_wait()) > 0) ; 2310#endif /* HAVE_WAITPID */ 2311#ifndef WIFEXITED 2312#define WIFEXITED(status) ((status & 0xff) != 0) 2313#endif 2314 if (WIFEXITED(childstat)) 2315 retval = 1; 2316 return retval; 2317 } 2318} 2319#endif /* !VMS */ 2320 2321int 2322xtermResetIds(TScreen *screen) 2323{ 2324 int result = 0; 2325 if (setgid(screen->gid) == -1) { 2326 xtermWarning("unable to reset group-id\n"); 2327 result = -1; 2328 } 2329 if (setuid(screen->uid) == -1) { 2330 xtermWarning("unable to reset user-id\n"); 2331 result = -1; 2332 } 2333 return result; 2334} 2335 2336#ifdef ALLOWLOGGING 2337 2338/* 2339 * Logging is a security hole, since it allows a setuid program to write 2340 * arbitrary data to an arbitrary file. So it is disabled by default. 2341 */ 2342 2343#ifdef ALLOWLOGFILEEXEC 2344static void 2345handle_SIGPIPE(int sig GCC_UNUSED) 2346{ 2347 XtermWidget xw = term; 2348 TScreen *screen = TScreenOf(xw); 2349 2350 DEBUG_MSG("handle:logpipe\n"); 2351#ifdef SYSV 2352 (void) signal(SIGPIPE, SIG_IGN); 2353#endif /* SYSV */ 2354 if (screen->logging) 2355 CloseLog(xw); 2356} 2357 2358/* 2359 * Open a command to pipe log data to it. 2360 * Warning, enabling this "feature" allows arbitrary programs 2361 * to be run. If ALLOWLOGFILECHANGES is enabled, this can be 2362 * done through escape sequences.... You have been warned. 2363 */ 2364static void 2365StartLogExec(TScreen *screen) 2366{ 2367 int pid; 2368 int p[2]; 2369 static char *shell; 2370 struct passwd pw; 2371 2372 if ((shell = x_getenv("SHELL")) == NULL) { 2373 2374 if (x_getpwuid(screen->uid, &pw)) { 2375 char *name = x_getlogin(screen->uid, &pw); 2376 if (*(pw.pw_shell)) { 2377 shell = pw.pw_shell; 2378 } 2379 free(name); 2380 } 2381 } 2382 2383 if (shell == 0) { 2384 static char dummy[] = "/bin/sh"; 2385 shell = dummy; 2386 } 2387 2388 if (access(shell, X_OK) != 0) { 2389 xtermPerror("Can't execute `%s'\n", shell); 2390 return; 2391 } 2392 2393 if (pipe(p) < 0) { 2394 xtermPerror("Can't make a pipe connection\n"); 2395 return; 2396 } else if ((pid = fork()) < 0) { 2397 xtermPerror("Can't fork...\n"); 2398 return; 2399 } 2400 if (pid == 0) { /* child */ 2401 /* 2402 * Close our output (we won't be talking back to the 2403 * parent), and redirect our child's output to the 2404 * original stderr. 2405 */ 2406 close(p[1]); 2407 dup2(p[0], 0); 2408 close(p[0]); 2409 dup2(fileno(stderr), 1); 2410 dup2(fileno(stderr), 2); 2411 2412 close(fileno(stderr)); 2413 close(ConnectionNumber(screen->display)); 2414 close(screen->respond); 2415 2416 signal(SIGHUP, SIG_DFL); 2417 signal(SIGCHLD, SIG_DFL); 2418 2419 /* (this is redundant) */ 2420 if (xtermResetIds(screen) < 0) 2421 exit(ERROR_SETUID); 2422 2423 execl(shell, shell, "-c", &screen->logfile[1], (void *) 0); 2424 xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]); 2425 exit(ERROR_LOGEXEC); 2426 } 2427 close(p[0]); 2428 screen->logfd = p[1]; 2429 signal(SIGPIPE, handle_SIGPIPE); 2430} 2431#endif /* ALLOWLOGFILEEXEC */ 2432 2433/* 2434 * Generate a path for a logfile if no default path is given. 2435 */ 2436static char * 2437GenerateLogPath(void) 2438{ 2439 static char *log_default = NULL; 2440 2441 /* once opened we just reuse the same log name */ 2442 if (log_default) 2443 return (log_default); 2444 2445#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME) 2446 { 2447#define LEN_HOSTNAME 255 2448 /* Internet standard limit (RFC 1035): ``To simplify implementations, 2449 * the total length of a domain name (i.e., label octets and label 2450 * length octets) is restricted to 255 octets or less.'' 2451 */ 2452#define LEN_GETPID 9 2453 /* 2454 * This is arbitrary... 2455 */ 2456 const char form[] = "Xterm.log.%s%s.%lu"; 2457 char where[LEN_HOSTNAME + 1]; 2458 char when[LEN_TIMESTAMP]; 2459 time_t now = time((time_t *) 0); 2460 struct tm *ltm = (struct tm *) localtime(&now); 2461 2462 if ((gethostname(where, sizeof(where)) == 0) && 2463 (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) && 2464 ((log_default = (char *) malloc((sizeof(form) 2465 + strlen(where) 2466 + strlen(when) 2467 + LEN_GETPID))) != NULL)) { 2468 (void) sprintf(log_default, 2469 form, 2470 where, when, 2471 ((unsigned long) getpid()) % ((unsigned long) 1e10)); 2472 } 2473 } 2474#else 2475 static const char log_def_name[] = "XtermLog.XXXXXX"; 2476 if ((log_default = x_strdup(log_def_name)) != NULL) { 2477 mktemp(log_default); 2478 } 2479#endif 2480 2481 return (log_default); 2482} 2483 2484void 2485StartLog(XtermWidget xw) 2486{ 2487 TScreen *screen = TScreenOf(xw); 2488 2489 if (screen->logging || (screen->inhibit & I_LOG)) 2490 return; 2491#ifdef VMS /* file name is fixed in VMS variant */ 2492 screen->logfd = open(XTERM_VMS_LOGFILE, 2493 O_CREAT | O_TRUNC | O_APPEND | O_RDWR, 2494 0640); 2495 if (screen->logfd < 0) 2496 return; /* open failed */ 2497#else /*VMS */ 2498 2499 /* if we weren't supplied with a logfile path, generate one */ 2500 if (IsEmpty(screen->logfile)) 2501 screen->logfile = GenerateLogPath(); 2502 2503 /* give up if we were unable to allocate the filename */ 2504 if (!screen->logfile) 2505 return; 2506 2507 if (*screen->logfile == '|') { /* exec command */ 2508#ifdef ALLOWLOGFILEEXEC 2509 StartLogExec(screen); 2510#else 2511 Bell(xw, XkbBI_Info, 0); 2512 Bell(xw, XkbBI_Info, 0); 2513 return; 2514#endif 2515 } else if (strcmp(screen->logfile, "-") == 0) { 2516 screen->logfd = STDOUT_FILENO; 2517 } else { 2518 if ((screen->logfd = open_userfile(screen->uid, 2519 screen->gid, 2520 screen->logfile, 2521 True)) < 0) 2522 return; 2523 } 2524#endif /*VMS */ 2525 screen->logstart = VTbuffer->next; 2526 screen->logging = True; 2527 update_logging(); 2528} 2529 2530void 2531CloseLog(XtermWidget xw) 2532{ 2533 TScreen *screen = TScreenOf(xw); 2534 2535 if (!screen->logging || (screen->inhibit & I_LOG)) 2536 return; 2537 FlushLog(xw); 2538 close(screen->logfd); 2539 screen->logging = False; 2540 update_logging(); 2541} 2542 2543void 2544FlushLog(XtermWidget xw) 2545{ 2546 TScreen *screen = TScreenOf(xw); 2547 2548 if (screen->logging && !(screen->inhibit & I_LOG)) { 2549 Char *cp; 2550 int i; 2551 2552#ifdef VMS /* avoid logging output loops which otherwise occur sometimes 2553 when there is no output and cp/screen->logstart are 1 apart */ 2554 if (!tt_new_output) 2555 return; 2556 tt_new_output = False; 2557#endif /* VMS */ 2558 cp = VTbuffer->next; 2559 if (screen->logstart != 0 2560 && (i = (int) (cp - screen->logstart)) > 0) { 2561 IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i)); 2562 } 2563 screen->logstart = VTbuffer->next; 2564 } 2565} 2566 2567#endif /* ALLOWLOGGING */ 2568 2569/***====================================================================***/ 2570 2571static unsigned 2572maskToShift(unsigned long mask) 2573{ 2574 unsigned result = 0; 2575 if (mask != 0) { 2576 while ((mask & 1) == 0) { 2577 mask >>= 1; 2578 ++result; 2579 } 2580 } 2581 return result; 2582} 2583 2584static unsigned 2585maskToWidth(unsigned long mask) 2586{ 2587 unsigned result = 0; 2588 while (mask != 0) { 2589 if ((mask & 1) != 0) 2590 ++result; 2591 mask >>= 1; 2592 } 2593 return result; 2594} 2595 2596int 2597getVisualInfo(XtermWidget xw) 2598{ 2599#define MYFMT "getVisualInfo \ 2600depth %d, \ 2601type %d (%s), \ 2602size %d \ 2603rgb masks (%04lx/%04lx/%04lx)\n" 2604#define MYARG \ 2605 vi->depth,\ 2606 vi->class,\ 2607 ((vi->class & 1) ? "dynamic" : "static"),\ 2608 vi->colormap_size,\ 2609 vi->red_mask,\ 2610 vi->green_mask,\ 2611 vi->blue_mask 2612 2613 TScreen *screen = TScreenOf(xw); 2614 Display *dpy = screen->display; 2615 XVisualInfo myTemplate; 2616 2617 if (xw->visInfo == 0 && xw->numVisuals == 0) { 2618 myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy, 2619 XDefaultScreen(dpy))); 2620 xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask, 2621 &myTemplate, &xw->numVisuals); 2622 2623 if ((xw->visInfo != 0) && (xw->numVisuals > 0)) { 2624 XVisualInfo *vi = xw->visInfo; 2625 xw->rgb_widths[0] = maskToWidth(vi->red_mask); 2626 xw->rgb_widths[1] = maskToWidth(vi->green_mask); 2627 xw->rgb_widths[2] = maskToWidth(vi->blue_mask); 2628 xw->rgb_shifts[0] = maskToShift(vi->red_mask); 2629 xw->rgb_shifts[1] = maskToShift(vi->green_mask); 2630 xw->rgb_shifts[2] = maskToShift(vi->blue_mask); 2631 2632 xw->has_rgb = ((vi->red_mask != 0) && 2633 (vi->green_mask != 0) && 2634 (vi->blue_mask != 0) && 2635 ((vi->red_mask & vi->green_mask) == 0) && 2636 ((vi->green_mask & vi->blue_mask) == 0) && 2637 ((vi->blue_mask & vi->red_mask) == 0)); 2638 2639 if (resource.reportColors) { 2640 printf(MYFMT, MYARG); 2641 } 2642 TRACE((MYFMT, MYARG)); 2643 TRACE(("...shifts %u/%u/%u\n", 2644 xw->rgb_shifts[0], 2645 xw->rgb_shifts[1], 2646 xw->rgb_shifts[2])); 2647 } 2648 } 2649 return (xw->visInfo != 0) && (xw->numVisuals > 0); 2650#undef MYFMT 2651#undef MYARG 2652} 2653 2654#if OPT_ISO_COLORS 2655static Bool 2656ReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final) 2657{ 2658 Bool result = False; 2659 2660 if (AllowColorOps(xw, ecGetAnsiColor)) { 2661 XColor color; 2662 Colormap cmap = xw->core.colormap; 2663 char buffer[80]; 2664 2665 TRACE(("ReportAnsiColorRequest %d\n", colornum)); 2666 color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]); 2667 XQueryColor(TScreenOf(xw)->display, cmap, &color); 2668 sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x", 2669 opcode, 2670 (opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum, 2671 color.red, 2672 color.green, 2673 color.blue); 2674 unparseputc1(xw, ANSI_OSC); 2675 unparseputs(xw, buffer); 2676 unparseputc1(xw, final); 2677 result = True; 2678 } 2679 return result; 2680} 2681 2682static void 2683getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep) 2684{ 2685 if (getVisualInfo(xw)) { 2686 *typep = (unsigned) xw->visInfo->class; 2687 *sizep = (unsigned) xw->visInfo->colormap_size; 2688 } else { 2689 *typep = 0; 2690 *sizep = 0; 2691 } 2692} 2693 2694#define MAX_COLORTABLE 4096 2695 2696/* 2697 * Make only one call to XQueryColors(), since it can be slow. 2698 */ 2699static Boolean 2700loadColorTable(XtermWidget xw, unsigned length) 2701{ 2702 Colormap cmap = xw->core.colormap; 2703 TScreen *screen = TScreenOf(xw); 2704 Boolean result = (screen->cmap_data != 0); 2705 2706 if (!result 2707 && length != 0 2708 && length < MAX_COLORTABLE) { 2709 screen->cmap_data = TypeMallocN(XColor, (size_t) length); 2710 2711 if (screen->cmap_data != 0) { 2712 unsigned i; 2713 unsigned shift; 2714 2715 if (getVisualInfo(xw)) 2716 shift = xw->rgb_shifts[2]; 2717 else 2718 shift = 0; 2719 2720 screen->cmap_size = length; 2721 2722 for (i = 0; i < screen->cmap_size; i++) { 2723 screen->cmap_data[i].pixel = (unsigned long) i << shift; 2724 } 2725 result = (Boolean) (XQueryColors(screen->display, 2726 cmap, 2727 screen->cmap_data, 2728 (int) screen->cmap_size) != 0); 2729 } 2730 } 2731 return result; 2732} 2733 2734/* 2735 * Find closest color for "def" in "cmap". 2736 * Set "def" to the resulting color. 2737 * 2738 * Based on Monish Shah's "find_closest_color()" for Vim 6.0, 2739 * modified with ideas from David Tong's "noflash" library. 2740 * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk. 2741 * 2742 * Return False if not able to find or allocate a color. 2743 */ 2744static Boolean 2745allocateClosestRGB(XtermWidget xw, Colormap cmap, XColor *def) 2746{ 2747 TScreen *screen = TScreenOf(xw); 2748 Boolean result = False; 2749 unsigned cmap_type; 2750 unsigned cmap_size; 2751 2752 getColormapInfo(xw, &cmap_type, &cmap_size); 2753 2754 if ((cmap_type & 1) != 0) { 2755 2756 if (loadColorTable(xw, cmap_size)) { 2757 char *tried = TypeCallocN(char, (size_t) cmap_size); 2758 2759 if (tried != 0) { 2760 unsigned attempts; 2761 2762 /* 2763 * Try (possibly each entry in the color map) to find the best 2764 * approximation to the requested color. 2765 */ 2766 for (attempts = 0; attempts < cmap_size; attempts++) { 2767 Boolean first = True; 2768 double bestRGB = 0.0; 2769 unsigned bestInx = 0; 2770 unsigned i; 2771 2772 for (i = 0; i < cmap_size; i++) { 2773 if (!tried[bestInx]) { 2774 double diff, thisRGB = 0.0; 2775 2776 /* 2777 * Look for the best match based on luminance. 2778 * Measure this by the least-squares difference of 2779 * the weighted R/G/B components from the color map 2780 * versus the requested color. Use the Y (luma) 2781 * component of the YIQ color space model for 2782 * weights that correspond to the luminance. 2783 */ 2784#define AddColorWeight(weight, color) \ 2785 diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \ 2786 thisRGB += diff * diff 2787 2788 AddColorWeight(0.30, red); 2789 AddColorWeight(0.61, green); 2790 AddColorWeight(0.11, blue); 2791 2792 if (first || (thisRGB < bestRGB)) { 2793 first = False; 2794 bestInx = i; 2795 bestRGB = thisRGB; 2796 } 2797 } 2798 } 2799 if (XAllocColor(screen->display, cmap, 2800 &screen->cmap_data[bestInx]) != 0) { 2801 *def = screen->cmap_data[bestInx]; 2802 TRACE(("...closest %x/%x/%x\n", def->red, 2803 def->green, def->blue)); 2804 result = True; 2805 break; 2806 } 2807 /* 2808 * It failed - either the color map entry was readonly, or 2809 * another client has allocated the entry. Mark the entry 2810 * so we will ignore it 2811 */ 2812 tried[bestInx] = True; 2813 } 2814 free(tried); 2815 } 2816 } 2817 } 2818 return result; 2819} 2820 2821#ifndef ULONG_MAX 2822#define ULONG_MAX (unsigned long)(~(0L)) 2823#endif 2824 2825#define CheckColor(result, value) \ 2826 result = 0; \ 2827 if (value.red) \ 2828 result |= 1; \ 2829 if (value.green) \ 2830 result |= 2; \ 2831 if (value.blue) \ 2832 result |= 4 2833 2834#define SelectColor(state, value, result) \ 2835 switch (state) { \ 2836 default: \ 2837 case 1: \ 2838 result = value.red; \ 2839 break; \ 2840 case 2: \ 2841 result = value.green; \ 2842 break; \ 2843 case 4: \ 2844 result = value.blue; \ 2845 break; \ 2846 } 2847 2848/* 2849 * Check if the color map consists of values in exactly one of the red, green 2850 * or blue columns. If it is not, we do not know how to use it for the exact 2851 * match. 2852 */ 2853static int 2854simpleColors(XColor *colortable, unsigned length) 2855{ 2856 unsigned n; 2857 int state = 0; 2858 int check; 2859 2860 for (n = 0; n < length; ++n) { 2861 if (state > 0) { 2862 CheckColor(check, colortable[n]); 2863 if (check > 0 && check != state) { 2864 state = 0; 2865 break; 2866 } 2867 } else { 2868 CheckColor(state, colortable[n]); 2869 } 2870 } 2871 switch (state) { 2872 case 1: 2873 case 2: 2874 case 4: 2875 break; 2876 default: 2877 state = 0; 2878 break; 2879 } 2880 return state; 2881} 2882 2883/* 2884 * Shift the mask left or right to put its most significant bit at the 16-bit 2885 * mark. 2886 */ 2887static unsigned 2888normalizeMask(unsigned mask) 2889{ 2890 while (mask < 0x8000) { 2891 mask <<= 1; 2892 } 2893 while (mask >= 0x10000) { 2894 mask >>= 1; 2895 } 2896 return mask; 2897} 2898 2899static unsigned 2900searchColors(XColor *colortable, unsigned mask, unsigned length, unsigned 2901 color, int state) 2902{ 2903 unsigned result = 0; 2904 unsigned n; 2905 unsigned long best = ULONG_MAX; 2906 unsigned value; 2907 2908 mask = normalizeMask(mask); 2909 for (n = 0; n < length; ++n) { 2910 unsigned long diff; 2911 2912 SelectColor(state, colortable[n], value); 2913 diff = ((color & mask) - (value & mask)); 2914 diff *= diff; 2915 if (diff < best) { 2916#if 0 2917 TRACE(("...%d:looking for %x, found %x/%x/%x (%lx)\n", 2918 n, color, 2919 colortable[n].red, 2920 colortable[n].green, 2921 colortable[n].blue, 2922 diff)); 2923#endif 2924 result = n; 2925 best = diff; 2926 } 2927 } 2928 SelectColor(state, colortable[result], value); 2929 return value; 2930} 2931 2932/* 2933 * This is a workaround for a longstanding defect in the X libraries. 2934 * 2935 * According to 2936 * http://www.unix.com/man-page/all/3x/XAllocColoA/ 2937 * 2938 * XAllocColor() acts differently on static and dynamic visuals. On Pseu- 2939 * doColor, DirectColor, and GrayScale visuals, XAllocColor() fails if 2940 * there are no unallocated colorcells and no allocated read-only cell 2941 * exactly matches the requested RGB values. On StaticColor, TrueColor, 2942 * and StaticGray visuals, XAllocColor() returns the closest RGB values 2943 * available in the colormap. The colorcell_in_out structure returns the 2944 * actual RGB values allocated. 2945 * 2946 * That is, XAllocColor() should suffice unless the color map is full. In that 2947 * case, allocateClosestRGB() is useful for the dynamic display classes such as 2948 * PseudoColor. It is not useful for TrueColor, since XQueryColors() does not 2949 * return regular RGB triples (unless a different scheme was used for 2950 * specifying the pixel values); only the blue value is filled in. However, it 2951 * is filled in with the colors that the server supports. 2952 * 2953 * Also (the reason for this function), XAllocColor() does not really work as 2954 * described. For some TrueColor configurations it merely returns a close 2955 * approximation, but not the closest. 2956 */ 2957static Boolean 2958allocateExactRGB(XtermWidget xw, Colormap cmap, XColor *def) 2959{ 2960 XColor save = *def; 2961 TScreen *screen = TScreenOf(xw); 2962 Boolean result = (Boolean) (XAllocColor(screen->display, cmap, def) != 0); 2963 2964 /* 2965 * If this is a statically allocated display with too many items to store 2966 * in our array, i.e., TrueColor, see if we can improve on the result by 2967 * using the color values actually supported by the server. 2968 */ 2969 if (result) { 2970 unsigned cmap_type; 2971 unsigned cmap_size; 2972 2973 getColormapInfo(xw, &cmap_type, &cmap_size); 2974 2975 if (cmap_type == TrueColor) { 2976 XColor temp = *def; 2977 int state; 2978 2979 if (loadColorTable(xw, cmap_size) 2980 && (state = simpleColors(screen->cmap_data, cmap_size)) > 0) { 2981#define SearchColors(which) \ 2982 temp.which = (unsigned short) searchColors(screen->cmap_data, \ 2983 (unsigned) xw->visInfo->which##_mask,\ 2984 cmap_size, \ 2985 save.which, \ 2986 state) 2987 SearchColors(red); 2988 SearchColors(green); 2989 SearchColors(blue); 2990 if (XAllocColor(screen->display, cmap, &temp) != 0) { 2991#if OPT_TRACE 2992 if (temp.red != save.red 2993 || temp.green != save.green 2994 || temp.blue != save.blue) { 2995 TRACE(("...improved %x/%x/%x ->%x/%x/%x\n", 2996 save.red, save.green, save.blue, 2997 temp.red, temp.green, temp.blue)); 2998 } else { 2999 TRACE(("...no improvement for %x/%x/%x\n", 3000 save.red, save.green, save.blue)); 3001 } 3002#endif 3003 *def = temp; 3004 } 3005 } 3006 } 3007 } 3008 3009 return result; 3010} 3011 3012/* 3013 * Allocate a color for the "ANSI" colors. That actually includes colors up 3014 * to 256. 3015 * 3016 * Returns 3017 * -1 on error 3018 * 0 on no change 3019 * 1 if a new color was allocated. 3020 */ 3021static int 3022AllocateAnsiColor(XtermWidget xw, 3023 ColorRes * res, 3024 const char *spec) 3025{ 3026 int result; 3027 XColor def; 3028 3029 if (xtermAllocColor(xw, &def, spec)) { 3030 if ( 3031#if OPT_COLOR_RES 3032 res->mode == True && 3033#endif 3034 EQL_COLOR_RES(res, def.pixel)) { 3035 result = 0; 3036 } else { 3037 result = 1; 3038 SET_COLOR_RES(res, def.pixel); 3039 res->red = def.red; 3040 res->green = def.green; 3041 res->blue = def.blue; 3042 TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n", 3043 (int) (res - TScreenOf(xw)->Acolors), spec, 3044 def.red, 3045 def.green, 3046 def.blue, 3047 def.pixel)); 3048#if OPT_COLOR_RES 3049 if (!res->mode) 3050 result = 0; 3051 res->mode = True; 3052#endif 3053 } 3054 } else { 3055 TRACE(("AllocateAnsiColor %s (failed)\n", spec)); 3056 result = -1; 3057 } 3058 return (result); 3059} 3060 3061#if OPT_COLOR_RES 3062Pixel 3063xtermGetColorRes(XtermWidget xw, ColorRes * res) 3064{ 3065 Pixel result = 0; 3066 3067 if (res->mode) { 3068 result = res->value; 3069 } else { 3070 TRACE(("xtermGetColorRes for Acolors[%d]\n", 3071 (int) (res - TScreenOf(xw)->Acolors))); 3072 3073 if (res >= TScreenOf(xw)->Acolors) { 3074 assert(res - TScreenOf(xw)->Acolors < MAXCOLORS); 3075 3076 if (AllocateAnsiColor(xw, res, res->resource) < 0) { 3077 res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value; 3078 res->mode = -True; 3079 xtermWarning("Cannot allocate color \"%s\"\n", 3080 NonNull(res->resource)); 3081 } 3082 result = res->value; 3083 } else { 3084 result = 0; 3085 } 3086 } 3087 return result; 3088} 3089#endif 3090 3091static int 3092ChangeOneAnsiColor(XtermWidget xw, int color, const char *name) 3093{ 3094 int code; 3095 3096 if (color < 0 || color >= MAXCOLORS) { 3097 code = -1; 3098 } else { 3099 ColorRes *res = &(TScreenOf(xw)->Acolors[color]); 3100 3101 TRACE(("ChangeAnsiColor for Acolors[%d]\n", color)); 3102 code = AllocateAnsiColor(xw, res, name); 3103 } 3104 return code; 3105} 3106 3107/* 3108 * Set or query entries in the Acolors[] array by parsing pairs of color/name 3109 * values from the given buffer. 3110 * 3111 * The color can be any legal index into Acolors[], which consists of the 3112 * 16/88/256 "ANSI" colors, followed by special color values for the various 3113 * colorXX resources. The indices for the special color values are not 3114 * simple to work with, so an alternative is to use the calls which pass in 3115 * 'first' set to the beginning of those indices. 3116 * 3117 * If the name is "?", report to the host the current value for the color. 3118 */ 3119static Bool 3120ChangeAnsiColorRequest(XtermWidget xw, 3121 int opcode, 3122 char *buf, 3123 int first, 3124 int final) 3125{ 3126 int repaint = False; 3127 int code; 3128 int last = (MAXCOLORS - first); 3129 int queried = 0; 3130 3131 TRACE(("ChangeAnsiColorRequest string='%s'\n", buf)); 3132 3133 while (buf && *buf) { 3134 int color; 3135 char *name = strchr(buf, ';'); 3136 3137 if (name == NULL) 3138 break; 3139 *name = '\0'; 3140 name++; 3141 color = atoi(buf); 3142 if (color < 0 || color >= last) 3143 break; /* quit on any error */ 3144 buf = strchr(name, ';'); 3145 if (buf) { 3146 *buf = '\0'; 3147 buf++; 3148 } 3149 if (!strcmp(name, "?")) { 3150 if (ReportAnsiColorRequest(xw, opcode, color + first, final)) 3151 ++queried; 3152 } else { 3153 code = ChangeOneAnsiColor(xw, color + first, name); 3154 if (code < 0) { 3155 /* stop on any error */ 3156 break; 3157 } else if (code > 0) { 3158 repaint = True; 3159 } 3160 /* FIXME: free old color somehow? We aren't for the other color 3161 * change style (dynamic colors). 3162 */ 3163 } 3164 } 3165 if (queried) 3166 unparse_end(xw); 3167 3168 return (repaint); 3169} 3170 3171static Bool 3172ResetOneAnsiColor(XtermWidget xw, int color, int start) 3173{ 3174 Bool repaint = False; 3175 int last = MAXCOLORS - start; 3176 3177 if (color >= 0 && color < last) { 3178 ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]); 3179 3180 if (res->mode) { 3181 /* a color has been allocated for this slot - test further... */ 3182 if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) { 3183 repaint = True; 3184 } 3185 } 3186 } 3187 return repaint; 3188} 3189 3190int 3191ResetAnsiColorRequest(XtermWidget xw, char *buf, int start) 3192{ 3193 int repaint = 0; 3194 int color; 3195 3196 TRACE(("ResetAnsiColorRequest(%s)\n", buf)); 3197 if (*buf != '\0') { 3198 /* reset specific colors */ 3199 while (!IsEmpty(buf)) { 3200 char *next; 3201 3202 color = (int) (strtol) (buf, &next, 10); 3203 if (!PartS2L(buf, next) || (color < 0)) 3204 break; /* no number at all */ 3205 if (next != 0) { 3206 if (strchr(";", *next) == 0) 3207 break; /* unexpected delimiter */ 3208 ++next; 3209 } 3210 3211 if (ResetOneAnsiColor(xw, color, start)) { 3212 ++repaint; 3213 } 3214 buf = next; 3215 } 3216 } else { 3217 TRACE(("...resetting all %d colors\n", MAXCOLORS)); 3218 for (color = 0; color < MAXCOLORS; ++color) { 3219 if (ResetOneAnsiColor(xw, color, start)) { 3220 ++repaint; 3221 } 3222 } 3223 } 3224 TRACE(("...ResetAnsiColorRequest ->%d\n", repaint)); 3225 return repaint; 3226} 3227#else 3228#define allocateClosestRGB(xw, cmap, def) 0 3229#define allocateExactRGB(xw, cmap, def) XAllocColor(TScreenOf(xw)->display, cmap, def) 3230#endif /* OPT_ISO_COLORS */ 3231 3232Boolean 3233allocateBestRGB(XtermWidget xw, XColor *def) 3234{ 3235 Colormap cmap = xw->core.colormap; 3236 3237 return allocateExactRGB(xw, cmap, def) || allocateClosestRGB(xw, cmap, def); 3238} 3239 3240static Boolean 3241xtermAllocColor(XtermWidget xw, XColor *def, const char *spec) 3242{ 3243 Boolean result = False; 3244 TScreen *screen = TScreenOf(xw); 3245 Colormap cmap = xw->core.colormap; 3246 size_t have = strlen(spec); 3247 3248 if (have == 0 || have > MAX_U_STRING) { 3249 if (resource.reportColors) { 3250 printf("color (ignored, length %lu)\n", (unsigned long)have); 3251 } 3252 } else if (XParseColor(screen->display, cmap, spec, def)) { 3253 XColor save_def = *def; 3254 if (resource.reportColors) { 3255 printf("color %04x/%04x/%04x = \"%s\"\n", 3256 def->red, def->green, def->blue, 3257 spec); 3258 } 3259 if (allocateBestRGB(xw, def)) { 3260 if (resource.reportColors) { 3261 if (def->red != save_def.red || 3262 def->green != save_def.green || 3263 def->blue != save_def.blue) { 3264 printf("color %04x/%04x/%04x ~ \"%s\"\n", 3265 def->red, def->green, def->blue, 3266 spec); 3267 } 3268 } 3269 TRACE(("xtermAllocColor -> %x/%x/%x\n", 3270 def->red, def->green, def->blue)); 3271 result = True; 3272 } 3273 } 3274 return result; 3275} 3276 3277/* 3278 * This provides an approximation (the closest color from xterm's palette) 3279 * rather than the "exact" color (whatever the display could provide, actually) 3280 * because of the context in which it is used. 3281 */ 3282#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given)) 3283int 3284xtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue) 3285{ 3286 int result = -1; 3287#if OPT_COLOR_RES && OPT_ISO_COLORS 3288 int n; 3289 int best_index = -1; 3290 unsigned long best_value = 0; 3291 unsigned long this_value; 3292 long diff_red, diff_green, diff_blue; 3293 3294 TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue)); 3295 3296 for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) { 3297 ColorRes *res = &(TScreenOf(xw)->Acolors[n]); 3298 3299 /* ensure that we have a value for each of the colors */ 3300 if (!res->mode) { 3301 (void) AllocateAnsiColor(xw, res, res->resource); 3302 } 3303 3304 /* find the closest match */ 3305 if (res->mode == True) { 3306 TRACE2(("...lookup %lx -> %x/%x/%x\n", 3307 res->value, res->red, res->green, res->blue)); 3308 diff_red = ColorDiff(find_red, res->red); 3309 diff_green = ColorDiff(find_green, res->green); 3310 diff_blue = ColorDiff(find_blue, res->blue); 3311 this_value = (unsigned long) ((diff_red * diff_red) 3312 + (diff_green * diff_green) 3313 + (diff_blue * diff_blue)); 3314 if (best_index < 0 || this_value < best_value) { 3315 best_index = n; 3316 best_value = this_value; 3317 } 3318 } 3319 } 3320 TRACE(("...best match at %d with diff %lx\n", best_index, best_value)); 3321 result = best_index; 3322#else 3323 (void) xw; 3324 (void) find_red; 3325 (void) find_green; 3326 (void) find_blue; 3327#endif 3328 return result; 3329} 3330 3331#if OPT_DIRECT_COLOR 3332int 3333getDirectColor(XtermWidget xw, int red, int green, int blue) 3334{ 3335#define nRGB(name,shift) \ 3336 ((unsigned long)(name << xw->rgb_shifts[shift]) \ 3337 & xw->visInfo->name ##_mask) 3338 MyPixel result = (MyPixel) (nRGB(red, 0) | nRGB(green, 1) | nRGB(blue, 2)); 3339 return (int) result; 3340} 3341 3342static void 3343formatDirectColor(char *target, XtermWidget xw, unsigned value) 3344{ 3345#define fRGB(name, shift) \ 3346 (value & xw->visInfo->name ## _mask) >> xw->rgb_shifts[shift] 3347 sprintf(target, "%lu:%lu:%lu", fRGB(red, 0), fRGB(green, 1), fRGB(blue, 2)); 3348} 3349#endif /* OPT_DIRECT_COLOR */ 3350 3351#define fg2SGR(n) \ 3352 (n) >= 8 ? 9 : 3, \ 3353 (n) >= 8 ? (n) - 8 : (n) 3354#define bg2SGR(n) \ 3355 (n) >= 8 ? 10 : 4, \ 3356 (n) >= 8 ? (n) - 8 : (n) 3357 3358#define EndOf(s) (s) + strlen(s) 3359 3360char * 3361xtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg) 3362{ 3363 TScreen *screen = TScreenOf(xw); 3364 char *msg = target; 3365 3366 strcpy(target, "0"); 3367 if (attr & BOLD) 3368 strcat(msg, ";1"); 3369 if (attr & UNDERLINE) 3370 strcat(msg, ";4"); 3371 if (attr & BLINK) 3372 strcat(msg, ";5"); 3373 if (attr & INVERSE) 3374 strcat(msg, ";7"); 3375 if (attr & INVISIBLE) 3376 strcat(msg, ";8"); 3377#if OPT_WIDE_ATTRS 3378 if (attr & ATR_FAINT) 3379 strcat(msg, ";2"); 3380 if (attr & ATR_ITALIC) 3381 strcat(msg, ";3"); 3382 if (attr & ATR_STRIKEOUT) 3383 strcat(msg, ";9"); 3384 if (attr & ATR_DBL_UNDER) 3385 strcat(msg, ";21"); 3386#endif 3387#if OPT_256_COLORS || OPT_88_COLORS 3388 if_OPT_ISO_COLORS(screen, { 3389 if (attr & FG_COLOR) { 3390 if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), { 3391 strcat(msg, ";38:2::"); 3392 formatDirectColor(EndOf(msg), xw, (unsigned) fg); 3393 }) if (fg >= 16) { 3394 sprintf(EndOf(msg), ";38:5:%d", fg); 3395 } else { 3396 sprintf(EndOf(msg), ";%d%d", fg2SGR(fg)); 3397 } 3398 } 3399 if (attr & BG_COLOR) { 3400 if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), { 3401 strcat(msg, ";48:2::"); 3402 formatDirectColor(EndOf(msg), xw, (unsigned) bg); 3403 }) if (bg >= 16) { 3404 sprintf(EndOf(msg), ";48:5:%d", bg); 3405 } else { 3406 sprintf(EndOf(msg), ";%d%d", bg2SGR(bg)); 3407 } 3408 } 3409 }); 3410#elif OPT_ISO_COLORS 3411 if_OPT_ISO_COLORS(screen, { 3412 if (attr & FG_COLOR) { 3413 sprintf(EndOf(msg), ";%d%d", fg2SGR(fg)); 3414 } 3415 if (attr & BG_COLOR) { 3416 sprintf(EndOf(msg), ";%d%d", bg2SGR(bg)); 3417 } 3418 }); 3419#else 3420 (void) screen; 3421 (void) fg; 3422 (void) bg; 3423#endif 3424 return target; 3425} 3426 3427#if OPT_PASTE64 3428static void 3429ManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final) 3430{ 3431#define PDATA(a,b) { a, #b } 3432 static struct { 3433 char given; 3434 String result; 3435 } table[] = { 3436 PDATA('s', SELECT), 3437 PDATA('p', PRIMARY), 3438 PDATA('q', SECONDARY), 3439 PDATA('c', CLIPBOARD), 3440 PDATA('0', CUT_BUFFER0), 3441 PDATA('1', CUT_BUFFER1), 3442 PDATA('2', CUT_BUFFER2), 3443 PDATA('3', CUT_BUFFER3), 3444 PDATA('4', CUT_BUFFER4), 3445 PDATA('5', CUT_BUFFER5), 3446 PDATA('6', CUT_BUFFER6), 3447 PDATA('7', CUT_BUFFER7), 3448 }; 3449 3450 const char *base = buf; 3451 Cardinal j, n = 0; 3452 3453 TRACE(("Manipulate selection data\n")); 3454 3455 while (*buf != ';' && *buf != '\0') { 3456 ++buf; 3457 } 3458 3459 if (*buf == ';') { 3460 char *used; 3461 3462 *buf++ = '\0'; 3463 3464 if (*base == '\0') 3465 base = "s0"; 3466 3467 if ((used = x_strdup(base)) != 0) { 3468 String *select_args; 3469 3470 if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) { 3471 while (*base != '\0') { 3472 for (j = 0; j < XtNumber(table); ++j) { 3473 if (*base == table[j].given) { 3474 used[n] = *base; 3475 select_args[n++] = table[j].result; 3476 TRACE(("atom[%d] %s\n", n, table[j].result)); 3477 break; 3478 } 3479 } 3480 ++base; 3481 } 3482 used[n] = 0; 3483 3484 if (!strcmp(buf, "?")) { 3485 if (AllowWindowOps(xw, ewGetSelection)) { 3486 TRACE(("Getting selection\n")); 3487 unparseputc1(xw, ANSI_OSC); 3488 unparseputs(xw, "52"); 3489 unparseputc(xw, ';'); 3490 3491 unparseputs(xw, used); 3492 unparseputc(xw, ';'); 3493 3494 /* Tell xtermGetSelection data is base64 encoded */ 3495 screen->base64_paste = n; 3496 screen->base64_final = final; 3497 3498 screen->selection_time = 3499 XtLastTimestampProcessed(TScreenOf(xw)->display); 3500 3501 /* terminator will be written in this call */ 3502 xtermGetSelection((Widget) xw, 3503 screen->selection_time, 3504 select_args, n, 3505 NULL); 3506 /* 3507 * select_args is used via SelectionReceived, cannot 3508 * free it here. 3509 */ 3510 } else { 3511 free(select_args); 3512 } 3513 } else { 3514 if (AllowWindowOps(xw, ewSetSelection)) { 3515 char *old = buf; 3516 3517 TRACE(("Setting selection(%s) with %s\n", used, buf)); 3518 screen->selection_time = 3519 XtLastTimestampProcessed(TScreenOf(xw)->display); 3520 3521 for (j = 0; j < n; ++j) { 3522 buf = old; 3523 ClearSelectionBuffer(screen, select_args[j]); 3524 while (*buf != '\0') { 3525 AppendToSelectionBuffer(screen, 3526 CharOf(*buf++), 3527 select_args[j]); 3528 } 3529 } 3530 CompleteSelection(xw, select_args, n); 3531 } 3532 free(select_args); 3533 } 3534 } 3535 free(used); 3536 } 3537 } 3538} 3539#endif /* OPT_PASTE64 */ 3540 3541/***====================================================================***/ 3542 3543#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \ 3544 || (xw->screen.utf8_title) \ 3545 || (xw->screen.c1_printable)) 3546 3547static Bool 3548xtermIsPrintable(XtermWidget xw, Char **bufp, Char *last) 3549{ 3550 TScreen *screen = TScreenOf(xw); 3551 Bool result = False; 3552 Char *cp = *bufp; 3553 Char *next = cp; 3554 3555 (void) screen; 3556 (void) last; 3557 3558#if OPT_WIDE_CHARS 3559 if (xtermEnvUTF8() && IsSetUtf8Title(xw)) { 3560 PtyData data; 3561 3562 if (decodeUtf8(screen, fakePtyData(&data, cp, last))) { 3563 if (data.utf_data != UCS_REPL 3564 && (data.utf_data >= 128 || 3565 ansi_table[data.utf_data] == CASE_PRINT)) { 3566 next += (data.utf_size - 1); 3567 result = True; 3568 } else { 3569 result = False; 3570 } 3571 } else { 3572 result = False; 3573 } 3574 } else 3575#endif 3576#if OPT_C1_PRINT 3577 if (screen->c1_printable 3578 && (*cp >= 128 && *cp < 160)) { 3579 result = True; 3580 } else 3581#endif 3582 if (ansi_table[*cp] == CASE_PRINT) { 3583 result = True; 3584 } 3585 *bufp = next; 3586 return result; 3587} 3588 3589/***====================================================================***/ 3590 3591/* 3592 * Enum corresponding to the actual OSC codes rather than the internal 3593 * array indices. Compare with TermColors. 3594 */ 3595typedef enum { 3596 OSC_TEXT_FG = 10 3597 ,OSC_TEXT_BG 3598 ,OSC_TEXT_CURSOR 3599 ,OSC_MOUSE_FG 3600 ,OSC_MOUSE_BG 3601#if OPT_TEK4014 3602 ,OSC_TEK_FG = 15 3603 ,OSC_TEK_BG 3604#endif 3605#if OPT_HIGHLIGHT_COLOR 3606 ,OSC_HIGHLIGHT_BG = 17 3607#endif 3608#if OPT_TEK4014 3609 ,OSC_TEK_CURSOR = 18 3610#endif 3611#if OPT_HIGHLIGHT_COLOR 3612 ,OSC_HIGHLIGHT_FG = 19 3613#endif 3614 ,OSC_NCOLORS 3615} OscTextColors; 3616 3617/* 3618 * Map codes to OSC controls that can reset colors. 3619 */ 3620#define OSC_RESET 100 3621#define OSC_Reset(code) (code) + OSC_RESET 3622 3623static Bool 3624GetOldColors(XtermWidget xw) 3625{ 3626 if (xw->work.oldColors == NULL) { 3627 int i; 3628 3629 xw->work.oldColors = TypeXtMalloc(ScrnColors); 3630 if (xw->work.oldColors == NULL) { 3631 xtermWarning("allocation failure in GetOldColors\n"); 3632 return (False); 3633 } 3634 xw->work.oldColors->which = 0; 3635 for (i = 0; i < NCOLORS; i++) { 3636 xw->work.oldColors->colors[i] = 0; 3637 xw->work.oldColors->names[i] = NULL; 3638 } 3639 GetColors(xw, xw->work.oldColors); 3640 } 3641 return (True); 3642} 3643 3644static int 3645oppositeColor(XtermWidget xw, int n) 3646{ 3647 Boolean reversed = (xw->misc.re_verse); 3648 3649 switch (n) { 3650 case TEXT_FG: 3651 n = reversed ? TEXT_FG : TEXT_BG; 3652 break; 3653 case TEXT_BG: 3654 n = reversed ? TEXT_BG : TEXT_FG; 3655 break; 3656 case MOUSE_FG: 3657 n = MOUSE_BG; 3658 break; 3659 case MOUSE_BG: 3660 n = MOUSE_FG; 3661 break; 3662#if OPT_TEK4014 3663 case TEK_FG: 3664 n = reversed ? TEK_FG : TEK_BG; 3665 break; 3666 case TEK_BG: 3667 n = reversed ? TEK_BG : TEK_FG; 3668 break; 3669#endif 3670#if OPT_HIGHLIGHT_COLOR 3671 case HIGHLIGHT_FG: 3672 n = HIGHLIGHT_BG; 3673 break; 3674 case HIGHLIGHT_BG: 3675 n = HIGHLIGHT_FG; 3676 break; 3677#endif 3678 default: 3679 break; 3680 } 3681 return n; 3682} 3683 3684static Bool 3685ReportColorRequest(XtermWidget xw, int ndx, int final) 3686{ 3687 Bool result = False; 3688 3689 if (AllowColorOps(xw, ecGetColor)) { 3690 XColor color; 3691 Colormap cmap = xw->core.colormap; 3692 char buffer[80]; 3693 3694 /* 3695 * ChangeColorsRequest() has "always" chosen the opposite color when 3696 * reverse-video is set. Report this as the original color index, but 3697 * reporting the opposite color which would be used. 3698 */ 3699 int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx; 3700 3701 GetOldColors(xw); 3702 color.pixel = xw->work.oldColors->colors[ndx]; 3703 XQueryColor(TScreenOf(xw)->display, cmap, &color); 3704 sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10, 3705 color.red, 3706 color.green, 3707 color.blue); 3708 TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n", 3709 ndx, xw->work.oldColors->colors[ndx], buffer)); 3710 unparseputc1(xw, ANSI_OSC); 3711 unparseputs(xw, buffer); 3712 unparseputc1(xw, final); 3713 result = True; 3714 } 3715 return result; 3716} 3717 3718static Bool 3719UpdateOldColors(XtermWidget xw, ScrnColors * pNew) 3720{ 3721 int i; 3722 3723 /* if we were going to free old colors, this would be the place to 3724 * do it. I've decided not to (for now), because it seems likely 3725 * that we'd have a small set of colors we use over and over, and that 3726 * we could save some overhead this way. The only case in which this 3727 * (clearly) fails is if someone is trying a boatload of colors, in 3728 * which case they can restart xterm 3729 */ 3730 for (i = 0; i < NCOLORS; i++) { 3731 if (COLOR_DEFINED(pNew, i)) { 3732 if (xw->work.oldColors->names[i] != NULL) { 3733 XtFree(xw->work.oldColors->names[i]); 3734 xw->work.oldColors->names[i] = NULL; 3735 } 3736 if (pNew->names[i]) { 3737 xw->work.oldColors->names[i] = pNew->names[i]; 3738 } 3739 xw->work.oldColors->colors[i] = pNew->colors[i]; 3740 } 3741 } 3742 return (True); 3743} 3744 3745/* 3746 * OSC codes are constant, but the indices for the color arrays depend on how 3747 * xterm is compiled. 3748 */ 3749static int 3750OscToColorIndex(OscTextColors mode) 3751{ 3752 int result = 0; 3753 3754#define CASE(name) case OSC_##name: result = name; break 3755 switch (mode) { 3756 CASE(TEXT_FG); 3757 CASE(TEXT_BG); 3758 CASE(TEXT_CURSOR); 3759 CASE(MOUSE_FG); 3760 CASE(MOUSE_BG); 3761#if OPT_TEK4014 3762 CASE(TEK_FG); 3763 CASE(TEK_BG); 3764#endif 3765#if OPT_HIGHLIGHT_COLOR 3766 CASE(HIGHLIGHT_BG); 3767 CASE(HIGHLIGHT_FG); 3768#endif 3769#if OPT_TEK4014 3770 CASE(TEK_CURSOR); 3771#endif 3772 case OSC_NCOLORS: 3773 break; 3774 } 3775 return result; 3776} 3777 3778static Bool 3779ChangeColorsRequest(XtermWidget xw, 3780 int start, 3781 char *names, 3782 int final) 3783{ 3784 Bool result = False; 3785 ScrnColors newColors; 3786 3787 TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names)); 3788 3789 if (GetOldColors(xw)) { 3790 int i; 3791 int queried = 0; 3792 3793 newColors.which = 0; 3794 for (i = 0; i < NCOLORS; i++) { 3795 newColors.names[i] = NULL; 3796 } 3797 for (i = start; i < OSC_NCOLORS; i++) { 3798 int ndx = OscToColorIndex((OscTextColors) i); 3799 if (xw->misc.re_verse) 3800 ndx = oppositeColor(xw, ndx); 3801 3802 if (IsEmpty(names)) { 3803 newColors.names[ndx] = NULL; 3804 } else { 3805 char *thisName = ((names[0] == ';') ? NULL : names); 3806 3807 names = strchr(names, ';'); 3808 if (names != NULL) { 3809 *names++ = '\0'; 3810 } 3811 if (thisName != 0) { 3812 if (!strcmp(thisName, "?")) { 3813 if (ReportColorRequest(xw, ndx, final)) 3814 ++queried; 3815 } else if (!xw->work.oldColors->names[ndx] 3816 || strcmp(thisName, xw->work.oldColors->names[ndx])) { 3817 AllocateTermColor(xw, &newColors, ndx, thisName, False); 3818 } 3819 } 3820 } 3821 } 3822 3823 if (newColors.which != 0) { 3824 ChangeColors(xw, &newColors); 3825 UpdateOldColors(xw, &newColors); 3826 } else if (queried) { 3827 unparse_end(xw); 3828 } 3829 result = True; 3830 } 3831 return result; 3832} 3833 3834static Bool 3835ResetColorsRequest(XtermWidget xw, 3836 int code) 3837{ 3838 Bool result = False; 3839 3840 (void) xw; 3841 (void) code; 3842 3843 TRACE(("ResetColorsRequest code=%d\n", code)); 3844 3845#if OPT_COLOR_RES 3846 if (GetOldColors(xw)) { 3847 ScrnColors newColors; 3848 const char *thisName; 3849 int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET)); 3850 3851 if (xw->misc.re_verse) 3852 ndx = oppositeColor(xw, ndx); 3853 3854 thisName = xw->screen.Tcolors[ndx].resource; 3855 3856 newColors.which = 0; 3857 newColors.names[ndx] = NULL; 3858 3859 if (thisName != 0 3860 && xw->work.oldColors->names[ndx] != 0 3861 && strcmp(thisName, xw->work.oldColors->names[ndx])) { 3862 AllocateTermColor(xw, &newColors, ndx, thisName, False); 3863 3864 if (newColors.which != 0) { 3865 ChangeColors(xw, &newColors); 3866 UpdateOldColors(xw, &newColors); 3867 } 3868 } 3869 result = True; 3870 } 3871#endif 3872 return result; 3873} 3874 3875#if OPT_SHIFT_FONTS 3876/* 3877 * Initially, 'source' points to '#' or '?'. 3878 * 3879 * Look for an optional sign and optional number. If those are found, lookup 3880 * the corresponding menu font entry. 3881 */ 3882static int 3883ParseShiftedFont(XtermWidget xw, String source, String *target) 3884{ 3885 TScreen *screen = TScreenOf(xw); 3886 int num = screen->menu_font_number; 3887 int rel = 0; 3888 3889 if (*++source == '+') { 3890 rel = 1; 3891 source++; 3892 } else if (*source == '-') { 3893 rel = -1; 3894 source++; 3895 } 3896 3897 if (isdigit(CharOf(*source))) { 3898 int val = atoi(source); 3899 if (rel > 0) 3900 rel = val; 3901 else if (rel < 0) 3902 rel = -val; 3903 else 3904 num = val; 3905 } 3906 3907 if (rel != 0) { 3908 num = lookupRelativeFontSize(xw, 3909 screen->menu_font_number, rel); 3910 3911 } 3912 TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source)); 3913 *target = source; 3914 return num; 3915} 3916 3917static void 3918QueryFontRequest(XtermWidget xw, String buf, int final) 3919{ 3920 if (AllowFontOps(xw, efGetFont)) { 3921 TScreen *screen = TScreenOf(xw); 3922 Bool success = True; 3923 int num; 3924 String base = buf + 1; 3925 const char *name = 0; 3926 3927 num = ParseShiftedFont(xw, buf, &buf); 3928 if (num < 0 3929 || num > fontMenu_lastBuiltin) { 3930 Bell(xw, XkbBI_MinorError, 0); 3931 success = False; 3932 } else { 3933#if OPT_RENDERFONT 3934 if (UsingRenderFont(xw)) { 3935 name = getFaceName(xw, False); 3936 } else 3937#endif 3938 if ((name = screen->MenuFontName(num)) == 0) { 3939 success = False; 3940 } 3941 } 3942 3943 unparseputc1(xw, ANSI_OSC); 3944 unparseputs(xw, "50"); 3945 3946 if (success) { 3947 unparseputc(xw, ';'); 3948 if (buf >= base) { 3949 /* identify the font-entry, unless it is the current one */ 3950 if (*buf != '\0') { 3951 char temp[10]; 3952 3953 unparseputc(xw, '#'); 3954 sprintf(temp, "%d", num); 3955 unparseputs(xw, temp); 3956 if (*name != '\0') 3957 unparseputc(xw, ' '); 3958 } 3959 } 3960 unparseputs(xw, name); 3961 } 3962 3963 unparseputc1(xw, final); 3964 unparse_end(xw); 3965 } 3966} 3967 3968static void 3969ChangeFontRequest(XtermWidget xw, String buf) 3970{ 3971 if (AllowFontOps(xw, efSetFont)) { 3972 TScreen *screen = TScreenOf(xw); 3973 Bool success = True; 3974 int num; 3975 VTFontNames fonts; 3976 char *name; 3977 3978 /* 3979 * If the font specification is a "#", followed by an optional sign and 3980 * optional number, lookup the corresponding menu font entry. 3981 * 3982 * Further, if the "#", etc., is followed by a font name, use that 3983 * to load the font entry. 3984 */ 3985 if (*buf == '#') { 3986 num = ParseShiftedFont(xw, buf, &buf); 3987 3988 if (num < 0 3989 || num > fontMenu_lastBuiltin) { 3990 Bell(xw, XkbBI_MinorError, 0); 3991 success = False; 3992 } else { 3993 /* 3994 * Skip past the optional number, and any whitespace to look 3995 * for a font specification within the control. 3996 */ 3997 while (isdigit(CharOf(*buf))) { 3998 ++buf; 3999 } 4000 while (isspace(CharOf(*buf))) { 4001 ++buf; 4002 } 4003#if OPT_RENDERFONT 4004 if (UsingRenderFont(xw)) { 4005 /* EMPTY */ 4006 /* there is only one font entry to load */ 4007 ; 4008 } else 4009#endif 4010 { 4011 /* 4012 * Normally there is no font specified in the control. 4013 * But if there is, simply overwrite the font entry. 4014 */ 4015 if (*buf == '\0') { 4016 if ((buf = screen->MenuFontName(num)) == 0) { 4017 success = False; 4018 } 4019 } 4020 } 4021 } 4022 } else { 4023 num = screen->menu_font_number; 4024 } 4025 name = x_strtrim(buf); 4026 if (screen->EscapeFontName()) { 4027 FREE_STRING(screen->EscapeFontName()); 4028 screen->EscapeFontName() = 0; 4029 } 4030 if (success && !IsEmpty(name)) { 4031#if OPT_RENDERFONT 4032 if (UsingRenderFont(xw)) { 4033 setFaceName(xw, name); 4034 xtermUpdateFontInfo(xw, True); 4035 } else 4036#endif 4037 { 4038 memset(&fonts, 0, sizeof(fonts)); 4039 fonts.f_n = name; 4040 SetVTFont(xw, num, True, &fonts); 4041 if (num == screen->menu_font_number && 4042 num != fontMenu_fontescape) { 4043 screen->EscapeFontName() = x_strdup(name); 4044 } 4045 } 4046 } else { 4047 Bell(xw, XkbBI_MinorError, 0); 4048 } 4049 update_font_escape(); 4050 free(name); 4051 } 4052} 4053#endif /* OPT_SHIFT_FONTS */ 4054 4055/***====================================================================***/ 4056 4057void 4058do_osc(XtermWidget xw, Char *oscbuf, size_t len, int final) 4059{ 4060 TScreen *screen = TScreenOf(xw); 4061 int mode; 4062 Char *cp; 4063 int state = 0; 4064 char *buf = 0; 4065 char temp[2]; 4066#if OPT_ISO_COLORS 4067 int ansi_colors = 0; 4068#endif 4069 Bool need_data = True; 4070 Bool optional_data = False; 4071 4072 TRACE(("do_osc %s\n", oscbuf)); 4073 4074 (void) screen; 4075 4076 /* 4077 * Lines should be of the form <OSC> number ; string <ST>, however 4078 * older xterms can accept <BEL> as a final character. We will respond 4079 * with the same final character as the application sends to make this 4080 * work better with shell scripts, which may have trouble reading an 4081 * <ESC><backslash>, which is the 7-bit equivalent to <ST>. 4082 */ 4083 mode = 0; 4084 for (cp = oscbuf; *cp != '\0'; cp++) { 4085 switch (state) { 4086 case 0: 4087 if (isdigit(*cp)) { 4088 mode = 10 * mode + (*cp - '0'); 4089 if (mode > 65535) { 4090 TRACE(("do_osc found unknown mode %d\n", mode)); 4091 return; 4092 } 4093 break; 4094 } else { 4095 switch (*cp) { 4096 case 'I': 4097 xtermLoadIcon(xw, (char *) ++cp); 4098 return; 4099 case 'l': 4100 ChangeTitle(xw, (char *) ++cp); 4101 return; 4102 case 'L': 4103 ChangeIconName(xw, (char *) ++cp); 4104 return; 4105 } 4106 } 4107 /* FALLTHRU */ 4108 case 1: 4109 if (*cp != ';') { 4110 TRACE(("do_osc did not find semicolon offset %d\n", 4111 (int) (cp - oscbuf))); 4112 return; 4113 } 4114 state = 2; 4115 break; 4116 case 2: 4117 buf = (char *) cp; 4118 state = 3; 4119 /* FALLTHRU */ 4120 default: 4121 if (!xtermIsPrintable(xw, &cp, oscbuf + len)) { 4122 switch (mode) { 4123 case 0: 4124 case 1: 4125 case 2: 4126 break; 4127 default: 4128 TRACE(("do_osc found nonprinting char %02X offset %d\n", 4129 CharOf(*cp), 4130 (int) (cp - oscbuf))); 4131 return; 4132 } 4133 } 4134 } 4135 } 4136 4137 /* 4138 * Check if the palette changed and there are no more immediate changes 4139 * that could be deferred to the next repaint. 4140 */ 4141 if (xw->work.palette_changed) { 4142 switch (mode) { 4143 case 03: /* change X property */ 4144 case 30: /* Konsole (unused) */ 4145 case 31: /* Konsole (unused) */ 4146 case 50: /* font operations */ 4147 case 51: /* Emacs (unused) */ 4148#if OPT_PASTE64 4149 case 52: /* selection data */ 4150#endif 4151 TRACE(("forced repaint after palette changed\n")); 4152 xw->work.palette_changed = False; 4153 xtermRepaint(xw); 4154 break; 4155 default: 4156 xtermNeedSwap(xw, 1); 4157 break; 4158 } 4159 } 4160 4161 /* 4162 * Most OSC controls other than resets require data. Handle the others as 4163 * a special case. 4164 */ 4165 switch (mode) { 4166 case 50: 4167#if OPT_ISO_COLORS 4168 case OSC_Reset(4): 4169 case OSC_Reset(5): 4170 need_data = False; 4171 optional_data = True; 4172 break; 4173 case OSC_Reset(OSC_TEXT_FG): 4174 case OSC_Reset(OSC_TEXT_BG): 4175 case OSC_Reset(OSC_TEXT_CURSOR): 4176 case OSC_Reset(OSC_MOUSE_FG): 4177 case OSC_Reset(OSC_MOUSE_BG): 4178#if OPT_HIGHLIGHT_COLOR 4179 case OSC_Reset(OSC_HIGHLIGHT_BG): 4180 case OSC_Reset(OSC_HIGHLIGHT_FG): 4181#endif 4182#if OPT_TEK4014 4183 case OSC_Reset(OSC_TEK_FG): 4184 case OSC_Reset(OSC_TEK_BG): 4185 case OSC_Reset(OSC_TEK_CURSOR): 4186#endif 4187 need_data = False; 4188 break; 4189#endif 4190 default: 4191 break; 4192 } 4193 4194 /* 4195 * Check if we have data when we want, and not when we do not want it. 4196 * Either way, that is a malformed control sequence, and will be ignored. 4197 */ 4198 if (IsEmpty(buf)) { 4199 if (need_data) { 4200 TRACE(("do_osc found no data\n")); 4201 return; 4202 } 4203 temp[0] = '\0'; 4204 buf = temp; 4205 } else if (!need_data && !optional_data) { 4206 TRACE(("do_osc found unwanted data\n")); 4207 return; 4208 } 4209 4210 switch (mode) { 4211 case 0: /* new icon name and title */ 4212 ChangeIconName(xw, buf); 4213 ChangeTitle(xw, buf); 4214 break; 4215 4216 case 1: /* new icon name only */ 4217 ChangeIconName(xw, buf); 4218 break; 4219 4220 case 2: /* new title only */ 4221 ChangeTitle(xw, buf); 4222 break; 4223 4224#ifdef notdef 4225 case 3: /* change X property */ 4226 if (AllowWindowOps(xw, ewSetXprop)) 4227 ChangeXprop(buf); 4228 break; 4229#endif 4230#if OPT_ISO_COLORS 4231 case 5: 4232 ansi_colors = NUM_ANSI_COLORS; 4233 /* FALLTHRU */ 4234 case 4: 4235 if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final)) 4236 xw->work.palette_changed = True; 4237 break; 4238 case 6: 4239 /* FALLTHRU */ 4240 case OSC_Reset(6): 4241 TRACE(("parse colorXXMode:%s\n", buf)); 4242 while (*buf != '\0') { 4243 long which = 0; 4244 long value = 0; 4245 char *next; 4246 if (*buf == ';') { 4247 ++buf; 4248 } else { 4249 which = strtol(buf, &next, 10); 4250 if (!PartS2L(buf, next) || (which < 0)) 4251 break; 4252 buf = next; 4253 if (*buf == ';') 4254 ++buf; 4255 } 4256 if (*buf == ';') { 4257 ++buf; 4258 } else { 4259 value = strtol(buf, &next, 10); 4260 if (!PartS2L(buf, next) || (value < 0)) 4261 break; 4262 buf = next; 4263 if (*buf == ';') 4264 ++buf; 4265 } 4266 TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value)); 4267 switch (which) { 4268 case 0: 4269 screen->colorBDMode = (value != 0); 4270 break; 4271 case 1: 4272 screen->colorULMode = (value != 0); 4273 break; 4274 case 2: 4275 screen->colorBLMode = (value != 0); 4276 break; 4277 case 3: 4278 screen->colorRVMode = (value != 0); 4279 break; 4280#if OPT_WIDE_ATTRS 4281 case 4: 4282 screen->colorITMode = (value != 0); 4283 break; 4284#endif 4285 default: 4286 TRACE(("...unknown colorXXMode\n")); 4287 break; 4288 } 4289 } 4290 break; 4291 case OSC_Reset(5): 4292 ansi_colors = NUM_ANSI_COLORS; 4293 /* FALLTHRU */ 4294 case OSC_Reset(4): 4295 if (ResetAnsiColorRequest(xw, buf, ansi_colors)) 4296 xw->work.palette_changed = True; 4297 break; 4298#endif 4299 case OSC_TEXT_FG: 4300 case OSC_TEXT_BG: 4301 case OSC_TEXT_CURSOR: 4302 case OSC_MOUSE_FG: 4303 case OSC_MOUSE_BG: 4304#if OPT_HIGHLIGHT_COLOR 4305 case OSC_HIGHLIGHT_BG: 4306 case OSC_HIGHLIGHT_FG: 4307#endif 4308#if OPT_TEK4014 4309 case OSC_TEK_FG: 4310 case OSC_TEK_BG: 4311 case OSC_TEK_CURSOR: 4312#endif 4313 if (xw->misc.dynamicColors) { 4314 ChangeColorsRequest(xw, mode, buf, final); 4315 } 4316 break; 4317 case OSC_Reset(OSC_TEXT_FG): 4318 case OSC_Reset(OSC_TEXT_BG): 4319 case OSC_Reset(OSC_TEXT_CURSOR): 4320 case OSC_Reset(OSC_MOUSE_FG): 4321 case OSC_Reset(OSC_MOUSE_BG): 4322#if OPT_HIGHLIGHT_COLOR 4323 case OSC_Reset(OSC_HIGHLIGHT_BG): 4324 case OSC_Reset(OSC_HIGHLIGHT_FG): 4325#endif 4326#if OPT_TEK4014 4327 case OSC_Reset(OSC_TEK_FG): 4328 case OSC_Reset(OSC_TEK_BG): 4329 case OSC_Reset(OSC_TEK_CURSOR): 4330#endif 4331 if (xw->misc.dynamicColors) { 4332 ResetColorsRequest(xw, mode); 4333 } 4334 break; 4335 4336 case 22: 4337 xtermSetupPointer(xw, buf); 4338 break; 4339 4340 case 30: 4341 case 31: 4342 /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ 4343 break; 4344 4345#ifdef ALLOWLOGGING 4346 case 46: /* new log file */ 4347#ifdef ALLOWLOGFILECHANGES 4348 /* 4349 * Warning, enabling this feature allows people to overwrite 4350 * arbitrary files accessible to the person running xterm. 4351 */ 4352 if (strcmp(buf, "?")) { 4353 char *bp; 4354 if ((bp = x_strdup(buf)) != NULL) { 4355 free(screen->logfile); 4356 screen->logfile = bp; 4357 break; 4358 } 4359 } 4360#endif 4361 Bell(xw, XkbBI_Info, 0); 4362 Bell(xw, XkbBI_Info, 0); 4363 break; 4364#endif /* ALLOWLOGGING */ 4365 4366 case 50: 4367#if OPT_SHIFT_FONTS 4368 if (*buf == '?') { 4369 QueryFontRequest(xw, buf, final); 4370 } else if (xw->misc.shift_fonts) { 4371 ChangeFontRequest(xw, buf); 4372 } 4373#endif /* OPT_SHIFT_FONTS */ 4374 break; 4375 case 51: 4376 /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ 4377 break; 4378 4379#if OPT_PASTE64 4380 case 52: 4381 ManipulateSelectionData(xw, screen, buf, final); 4382 break; 4383#endif 4384 /* 4385 * One could write code to send back the display and host names, 4386 * but that could potentially open a fairly nasty security hole. 4387 */ 4388 default: 4389 TRACE(("do_osc - unrecognized code\n")); 4390 break; 4391 } 4392 unparse_end(xw); 4393} 4394 4395/* 4396 * Parse one nibble of a hex byte from the OSC string. We have removed the 4397 * string-terminator (replacing it with a null), so the only other delimiter 4398 * that is expected is semicolon. Ignore other characters (Ray Neuman says 4399 * "real" terminals accept commas in the string definitions). 4400 */ 4401static int 4402udk_value(const char **cp) 4403{ 4404 int result = -1; 4405 4406 for (;;) { 4407 int c; 4408 4409 if ((c = **cp) != '\0') 4410 *cp = *cp + 1; 4411 if (c == ';' || c == '\0') 4412 break; 4413 if ((result = x_hex2int(c)) >= 0) 4414 break; 4415 } 4416 4417 return result; 4418} 4419 4420void 4421reset_decudk(XtermWidget xw) 4422{ 4423 int n; 4424 for (n = 0; n < MAX_UDK; n++) { 4425 FreeAndNull(xw->work.user_keys[n].str); 4426 xw->work.user_keys[n].len = 0; 4427 } 4428} 4429 4430/* 4431 * Parse the data for DECUDK (user-defined keys). 4432 */ 4433static void 4434parse_decudk(XtermWidget xw, const char *cp) 4435{ 4436 while (*cp) { 4437 const char *base = cp; 4438 char *str = malloc(strlen(cp) + 3); 4439 unsigned key = 0; 4440 int len = 0; 4441 4442 if (str == NULL) 4443 break; 4444 4445 while (isdigit(CharOf(*cp))) 4446 key = (key * 10) + (unsigned) (*cp++ - '0'); 4447 4448 if (*cp == '/') { 4449 int lo, hi; 4450 4451 cp++; 4452 while ((hi = udk_value(&cp)) >= 0 4453 && (lo = udk_value(&cp)) >= 0) { 4454 str[len++] = (char) ((hi << 4) | lo); 4455 } 4456 } 4457 if (len > 0 && key < MAX_UDK) { 4458 str[len] = '\0'; 4459 free(xw->work.user_keys[key].str); 4460 xw->work.user_keys[key].str = str; 4461 xw->work.user_keys[key].len = len; 4462 TRACE(("parse_decudk %d:%.*s\n", key, len, str)); 4463 } else { 4464 free(str); 4465 } 4466 if (*cp == ';') 4467 cp++; 4468 if (cp == base) /* badly-formed sequence - bail out */ 4469 break; 4470 } 4471} 4472 4473/* 4474 * Parse numeric parameters. Normally we use a state machine to simplify 4475 * interspersing with control characters, but have the string already. 4476 */ 4477static void 4478parse_ansi_params(ANSI *params, const char **string) 4479{ 4480 const char *cp = *string; 4481 ParmType nparam = 0; 4482 int last_empty = 1; 4483 4484 memset(params, 0, sizeof(*params)); 4485 while (*cp != '\0') { 4486 Char ch = CharOf(*cp++); 4487 4488 if (isdigit(ch)) { 4489 last_empty = 0; 4490 if (nparam < NPARAM) { 4491 params->a_param[nparam] = 4492 (ParmType) ((params->a_param[nparam] * 10) 4493 + (ch - '0')); 4494 } 4495 } else if (ch == ';') { 4496 last_empty = 1; 4497 nparam++; 4498 } else if (ch < 32) { 4499 /* EMPTY */ ; 4500 } else { 4501 /* should be 0x30 to 0x7e */ 4502 params->a_final = ch; 4503 break; 4504 } 4505 } 4506 4507 *string = cp; 4508 if (!last_empty) 4509 nparam++; 4510 if (nparam > NPARAM) 4511 params->a_nparam = NPARAM; 4512 else 4513 params->a_nparam = nparam; 4514} 4515 4516#if OPT_TRACE 4517#define SOFT_WIDE 10 4518#define SOFT_HIGH 20 4519 4520static void 4521parse_decdld(ANSI *params, const char *string) 4522{ 4523 char DscsName[8]; 4524 int len; 4525 int Pfn = params->a_param[0]; 4526 int Pcn = params->a_param[1]; 4527 int Pe = params->a_param[2]; 4528 int Pcmw = params->a_param[3]; 4529 int Pw = params->a_param[4]; 4530 int Pt = params->a_param[5]; 4531 int Pcmh = params->a_param[6]; 4532 int Pcss = params->a_param[7]; 4533 4534 int start_char = Pcn + 0x20; 4535 int char_wide = ((Pcmw == 0) 4536 ? (Pcss ? 6 : 10) 4537 : (Pcmw > 4 4538 ? Pcmw 4539 : (Pcmw + 3))); 4540 int char_high = ((Pcmh == 0) 4541 ? ((Pcmw >= 2 && Pcmw <= 4) 4542 ? 10 4543 : 20) 4544 : Pcmh); 4545 Char ch; 4546 Char bits[SOFT_HIGH][SOFT_WIDE]; 4547 Bool first = True; 4548 Bool prior = False; 4549 int row = 0, col = 0; 4550 4551 TRACE(("Parsing DECDLD\n")); 4552 TRACE((" font number %d\n", Pfn)); 4553 TRACE((" starting char %d\n", Pcn)); 4554 TRACE((" erase control %d\n", Pe)); 4555 TRACE((" char-width %d\n", Pcmw)); 4556 TRACE((" font-width %d\n", Pw)); 4557 TRACE((" text/full %d\n", Pt)); 4558 TRACE((" char-height %d\n", Pcmh)); 4559 TRACE((" charset-size %d\n", Pcss)); 4560 4561 if (Pfn > 1 4562 || Pcn > 95 4563 || Pe > 2 4564 || Pcmw > 10 4565 || Pcmw == 1 4566 || Pt > 2 4567 || Pcmh > 20 4568 || Pcss > 1 4569 || char_wide > SOFT_WIDE 4570 || char_high > SOFT_HIGH) { 4571 TRACE(("DECDLD illegal parameter\n")); 4572 return; 4573 } 4574 4575 len = 0; 4576 while (*string != '\0') { 4577 ch = CharOf(*string++); 4578 if (ch >= ANSI_SPA && ch <= 0x2f) { 4579 if (len < 2) 4580 DscsName[len++] = (char) ch; 4581 } else if (ch >= 0x30 && ch <= 0x7e) { 4582 DscsName[len++] = (char) ch; 4583 break; 4584 } 4585 } 4586 DscsName[len] = 0; 4587 TRACE((" Dscs name '%s'\n", DscsName)); 4588 4589 TRACE((" character matrix %dx%d\n", char_high, char_wide)); 4590 while (*string != '\0') { 4591 if (first) { 4592 TRACE(("Char %d:\n", start_char)); 4593 if (prior) { 4594 for (row = 0; row < char_high; ++row) { 4595 TRACE(("%.*s\n", char_wide, bits[row])); 4596 } 4597 } 4598 prior = False; 4599 first = False; 4600 for (row = 0; row < char_high; ++row) { 4601 for (col = 0; col < char_wide; ++col) { 4602 bits[row][col] = '.'; 4603 } 4604 } 4605 row = col = 0; 4606 } 4607 ch = CharOf(*string++); 4608 if (ch >= 0x3f && ch <= 0x7e) { 4609 int n; 4610 4611 ch = CharOf(ch - 0x3f); 4612 for (n = 0; n < 6; ++n) { 4613 bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.'); 4614 } 4615 col += 1; 4616 prior = True; 4617 } else if (ch == '/') { 4618 row += 6; 4619 col = 0; 4620 } else if (ch == ';') { 4621 first = True; 4622 ++start_char; 4623 } 4624 } 4625} 4626#else 4627#define parse_decdld(p,q) /* nothing */ 4628#endif 4629 4630#if OPT_DEC_RECTOPS 4631static const char * 4632skip_params(const char *cp) 4633{ 4634 while (*cp == ';' || (*cp >= '0' && *cp <= '9')) 4635 ++cp; 4636 return cp; 4637} 4638 4639static int 4640parse_int_param(const char **cp) 4641{ 4642 int result = 0; 4643 const char *s = *cp; 4644 while (*s != '\0') { 4645 if (*s == ';') { 4646 ++s; 4647 break; 4648 } else if (*s >= '0' && *s <= '9') { 4649 result = (result * 10) + (*s++ - '0'); 4650 } else { 4651 s += strlen(s); 4652 } 4653 } 4654 TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s)); 4655 *cp = s; 4656 return result; 4657} 4658 4659static int 4660parse_chr_param(const char **cp) 4661{ 4662 int result = 0; 4663 const char *s = *cp; 4664 if (*s != '\0') { 4665 if ((result = CharOf(*s++)) != 0) { 4666 if (*s == ';') { 4667 ++s; 4668 } else if (*s != '\0') { 4669 result = 0; 4670 } 4671 } 4672 } 4673 TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s)); 4674 *cp = s; 4675 return result; 4676} 4677 4678static void 4679restore_DECCIR(XtermWidget xw, const char *cp) 4680{ 4681 TScreen *screen = TScreenOf(xw); 4682 int value; 4683 4684 /* row */ 4685 if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen)) 4686 return; 4687 screen->cur_row = (value - 1); 4688 4689 /* column */ 4690 if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen)) 4691 return; 4692 screen->cur_col = (value - 1); 4693 4694 /* page */ 4695 if (parse_int_param(&cp) != 1) 4696 return; 4697 4698 /* rendition */ 4699 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) 4700 return; 4701 UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD)); 4702 xw->flags |= (value & 8) ? INVERSE : 0; 4703 xw->flags |= (value & 4) ? BLINK : 0; 4704 xw->flags |= (value & 2) ? UNDERLINE : 0; 4705 xw->flags |= (value & 1) ? BOLD : 0; 4706 4707 /* attributes */ 4708 if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40) 4709 return; 4710 screen->protected_mode &= ~DEC_PROTECT; 4711 screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0; 4712 4713 /* flags */ 4714 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) 4715 return; 4716 screen->do_wrap = (value & 8) ? True : False; 4717 screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0)); 4718 UIntClr(xw->flags, ORIGIN); 4719 xw->flags |= (value & 1) ? ORIGIN : 0; 4720 4721 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS) 4722 return; 4723 screen->curgl = (Char) value; 4724 4725 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS) 4726 return; 4727 screen->curgr = (Char) value; 4728 4729 /* character-set size */ 4730 if (parse_chr_param(&cp) != 0x4f) /* works for xterm */ 4731 return; 4732 4733 /* SCS designators */ 4734 for (value = 0; value < NUM_GSETS; ++value) { 4735 if (*cp == '%') { 4736 xtermDecodeSCS(xw, value, 0, '%', *++cp); 4737 } else if (*cp != '\0') { 4738 xtermDecodeSCS(xw, value, 0, '\0', *cp); 4739 } else { 4740 return; 4741 } 4742 cp++; 4743 } 4744 4745 TRACE(("...done DECCIR\n")); 4746} 4747 4748static void 4749restore_DECTABSR(XtermWidget xw, const char *cp) 4750{ 4751 int stop = 0; 4752 Bool fail = False; 4753 4754 TabZonk(xw->tabs); 4755 while (*cp != '\0' && !fail) { 4756 if ((*cp) >= '0' && (*cp) <= '9') { 4757 stop = (stop * 10) + ((*cp) - '0'); 4758 } else if (*cp == '/') { 4759 --stop; 4760 if (OkTAB(stop)) { 4761 TabSet(xw->tabs, stop); 4762 stop = 0; 4763 } else { 4764 fail = True; 4765 } 4766 } else { 4767 fail = True; 4768 } 4769 ++cp; 4770 } 4771 --stop; 4772 if (OkTAB(stop)) 4773 TabSet(xw->tabs, stop); 4774 4775 TRACE(("...done DECTABSR\n")); 4776} 4777#endif 4778 4779void 4780do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen) 4781{ 4782 TScreen *screen = TScreenOf(xw); 4783 char reply[BUFSIZ]; 4784 const char *cp = (const char *) dcsbuf; 4785 Bool okay; 4786 ANSI params; 4787#if OPT_DEC_RECTOPS 4788 char psarg = '0'; 4789#endif 4790 4791 TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen)); 4792 4793 if (dcslen != strlen(cp)) 4794 /* shouldn't have nulls in the string */ 4795 return; 4796 4797 switch (*cp) { /* intermediate character, or parameter */ 4798 case '$': /* DECRQSS */ 4799 okay = True; 4800 4801 cp++; 4802 if (*cp == 'q') { 4803 *reply = '\0'; 4804 cp++; 4805 if (!strcmp(cp, "\"q")) { /* DECSCA */ 4806 TRACE(("DECRQSS -> DECSCA\n")); 4807 sprintf(reply, "%d%s", 4808 (screen->protected_mode == DEC_PROTECT) 4809 && (xw->flags & PROTECTED) ? 1 : 0, 4810 cp); 4811 } else if (!strcmp(cp, "\"p")) { /* DECSCL */ 4812 if (screen->vtXX_level < 2) { 4813 /* actually none of DECRQSS is valid for vt100's */ 4814 break; 4815 } 4816 TRACE(("DECRQSS -> DECSCL\n")); 4817 sprintf(reply, "%d%s%s", 4818 (screen->vtXX_level ? 4819 screen->vtXX_level : 1) + 60, 4820 (screen->vtXX_level >= 2) 4821 ? (screen->control_eight_bits 4822 ? ";0" : ";1") 4823 : "", 4824 cp); 4825 } else if (!strcmp(cp, "r")) { /* DECSTBM */ 4826 TRACE(("DECRQSS -> DECSTBM\n")); 4827 sprintf(reply, "%d;%dr", 4828 screen->top_marg + 1, 4829 screen->bot_marg + 1); 4830 } else if (!strcmp(cp, "s")) { /* DECSLRM */ 4831 if (screen->vtXX_level >= 4) { /* VT420 */ 4832 TRACE(("DECRQSS -> DECSLRM\n")); 4833 sprintf(reply, "%d;%ds", 4834 screen->lft_marg + 1, 4835 screen->rgt_marg + 1); 4836 } else { 4837 okay = False; 4838 } 4839 } else if (!strcmp(cp, "m")) { /* SGR */ 4840 TRACE(("DECRQSS -> SGR\n")); 4841 xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background); 4842 strcat(reply, "m"); 4843 } else if (!strcmp(cp, " q")) { /* DECSCUSR */ 4844 int code = STEADY_BLOCK; 4845 if (isCursorUnderline(screen)) 4846 code = STEADY_UNDERLINE; 4847 else if (isCursorBar(screen)) 4848 code = STEADY_BAR; 4849#if OPT_BLINK_CURS 4850 if (screen->cursor_blink_esc != 0) 4851 code -= 1; 4852#endif 4853 TRACE(("reply DECSCUSR\n")); 4854 sprintf(reply, "%d%s", code, cp); 4855 } else if (!strcmp(cp, "t")) { /* DECSLPP */ 4856 sprintf(reply, "%d%s", 4857 ((screen->max_row > 24) ? screen->max_row : 24), 4858 cp); 4859 TRACE(("reply DECSLPP\n")); 4860 } else if (!strcmp(cp, "$|")) { /* DECSCPP */ 4861 TRACE(("reply DECSCPP\n")); 4862 sprintf(reply, "%d%s", 4863 ((xw->flags & IN132COLUMNS) ? 132 : 80), 4864 cp); 4865 } else if (!strcmp(cp, "*|")) { /* DECSNLS */ 4866 TRACE(("reply DECSNLS\n")); 4867 sprintf(reply, "%d%s", 4868 screen->max_row + 1, 4869 cp); 4870 } else { 4871 okay = False; 4872 } 4873 4874 unparseputc1(xw, ANSI_DCS); 4875 unparseputc(xw, okay ? '1' : '0'); 4876 unparseputc(xw, '$'); 4877 unparseputc(xw, 'r'); 4878 cp = reply; 4879 unparseputs(xw, cp); 4880 unparseputc1(xw, ANSI_ST); 4881 } else { 4882 unparseputc(xw, ANSI_CAN); 4883 } 4884 break; 4885 case '+': 4886 cp++; 4887 switch (*cp) { 4888#if OPT_TCAP_QUERY 4889 case 'p': 4890 if (AllowTcapOps(xw, etSetTcap)) { 4891 set_termcap(xw, cp + 1); 4892 } 4893 break; 4894 case 'q': 4895 if (AllowTcapOps(xw, etGetTcap)) { 4896 Bool fkey; 4897 unsigned state; 4898 int code; 4899 const char *tmp; 4900 const char *parsed = ++cp; 4901 4902 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4903 4904 unparseputc1(xw, ANSI_DCS); 4905 4906 unparseputc(xw, code >= 0 ? '1' : '0'); 4907 4908 unparseputc(xw, '+'); 4909 unparseputc(xw, 'r'); 4910 4911 while (*cp != 0 && (code >= -1)) { 4912 if (cp == parsed) 4913 break; /* no data found, error */ 4914 4915 for (tmp = cp; tmp != parsed; ++tmp) 4916 unparseputc(xw, *tmp); 4917 4918 if (code >= 0) { 4919 unparseputc(xw, '='); 4920 screen->tc_query_code = code; 4921 screen->tc_query_fkey = fkey; 4922#if OPT_ISO_COLORS 4923 /* XK_COLORS is a fake code for the "Co" entry (maximum 4924 * number of colors) */ 4925 if (code == XK_COLORS) { 4926 unparseputn(xw, (unsigned) NUM_ANSI_COLORS); 4927 } else 4928#if OPT_DIRECT_COLOR 4929 if (code == XK_RGB) { 4930 if (TScreenOf(xw)->direct_color && xw->has_rgb) { 4931 if (xw->rgb_widths[0] == xw->rgb_widths[1] && 4932 xw->rgb_widths[1] == xw->rgb_widths[2]) { 4933 unparseputn(xw, xw->rgb_widths[0]); 4934 } else { 4935 char temp[1024]; 4936 sprintf(temp, "%d/%d/%d", 4937 xw->rgb_widths[0], 4938 xw->rgb_widths[1], 4939 xw->rgb_widths[2]); 4940 unparseputs(xw, temp); 4941 } 4942 } else { 4943 unparseputs(xw, "-1"); 4944 } 4945 } else 4946#endif 4947#endif 4948 if (code == XK_TCAPNAME) { 4949 unparseputs(xw, resource.term_name); 4950 } else { 4951 XKeyEvent event; 4952 memset(&event, 0, sizeof(event)); 4953 event.state = state; 4954 Input(xw, &event, False); 4955 } 4956 screen->tc_query_code = -1; 4957 } else { 4958 break; /* no match found, error */ 4959 } 4960 4961 cp = parsed; 4962 if (*parsed == ';') { 4963 unparseputc(xw, *parsed++); 4964 cp = parsed; 4965 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4966 } 4967 } 4968 unparseputc1(xw, ANSI_ST); 4969 } 4970 break; 4971#endif 4972#if OPT_XRES_QUERY 4973 case 'Q': 4974 ++cp; 4975 if (AllowXResOps(xw)) { 4976 Boolean first = True; 4977 while (*cp != '\0') { 4978 const char *parsed = 0; 4979 const char *tmp; 4980 char *name = x_decode_hex(cp, &parsed); 4981 char *value; 4982 char *result; 4983 if (cp == parsed || name == NULL) { 4984 free(name); 4985 break; /* no data found, error */ 4986 } 4987 TRACE(("query-feature '%s'\n", name)); 4988 if ((value = vt100ResourceToString(xw, name)) != 0) { 4989 okay = True; /* valid */ 4990 } else { 4991 okay = False; /* invalid */ 4992 } 4993 if (first) { 4994 unparseputc1(xw, ANSI_DCS); 4995 unparseputc(xw, okay ? '1' : '0'); 4996 unparseputc(xw, '+'); 4997 unparseputc(xw, 'R'); 4998 first = False; 4999 } 5000 5001 for (tmp = cp; tmp != parsed; ++tmp) 5002 unparseputc(xw, *tmp); 5003 5004 if (value != 0) { 5005 unparseputc1(xw, '='); 5006 result = x_encode_hex(value); 5007 unparseputs(xw, result); 5008 } else { 5009 result = NULL; 5010 } 5011 5012 free(name); 5013 free(value); 5014 free(result); 5015 5016 cp = parsed; 5017 if (*parsed == ';') { 5018 unparseputc(xw, *parsed++); 5019 cp = parsed; 5020 } 5021 } 5022 if (!first) 5023 unparseputc1(xw, ANSI_ST); 5024 } 5025 break; 5026#endif 5027 } 5028 break; 5029#if OPT_DEC_RECTOPS 5030 case '1': 5031 /* FALLTHRU */ 5032 case '2': 5033 if (*skip_params(cp) == '$') { 5034 psarg = *cp++; 5035 if ((*cp++ == '$') 5036 && (*cp++ == 't') 5037 && (screen->vtXX_level >= 3)) { 5038 switch (psarg) { 5039 case '1': 5040 TRACE(("DECRSPS (DECCIR)\n")); 5041 restore_DECCIR(xw, cp); 5042 break; 5043 case '2': 5044 TRACE(("DECRSPS (DECTABSR)\n")); 5045 restore_DECTABSR(xw, cp); 5046 break; 5047 } 5048 } 5049 break; 5050 } 5051#endif 5052 /* FALLTHRU */ 5053 default: 5054 if (optRegisGraphics(screen) || 5055 optSixelGraphics(screen) || 5056 screen->vtXX_level >= 2) { /* VT220 */ 5057 parse_ansi_params(¶ms, &cp); 5058 switch (params.a_final) { 5059 case 'p': /* ReGIS */ 5060#if OPT_REGIS_GRAPHICS 5061 if (optRegisGraphics(screen)) { 5062 parse_regis(xw, ¶ms, cp); 5063 } 5064#else 5065 TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n")); 5066#endif 5067 break; 5068 case 'q': /* sixel */ 5069#if OPT_SIXEL_GRAPHICS 5070 if (optSixelGraphics(screen)) { 5071 (void) parse_sixel(xw, ¶ms, cp); 5072 } 5073#else 5074 TRACE(("ignoring sixel graphic (compilation flag not enabled)\n")); 5075#endif 5076 break; 5077 case '|': /* DECUDK */ 5078 if (screen->vtXX_level >= 2) { /* VT220 */ 5079 if (params.a_param[0] == 0) 5080 reset_decudk(xw); 5081 parse_decudk(xw, cp); 5082 } 5083 break; 5084 case L_CURL: /* DECDLD */ 5085 if (screen->vtXX_level >= 2) { /* VT220 */ 5086 parse_decdld(¶ms, cp); 5087 } 5088 break; 5089 } 5090 } 5091 break; 5092 } 5093 unparse_end(xw); 5094} 5095 5096#if OPT_DEC_RECTOPS 5097enum { 5098 mdUnknown = 0, 5099 mdMaybeSet = 1, 5100 mdMaybeReset = 2, 5101 mdAlwaysSet = 3, 5102 mdAlwaysReset = 4 5103}; 5104 5105#define MdBool(bool) ((bool) ? mdMaybeSet : mdMaybeReset) 5106#define MdFlag(mode,flag) MdBool((mode) & (flag)) 5107 5108/* 5109 * Reply is the same format as the query, with pair of mode/value: 5110 * 0 - not recognized 5111 * 1 - set 5112 * 2 - reset 5113 * 3 - permanently set 5114 * 4 - permanently reset 5115 * Only one mode can be reported at a time. 5116 */ 5117void 5118do_ansi_rqm(XtermWidget xw, int nparams, int *params) 5119{ 5120 ANSI reply; 5121 int count = 0; 5122 5123 TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0])); 5124 memset(&reply, 0, sizeof(reply)); 5125 5126 if (nparams >= 1) { 5127 int result = mdUnknown; 5128 5129 /* DECRQM can only ask about one mode at a time */ 5130 switch (params[0]) { 5131 case 1: /* GATM */ 5132 result = mdAlwaysReset; 5133 break; 5134 case 2: 5135 result = MdFlag(xw->keyboard.flags, MODE_KAM); 5136 break; 5137 case 3: /* CRM */ 5138 result = mdMaybeReset; 5139 break; 5140 case 4: 5141 result = MdFlag(xw->flags, INSERT); 5142 break; 5143 case 5: /* SRTM */ 5144 case 7: /* VEM */ 5145 case 10: /* HEM */ 5146 case 11: /* PUM */ 5147 result = mdAlwaysReset; 5148 break; 5149 case 12: 5150 result = MdFlag(xw->keyboard.flags, MODE_SRM); 5151 break; 5152 case 13: /* FEAM */ 5153 case 14: /* FETM */ 5154 case 15: /* MATM */ 5155 case 16: /* TTM */ 5156 case 17: /* SATM */ 5157 case 18: /* TSM */ 5158 case 19: /* EBM */ 5159 result = mdAlwaysReset; 5160 break; 5161 case 20: 5162 result = MdFlag(xw->flags, LINEFEED); 5163 break; 5164 } 5165 reply.a_param[count++] = (ParmType) params[0]; 5166 reply.a_param[count++] = (ParmType) result; 5167 } 5168 reply.a_type = ANSI_CSI; 5169 reply.a_nparam = (ParmType) count; 5170 reply.a_inters = '$'; 5171 reply.a_final = 'y'; 5172 unparseseq(xw, &reply); 5173} 5174 5175void 5176do_dec_rqm(XtermWidget xw, int nparams, int *params) 5177{ 5178 ANSI reply; 5179 int count = 0; 5180 5181 TRACE(("do_dec_rqm %d:%d\n", nparams, params[0])); 5182 memset(&reply, 0, sizeof(reply)); 5183 5184 if (nparams >= 1) { 5185 TScreen *screen = TScreenOf(xw); 5186 int result = mdUnknown; 5187 5188 /* DECRQM can only ask about one mode at a time */ 5189 switch ((DECSET_codes) params[0]) { 5190 case srm_DECCKM: 5191 result = MdFlag(xw->keyboard.flags, MODE_DECCKM); 5192 break; 5193 case srm_DECANM: /* ANSI/VT52 mode */ 5194#if OPT_VT52_MODE 5195 result = MdBool(screen->vtXX_level >= 1); 5196#else 5197 result = mdMaybeSet; 5198#endif 5199 break; 5200 case srm_DECCOLM: 5201 result = MdFlag(xw->flags, IN132COLUMNS); 5202 break; 5203 case srm_DECSCLM: /* (slow scroll) */ 5204 result = MdFlag(xw->flags, SMOOTHSCROLL); 5205 break; 5206 case srm_DECSCNM: 5207 result = MdFlag(xw->flags, REVERSE_VIDEO); 5208 break; 5209 case srm_DECOM: 5210 result = MdFlag(xw->flags, ORIGIN); 5211 break; 5212 case srm_DECAWM: 5213 result = MdFlag(xw->flags, WRAPAROUND); 5214 break; 5215 case srm_DECARM: 5216 result = mdAlwaysReset; 5217 break; 5218 case srm_X10_MOUSE: /* X10 mouse */ 5219 result = MdBool(screen->send_mouse_pos == X10_MOUSE); 5220 break; 5221#if OPT_TOOLBAR 5222 case srm_RXVT_TOOLBAR: 5223 result = MdBool(resource.toolBar); 5224 break; 5225#endif 5226#if OPT_BLINK_CURS 5227 case srm_ATT610_BLINK: /* AT&T 610: Start/stop blinking cursor */ 5228 result = MdBool(screen->cursor_blink_esc); 5229 break; 5230 case srm_CURSOR_BLINK_OPS: 5231 switch (screen->cursor_blink) { 5232 case cbTrue: 5233 result = mdMaybeSet; 5234 break; 5235 case cbFalse: 5236 result = mdMaybeReset; 5237 break; 5238 case cbAlways: 5239 result = mdAlwaysSet; 5240 break; 5241 case cbLAST: 5242 /* FALLTHRU */ 5243 case cbNever: 5244 result = mdAlwaysReset; 5245 break; 5246 } 5247 break; 5248 case srm_XOR_CURSOR_BLINKS: 5249 result = (screen->cursor_blink_xor 5250 ? mdAlwaysSet 5251 : mdAlwaysReset); 5252 break; 5253#endif 5254 case srm_DECPFF: /* print form feed */ 5255 result = MdBool(PrinterOf(screen).printer_formfeed); 5256 break; 5257 case srm_DECPEX: /* print extent */ 5258 result = MdBool(PrinterOf(screen).printer_extent); 5259 break; 5260 case srm_DECTCEM: /* Show/hide cursor (VT200) */ 5261 result = MdBool(screen->cursor_set); 5262 break; 5263 case srm_RXVT_SCROLLBAR: 5264 result = MdBool(screen->fullVwin.sb_info.width != OFF); 5265 break; 5266#if OPT_SHIFT_FONTS 5267 case srm_RXVT_FONTSIZE: 5268 result = MdBool(xw->misc.shift_fonts); 5269 break; 5270#endif 5271#if OPT_TEK4014 5272 case srm_DECTEK: 5273 result = MdBool(TEK4014_ACTIVE(xw)); 5274 break; 5275#endif 5276 case srm_132COLS: 5277 result = MdBool(screen->c132); 5278 break; 5279 case srm_CURSES_HACK: 5280 result = MdBool(screen->curses); 5281 break; 5282 case srm_DECNRCM: /* national charset (VT220) */ 5283 if (screen->vtXX_level >= 2) { 5284 result = MdFlag(xw->flags, NATIONAL); 5285 } else { 5286 result = 0; 5287 } 5288 break; 5289 case srm_MARGIN_BELL: /* margin bell */ 5290 result = MdBool(screen->marginbell); 5291 break; 5292#if OPT_PRINT_GRAPHICS 5293 case srm_DECGEPM: /* Graphics Expanded Print Mode */ 5294 result = MdBool(screen->graphics_expanded_print_mode); 5295 break; 5296#endif 5297 case srm_REVERSEWRAP: /* reverse wraparound */ 5298 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax)) 5299 result = MdFlag(xw->flags, REVERSEWRAP); 5300 break; 5301#if defined(ALLOWLOGGING) 5302 case srm_ALLOWLOGGING: /* logging */ 5303 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode)) 5304#if defined(ALLOWLOGFILEONOFF) 5305 result = MdBool(screen->logging); 5306#else 5307 result = ((MdBool(screen->logging) == mdMaybeSet) 5308 ? mdAlwaysSet 5309 : mdAlwaysReset); 5310#endif 5311 break; 5312#elif OPT_PRINT_GRAPHICS 5313 case srm_DECGPBM: /* Graphics Print Background Mode */ 5314 result = MdBool(screen->graphics_print_background_mode); 5315 break; 5316#endif 5317 case srm_OPT_ALTBUF_CURSOR: /* alternate buffer & cursor */ 5318 /* FALLTHRU */ 5319 case srm_OPT_ALTBUF: 5320 result = MdBool(screen->whichBuf); 5321 break; 5322 case srm_ALTBUF: 5323 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode)) 5324 result = MdBool(screen->whichBuf); 5325 break; 5326 case srm_DECNKM: 5327 result = MdFlag(xw->keyboard.flags, MODE_DECKPAM); 5328 break; 5329 case srm_DECBKM: 5330 result = MdFlag(xw->keyboard.flags, MODE_DECBKM); 5331 break; 5332 case srm_DECLRMM: 5333 if (screen->vtXX_level >= 4) { /* VT420 */ 5334 result = MdFlag(xw->flags, LEFT_RIGHT); 5335 } else { 5336 result = 0; 5337 } 5338 break; 5339#if OPT_SIXEL_GRAPHICS 5340 case srm_DECSDM: 5341 result = MdFlag(xw->keyboard.flags, MODE_DECSDM); 5342 break; 5343#endif 5344 case srm_DECNCSM: 5345 if (screen->vtXX_level >= 5) { /* VT510 */ 5346 result = MdFlag(xw->flags, NOCLEAR_COLM); 5347 } else { 5348 result = 0; 5349 } 5350 break; 5351 case srm_VT200_MOUSE: /* xterm bogus sequence */ 5352 result = MdBool(screen->send_mouse_pos == VT200_MOUSE); 5353 break; 5354 case srm_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */ 5355 result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE); 5356 break; 5357 case srm_BTN_EVENT_MOUSE: 5358 result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE); 5359 break; 5360 case srm_ANY_EVENT_MOUSE: 5361 result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE); 5362 break; 5363#if OPT_FOCUS_EVENT 5364 case srm_FOCUS_EVENT_MOUSE: 5365 result = MdBool(screen->send_focus_pos); 5366 break; 5367#endif 5368 case srm_EXT_MODE_MOUSE: 5369 /* FALLTHRU */ 5370 case srm_SGR_EXT_MODE_MOUSE: 5371 /* FALLTHRU */ 5372 case srm_URXVT_EXT_MODE_MOUSE: 5373 /* FALLTHRU */ 5374 case srm_PIXEL_POSITION_MOUSE: 5375 result = MdBool(screen->extend_coords == params[0]); 5376 break; 5377 case srm_ALTERNATE_SCROLL: 5378 result = MdBool(screen->alternateScroll); 5379 break; 5380 case srm_RXVT_SCROLL_TTY_OUTPUT: 5381 result = MdBool(screen->scrollttyoutput); 5382 break; 5383 case srm_RXVT_SCROLL_TTY_KEYPRESS: 5384 result = MdBool(screen->scrollkey); 5385 break; 5386 case srm_EIGHT_BIT_META: 5387 result = MdBool(screen->eight_bit_meta); 5388 break; 5389#if OPT_NUM_LOCK 5390 case srm_REAL_NUMLOCK: 5391 result = MdBool(xw->misc.real_NumLock); 5392 break; 5393 case srm_META_SENDS_ESC: 5394 result = MdBool(screen->meta_sends_esc); 5395 break; 5396#endif 5397 case srm_DELETE_IS_DEL: 5398 result = MdBool(xtermDeleteIsDEL(xw)); 5399 break; 5400#if OPT_NUM_LOCK 5401 case srm_ALT_SENDS_ESC: 5402 result = MdBool(screen->alt_sends_esc); 5403 break; 5404#endif 5405 case srm_KEEP_SELECTION: 5406 result = MdBool(screen->keepSelection); 5407 break; 5408 case srm_SELECT_TO_CLIPBOARD: 5409 result = MdBool(screen->selectToClipboard); 5410 break; 5411 case srm_BELL_IS_URGENT: 5412 result = MdBool(screen->bellIsUrgent); 5413 break; 5414 case srm_POP_ON_BELL: 5415 result = MdBool(screen->poponbell); 5416 break; 5417 case srm_KEEP_CLIPBOARD: 5418 result = MdBool(screen->keepClipboard); 5419 break; 5420 case srm_ALLOW_ALTBUF: 5421 result = MdBool(xw->misc.titeInhibit); 5422 break; 5423 case srm_SAVE_CURSOR: 5424 result = MdBool(screen->sc[screen->whichBuf].saved); 5425 break; 5426#if OPT_TCAP_FKEYS 5427 case srm_TCAP_FKEYS: 5428 result = MdBool(xw->keyboard.type == keyboardIsTermcap); 5429 break; 5430#endif 5431#if OPT_SUN_FUNC_KEYS 5432 case srm_SUN_FKEYS: 5433 result = MdBool(xw->keyboard.type == keyboardIsSun); 5434 break; 5435#endif 5436#if OPT_HP_FUNC_KEYS 5437 case srm_HP_FKEYS: 5438 result = MdBool(xw->keyboard.type == keyboardIsHP); 5439 break; 5440#endif 5441#if OPT_SCO_FUNC_KEYS 5442 case srm_SCO_FKEYS: 5443 result = MdBool(xw->keyboard.type == keyboardIsSCO); 5444 break; 5445#endif 5446 case srm_LEGACY_FKEYS: 5447 result = MdBool(xw->keyboard.type == keyboardIsLegacy); 5448 break; 5449#if OPT_SUNPC_KBD 5450 case srm_VT220_FKEYS: 5451 result = MdBool(xw->keyboard.type == keyboardIsVT220); 5452 break; 5453#endif 5454#if OPT_PASTE64 || OPT_READLINE 5455 case srm_PASTE_IN_BRACKET: 5456 result = MdBool(SCREEN_FLAG(screen, paste_brackets)); 5457 break; 5458#endif 5459#if OPT_READLINE 5460 case srm_BUTTON1_MOVE_POINT: 5461 result = MdBool(SCREEN_FLAG(screen, click1_moves)); 5462 break; 5463 case srm_BUTTON2_MOVE_POINT: 5464 result = MdBool(SCREEN_FLAG(screen, paste_moves)); 5465 break; 5466 case srm_DBUTTON3_DELETE: 5467 result = MdBool(SCREEN_FLAG(screen, dclick3_deletes)); 5468 break; 5469 case srm_PASTE_QUOTE: 5470 result = MdBool(SCREEN_FLAG(screen, paste_quotes)); 5471 break; 5472 case srm_PASTE_LITERAL_NL: 5473 result = MdBool(SCREEN_FLAG(screen, paste_literal_nl)); 5474 break; 5475#endif /* OPT_READLINE */ 5476#if OPT_GRAPHICS 5477 case srm_PRIVATE_COLOR_REGISTERS: 5478 result = MdBool(screen->privatecolorregisters); 5479 break; 5480#endif 5481#if OPT_SIXEL_GRAPHICS 5482 case srm_SIXEL_SCROLLS_RIGHT: 5483 result = MdBool(screen->sixel_scrolls_right); 5484 break; 5485#endif 5486 default: 5487 TRACE(("DATA_ERROR: requested report for unknown private mode %d\n", 5488 params[0])); 5489 } 5490 reply.a_param[count++] = (ParmType) params[0]; 5491 reply.a_param[count++] = (ParmType) result; 5492 TRACE(("DECRPM(%d) = %d\n", params[0], result)); 5493 } 5494 reply.a_type = ANSI_CSI; 5495 reply.a_pintro = '?'; 5496 reply.a_nparam = (ParmType) count; 5497 reply.a_inters = '$'; 5498 reply.a_final = 'y'; 5499 unparseseq(xw, &reply); 5500} 5501#endif /* OPT_DEC_RECTOPS */ 5502 5503char * 5504udk_lookup(XtermWidget xw, int keycode, int *len) 5505{ 5506 char *result = NULL; 5507 if (keycode >= 0 && keycode < MAX_UDK) { 5508 *len = xw->work.user_keys[keycode].len; 5509 result = xw->work.user_keys[keycode].str; 5510 TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result)); 5511 } else { 5512 TRACE(("udk_lookup(%d) = <null>\n", keycode)); 5513 } 5514 return result; 5515} 5516 5517#if OPT_REPORT_ICONS 5518void 5519report_icons(const char *fmt, ...) 5520{ 5521 if (resource.reportIcons) { 5522 va_list ap; 5523 va_start(ap, fmt); 5524 vfprintf(stdout, fmt, ap); 5525 va_end(ap); 5526#if OPT_TRACE 5527 va_start(ap, fmt); 5528 TraceVA(fmt, ap); 5529 va_end(ap); 5530#endif 5531 } 5532} 5533#endif 5534 5535#ifdef HAVE_LIBXPM 5536 5537#ifndef PIXMAP_ROOTDIR 5538#define PIXMAP_ROOTDIR "/usr/share/pixmaps/" 5539#endif 5540 5541typedef struct { 5542 const char *name; 5543 const char *const *data; 5544} XPM_DATA; 5545 5546static char * 5547x_find_icon(char **work, int *state, const char *filename, const char *suffix) 5548{ 5549 const char *prefix = PIXMAP_ROOTDIR; 5550 const char *larger = "_48x48"; 5551 char *result = 0; 5552 5553 if (*state >= 0) { 5554 if ((*state & 1) == 0) 5555 suffix = ""; 5556 if ((*state & 2) == 0) 5557 larger = ""; 5558 if ((*state & 4) == 0) { 5559 prefix = ""; 5560 } else if (!strncmp(filename, "/", (size_t) 1) || 5561 !strncmp(filename, "./", (size_t) 2) || 5562 !strncmp(filename, "../", (size_t) 3)) { 5563 *state = -1; 5564 } else if (*state >= 8) { 5565 *state = -1; 5566 } 5567 } 5568 5569 if (*state >= 0) { 5570 size_t length; 5571 5572 FreeAndNull(*work); 5573 length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) + 5574 strlen(suffix); 5575 if ((result = malloc(length)) != 0) { 5576 sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix); 5577 *work = result; 5578 } 5579 *state += 1; 5580 } 5581 TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result))); 5582 return result; 5583} 5584 5585#if OPT_BUILTIN_XPMS 5586 5587static const XPM_DATA * 5588built_in_xpm(const XPM_DATA * table, Cardinal length, const char *find) 5589{ 5590 const XPM_DATA *result = 0; 5591 if (!IsEmpty(find)) { 5592 Cardinal n; 5593 for (n = 0; n < length; ++n) { 5594 if (!x_strcasecmp(find, table[n].name)) { 5595 result = table + n; 5596 ReportIcons(("use builtin-icon %s\n", table[n].name)); 5597 break; 5598 } 5599 } 5600 5601 /* 5602 * As a fallback, check if the icon name matches without the lengths, 5603 * which are all _HHxWW format. 5604 */ 5605 if (result == 0) { 5606 const char *base = table[0].name; 5607 const char *last = strchr(base, '_'); 5608 if (last != 0 5609 && !x_strncasecmp(find, base, (unsigned) (last - base))) { 5610 result = table + length - 1; 5611 ReportIcons(("use builtin-icon %s\n", table[0].name)); 5612 } 5613 } 5614 } 5615 return result; 5616} 5617#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint) 5618#endif /* OPT_BUILTIN_XPMS */ 5619 5620typedef enum { 5621 eHintDefault = 0 /* use the largest builtin-icon */ 5622 ,eHintNone 5623 ,eHintSearch 5624} ICON_HINT; 5625#endif /* HAVE_LIBXPM */ 5626 5627int 5628getVisualDepth(XtermWidget xw) 5629{ 5630 int result = 0; 5631 5632 if (getVisualInfo(xw)) { 5633 result = xw->visInfo->depth; 5634 } 5635 return result; 5636} 5637 5638/* 5639 * WM_ICON_SIZE should be honored if possible. 5640 */ 5641void 5642xtermLoadIcon(XtermWidget xw, const char *icon_hint) 5643{ 5644#ifdef HAVE_LIBXPM 5645 Display *dpy = XtDisplay(xw); 5646 Pixmap myIcon = 0; 5647 Pixmap myMask = 0; 5648 char *workname = 0; 5649 ICON_HINT hint = eHintDefault; 5650#include <builtin_icons.h> 5651 5652 ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint))); 5653 if (!IsEmpty(icon_hint)) { 5654 if (!x_strcasecmp(icon_hint, "none")) { 5655 hint = eHintNone; 5656 } else { 5657 hint = eHintSearch; 5658 } 5659 } 5660 5661 if (hint == eHintSearch) { 5662 int state = 0; 5663 while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) { 5664 Pixmap resIcon = 0; 5665 Pixmap shapemask = 0; 5666 XpmAttributes attributes; 5667 struct stat sb; 5668 5669 attributes.depth = (unsigned) getVisualDepth(xw); 5670 attributes.valuemask = XpmDepth; 5671 5672 if (IsEmpty(workname) 5673 || lstat(workname, &sb) != 0 5674 || !S_ISREG(sb.st_mode)) { 5675 TRACE(("...failure (no such file)\n")); 5676 } else { 5677 int rc = XpmReadFileToPixmap(dpy, 5678 DefaultRootWindow(dpy), 5679 workname, 5680 &resIcon, 5681 &shapemask, 5682 &attributes); 5683 if (rc == XpmSuccess) { 5684 myIcon = resIcon; 5685 myMask = shapemask; 5686 TRACE(("...success\n")); 5687 ReportIcons(("found/loaded icon-file %s\n", workname)); 5688 break; 5689 } else { 5690 TRACE(("...failure (%s)\n", XpmGetErrorString(rc))); 5691 } 5692 } 5693 } 5694 } 5695 5696 /* 5697 * If no external file was found, look for the name in the built-in table. 5698 * If that fails, just use the biggest mini-icon. 5699 */ 5700 if (myIcon == 0 && hint != eHintNone) { 5701 char **data; 5702#if OPT_BUILTIN_XPMS 5703 const XPM_DATA *myData = 0; 5704 myData = BuiltInXPM(mini_xterm_xpms); 5705 if (myData == 0) 5706 myData = BuiltInXPM(filled_xterm_xpms); 5707 if (myData == 0) 5708 myData = BuiltInXPM(xterm_color_xpms); 5709 if (myData == 0) 5710 myData = BuiltInXPM(xterm_xpms); 5711 if (myData == 0) 5712 myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1]; 5713 data = (char **) myData->data; 5714#else 5715 data = (char **) &mini_xterm_48x48_xpm; 5716#endif 5717 if (XpmCreatePixmapFromData(dpy, 5718 DefaultRootWindow(dpy), 5719 data, 5720 &myIcon, &myMask, 0) == 0) { 5721 ReportIcons(("loaded built-in pixmap icon\n")); 5722 } else { 5723 myIcon = 0; 5724 myMask = 0; 5725 } 5726 } 5727 5728 if (myIcon != 0) { 5729 XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw)); 5730 if (!hints) 5731 hints = XAllocWMHints(); 5732 5733 if (hints) { 5734 hints->flags |= IconPixmapHint; 5735 hints->icon_pixmap = myIcon; 5736 if (myMask) { 5737 hints->flags |= IconMaskHint; 5738 hints->icon_mask = myMask; 5739 } 5740 5741 XSetWMHints(dpy, VShellWindow(xw), hints); 5742 XFree(hints); 5743 ReportIcons(("updated window-manager hints\n")); 5744 } 5745 } 5746 5747 free(workname); 5748 5749#else 5750 (void) xw; 5751 (void) icon_hint; 5752#endif 5753} 5754 5755void 5756ChangeGroup(XtermWidget xw, const char *attribute, char *value) 5757{ 5758 Arg args[1]; 5759 Boolean changed = True; 5760 Widget w = CURRENT_EMU(); 5761 Widget top = SHELL_OF(w); 5762 5763 char *my_attr = NULL; 5764 char *old_value = value; 5765#if OPT_WIDE_CHARS 5766 Boolean titleIsUTF8; 5767#endif 5768 5769 if (!AllowTitleOps(xw)) 5770 return; 5771 5772 /* 5773 * Ignore empty or too-long requests. 5774 */ 5775 if (value == 0 || strlen(value) > 1000) 5776 return; 5777 5778 if (IsTitleMode(xw, tmSetBase16)) { 5779 const char *temp; 5780 char *test; 5781 5782 /* this allocates a new string, if no error is detected */ 5783 value = x_decode_hex(value, &temp); 5784 if (value == 0 || *temp != '\0') { 5785 free(value); 5786 return; 5787 } 5788 for (test = value; *test != '\0'; ++test) { 5789 if (CharOf(*test) < 32) { 5790 *test = '\0'; 5791 break; 5792 } 5793 } 5794 } 5795#if OPT_WIDE_CHARS 5796 /* 5797 * By design, xterm uses the XtNtitle resource of the X Toolkit for setting 5798 * the WM_NAME property, rather than doing this directly. That relies on 5799 * the application to tell it if the format should be something other than 5800 * STRING, i.e., by setting the XtNtitleEncoding resource. 5801 * 5802 * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted). In X11R6, 5803 * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86 5804 * added UTF8_STRING (the documentation for that was discarded by an Xorg 5805 * developer, although the source-code provides this feature). 5806 * 5807 * Since X11R5, if the X11 library fails to store a text property as 5808 * STRING, it falls back to COMPOUND_TEXT. For best interoperability, we 5809 * prefer to use STRING if the data fits, or COMPOUND_TEXT. In either 5810 * case, limit the resulting characters to the printable ISO-8859-1 set. 5811 */ 5812 titleIsUTF8 = isValidUTF8((Char *) value); 5813 if (IsSetUtf8Title(xw) && titleIsUTF8) { 5814 char *testc = malloc(strlen(value) + 1); 5815 Char *nextc = (Char *) value; 5816 Boolean ok8bit = True; 5817 5818 if (testc != NULL) { 5819 /* 5820 * Check if the data fits in STRING. Along the way, replace 5821 * control characters. 5822 */ 5823 Char *lastc = (Char *) testc; 5824 while (*nextc != '\0') { 5825 unsigned ch; 5826 nextc = convertFromUTF8(nextc, &ch); 5827 if (ch > 255) { 5828 ok8bit = False; 5829 } else if (!IsLatin1(ch)) { 5830 ch = OnlyLatin1(ch); 5831 } 5832 *lastc++ = (Char) ch; 5833 } 5834 *lastc = '\0'; 5835 if (ok8bit) { 5836 TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n")); 5837 if (value != old_value) 5838 free(value); 5839 value = testc; 5840 titleIsUTF8 = False; 5841 } else { 5842 TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n" 5843 "\t%s\n", value)); 5844 free(testc); 5845 nextc = (Char *) value; 5846 while (*nextc != '\0') { 5847 unsigned ch; 5848 Char *skip = convertFromUTF8(nextc, &ch); 5849 if (iswcntrl((wint_t) ch)) { 5850 memset(nextc, BAD_ASCII, (size_t) (skip - nextc)); 5851 } 5852 nextc = skip; 5853 } 5854 } 5855 } 5856 } else 5857#endif 5858 { 5859 Char *c1 = (Char *) value; 5860 5861 TRACE(("ChangeGroup: assume ISO-8859-1\n")); 5862 for (c1 = (Char *) value; *c1 != '\0'; ++c1) { 5863 *c1 = (Char) OnlyLatin1(*c1); 5864 } 5865 } 5866 5867 my_attr = x_strdup(attribute); 5868 5869 ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value)); 5870 5871#if OPT_WIDE_CHARS 5872 /* 5873 * If we're running in UTF-8 mode, and have not been told that the 5874 * title string is in UTF-8, it is likely that non-ASCII text in the 5875 * string will be rejected because it is not printable in the current 5876 * locale. So we convert it to UTF-8, allowing the X library to 5877 * convert it back. 5878 */ 5879 TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT ")); 5880 if (xtermEnvUTF8() && !titleIsUTF8) { 5881 size_t limit = strlen(value); 5882 Char *c1 = (Char *) value; 5883 int n; 5884 5885 for (n = 0; c1[n] != '\0'; ++n) { 5886 if (c1[n] > 127) { 5887 Char *converted; 5888 if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) { 5889 Char *temp = converted; 5890 while (*c1 != 0) { 5891 temp = convertToUTF8(temp, *c1++); 5892 } 5893 *temp = 0; 5894 if (value != old_value) 5895 free(value); 5896 value = (char *) converted; 5897 ReportIcons(("...converted{%s}\n", value)); 5898 } 5899 break; 5900 } 5901 } 5902 } 5903#endif 5904 5905#if OPT_SAME_NAME 5906 /* If the attribute isn't going to change, then don't bother... */ 5907 if (resource.sameName) { 5908 char *buf = 0; 5909 XtSetArg(args[0], my_attr, &buf); 5910 XtGetValues(top, args, 1); 5911 TRACE(("...comparing{%s}\n", NonNull(buf))); 5912 if (buf != 0 && strcmp(value, buf) == 0) 5913 changed = False; 5914 } 5915#endif /* OPT_SAME_NAME */ 5916 5917 if (changed) { 5918 ReportIcons(("...updating %s\n", my_attr)); 5919 ReportIcons(("...value is %s\n", value)); 5920 XtSetArg(args[0], my_attr, value); 5921 XtSetValues(top, args, 1); 5922 } 5923#if OPT_WIDE_CHARS 5924 if (xtermEnvUTF8()) { 5925 Display *dpy = XtDisplay(xw); 5926 const char *propname = (!strcmp(my_attr, XtNtitle) 5927 ? "_NET_WM_NAME" 5928 : "_NET_WM_ICON_NAME"); 5929 Atom my_atom = XInternAtom(dpy, propname, False); 5930 5931 if (my_atom != None) { 5932 changed = True; 5933 5934 if (IsSetUtf8Title(xw)) { 5935#if OPT_SAME_NAME 5936 if (resource.sameName) { 5937 Atom actual_type; 5938 Atom requested_type = XA_UTF8_STRING(dpy); 5939 int actual_format = 0; 5940 long long_length = 1024; 5941 unsigned long nitems = 0; 5942 unsigned long bytes_after = 0; 5943 unsigned char *prop = 0; 5944 5945 if (xtermGetWinProp(dpy, 5946 VShellWindow(xw), 5947 my_atom, 5948 0L, 5949 long_length, 5950 requested_type, 5951 &actual_type, 5952 &actual_format, 5953 &nitems, 5954 &bytes_after, 5955 &prop) 5956 && actual_type == requested_type 5957 && actual_format == 8 5958 && prop != 0 5959 && nitems == strlen(value) 5960 && memcmp(value, prop, nitems) == 0) { 5961 changed = False; 5962 } 5963 } 5964#endif /* OPT_SAME_NAME */ 5965 if (changed) { 5966 ReportIcons(("...updating %s\n", propname)); 5967 ReportIcons(("...value is %s\n", value)); 5968 XChangeProperty(dpy, VShellWindow(xw), my_atom, 5969 XA_UTF8_STRING(dpy), 8, 5970 PropModeReplace, 5971 (Char *) value, 5972 (int) strlen(value)); 5973 } 5974 } else { 5975 ReportIcons(("...deleting %s\n", propname)); 5976 XDeleteProperty(dpy, VShellWindow(xw), my_atom); 5977 } 5978 } 5979 } 5980#endif 5981 if (value != old_value) { 5982 free(value); 5983 } 5984 free(my_attr); 5985 5986 return; 5987} 5988 5989void 5990ChangeIconName(XtermWidget xw, char *name) 5991{ 5992 if (name == 0) { 5993 name = emptyString; 5994 } 5995 if (!showZIconBeep(xw, name)) 5996 ChangeGroup(xw, XtNiconName, name); 5997} 5998 5999void 6000ChangeTitle(XtermWidget xw, char *name) 6001{ 6002 ChangeGroup(xw, XtNtitle, name); 6003} 6004 6005#define Strlen(s) strlen((const char *)(s)) 6006 6007void 6008ChangeXprop(char *buf) 6009{ 6010 Display *dpy = XtDisplay(toplevel); 6011 Window w = XtWindow(toplevel); 6012 XTextProperty text_prop; 6013 Atom aprop; 6014 Char *pchEndPropName = (Char *) strchr(buf, '='); 6015 6016 if (pchEndPropName) 6017 *pchEndPropName = '\0'; 6018 aprop = XInternAtom(dpy, buf, False); 6019 if (pchEndPropName == NULL) { 6020 /* no "=value" given, so delete the property */ 6021 XDeleteProperty(dpy, w, aprop); 6022 } else { 6023 text_prop.value = pchEndPropName + 1; 6024 text_prop.encoding = XA_STRING; 6025 text_prop.format = 8; 6026 text_prop.nitems = Strlen(text_prop.value); 6027 XSetTextProperty(dpy, w, &text_prop, aprop); 6028 } 6029} 6030 6031/***====================================================================***/ 6032 6033/* 6034 * This is part of ReverseVideo(). It reverses the data stored for the old 6035 * "dynamic" colors that might have been retrieved using OSC 10-18. 6036 */ 6037void 6038ReverseOldColors(XtermWidget xw) 6039{ 6040 ScrnColors *pOld = xw->work.oldColors; 6041 Pixel tmpPix; 6042 char *tmpName; 6043 6044 if (pOld) { 6045 /* change text cursor, if necessary */ 6046 if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { 6047 pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; 6048 if (pOld->names[TEXT_CURSOR]) { 6049 XtFree(xw->work.oldColors->names[TEXT_CURSOR]); 6050 pOld->names[TEXT_CURSOR] = NULL; 6051 } 6052 if (pOld->names[TEXT_BG]) { 6053 if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { 6054 pOld->names[TEXT_CURSOR] = tmpName; 6055 } 6056 } 6057 } 6058 6059 EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); 6060 EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); 6061 6062 EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); 6063 EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); 6064 6065#if OPT_TEK4014 6066 EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); 6067 EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); 6068#endif 6069 FreeMarkGCs(xw); 6070 } 6071 return; 6072} 6073 6074Bool 6075AllocateTermColor(XtermWidget xw, 6076 ScrnColors * pNew, 6077 int ndx, 6078 const char *name, 6079 Bool always) 6080{ 6081 Bool result = False; 6082 6083 if (always || AllowColorOps(xw, ecSetColor)) { 6084 XColor def; 6085 char *newName; 6086 6087 result = True; 6088 if (!x_strcasecmp(name, XtDefaultForeground)) { 6089 def.pixel = xw->old_foreground; 6090 } else if (!x_strcasecmp(name, XtDefaultBackground)) { 6091 def.pixel = xw->old_background; 6092 } else if (!xtermAllocColor(xw, &def, name)) { 6093 result = False; 6094 } 6095 6096 if (result 6097 && (newName = x_strdup(name)) != 0) { 6098 if (COLOR_DEFINED(pNew, ndx)) { 6099 free(pNew->names[ndx]); 6100 } 6101 SET_COLOR_VALUE(pNew, ndx, def.pixel); 6102 SET_COLOR_NAME(pNew, ndx, newName); 6103 TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n", 6104 ndx, newName, def.pixel)); 6105 } else { 6106 TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); 6107 result = False; 6108 } 6109 } 6110 return result; 6111} 6112/***====================================================================***/ 6113 6114/* ARGSUSED */ 6115void 6116Panic(const char *s GCC_UNUSED, int a GCC_UNUSED) 6117{ 6118 if_DEBUG({ 6119 xtermWarning(s, a); 6120 }); 6121} 6122 6123const char * 6124SysErrorMsg(int code) 6125{ 6126 static const char unknown[] = "unknown error"; 6127 const char *s = strerror(code); 6128 return s ? s : unknown; 6129} 6130 6131const char * 6132SysReasonMsg(int code) 6133{ 6134 /* *INDENT-OFF* */ 6135 static const struct { 6136 int code; 6137 const char *name; 6138 } table[] = { 6139 { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" }, 6140 { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" }, 6141 { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", }, 6142 { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", }, 6143 { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", }, 6144 { ERROR_PTSNAME, "spawn: ptsname() failed", }, 6145 { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", }, 6146 { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" }, 6147 { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" }, 6148 { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" }, 6149 { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" }, 6150 { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" }, 6151 { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" }, 6152 { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" }, 6153 { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" }, 6154 { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" }, 6155 { ERROR_INIGROUPS, "spawn: initgroups() failed" }, 6156 { ERROR_FORK, "spawn: fork() failed" }, 6157 { ERROR_EXEC, "spawn: exec() failed" }, 6158 { ERROR_PTYS, "get_pty: not enough ptys" }, 6159 { ERROR_PTY_EXEC, "waiting for initial map" }, 6160 { ERROR_SETUID, "spawn: setuid() failed" }, 6161 { ERROR_INIT, "spawn: can't initialize window" }, 6162 { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" }, 6163 { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" }, 6164 { ERROR_LUMALLOC, "luit: command-line malloc failed" }, 6165 { ERROR_SELECT, "in_put: select() failed" }, 6166 { ERROR_VINIT, "VTInit: can't initialize window" }, 6167 { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" }, 6168 { ERROR_TSELECT, "Tinput: select() failed" }, 6169 { ERROR_TINIT, "TekInit: can't initialize window" }, 6170 { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" }, 6171 { ERROR_LOGEXEC, "StartLog: exec() failed" }, 6172 { ERROR_XERROR, "xerror: XError event" }, 6173 { ERROR_XIOERROR, "xioerror: X I/O error" }, 6174 { ERROR_SCALLOC, "Alloc: calloc() failed on base" }, 6175 { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" }, 6176 { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" }, 6177 }; 6178 /* *INDENT-ON* */ 6179 6180 Cardinal n; 6181 const char *result = "?"; 6182 6183 for (n = 0; n < XtNumber(table); ++n) { 6184 if (code == table[n].code) { 6185 result = table[n].name; 6186 break; 6187 } 6188 } 6189 return result; 6190} 6191 6192void 6193SysError(int code) 6194{ 6195 int oerrno = errno; 6196 6197 fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno); 6198 fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); 6199 fprintf(stderr, "Reason: %s\n", SysReasonMsg(code)); 6200 6201 Cleanup(code); 6202} 6203 6204void 6205NormalExit(void) 6206{ 6207 static Bool cleaning; 6208 6209 /* 6210 * Process "-hold" and session cleanup only for a normal exit. 6211 */ 6212 if (cleaning) { 6213 hold_screen = 0; 6214 return; 6215 } 6216 6217 cleaning = True; 6218 need_cleanup = False; 6219 6220 if (hold_screen) { 6221 hold_screen = 2; 6222 while (hold_screen) { 6223 xtermFlushDbe(term); 6224 xevents(term); 6225 Sleep(EVENT_DELAY); 6226 } 6227 } 6228#if OPT_SESSION_MGT 6229 if (resource.sessionMgt) { 6230 XtVaSetValues(toplevel, 6231 XtNjoinSession, False, 6232 (void *) 0); 6233 } 6234#endif 6235 Cleanup(0); 6236} 6237 6238#if USE_DOUBLE_BUFFER 6239void 6240xtermFlushDbe(XtermWidget xw) 6241{ 6242 TScreen *screen = TScreenOf(xw); 6243 if (resource.buffered && screen->needSwap) { 6244 XdbeSwapInfo swap; 6245 swap.swap_window = VWindow(screen); 6246 swap.swap_action = XdbeCopied; 6247 XdbeSwapBuffers(XtDisplay(xw), &swap, 1); 6248 XFlush(XtDisplay(xw)); 6249 screen->needSwap = 0; 6250 ScrollBarDrawThumb(xw, 2); 6251 X_GETTIMEOFDAY(&screen->buffered_at); 6252 } 6253} 6254 6255void 6256xtermTimedDbe(XtermWidget xw) 6257{ 6258 if (resource.buffered) { 6259 TScreen *screen = TScreenOf(xw); 6260 struct timeval now; 6261 long elapsed; 6262 long limit = DbeMsecs(xw); 6263 6264 X_GETTIMEOFDAY(&now); 6265 if (screen->buffered_at.tv_sec) { 6266 elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec) 6267 + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L); 6268 } else { 6269 elapsed = limit; 6270 } 6271 if (elapsed >= limit) { 6272 xtermNeedSwap(xw, 1); 6273 xtermFlushDbe(xw); 6274 } 6275 } 6276} 6277#endif 6278 6279/* 6280 * cleanup by sending SIGHUP to client processes 6281 */ 6282void 6283Cleanup(int code) 6284{ 6285 TScreen *screen = TScreenOf(term); 6286 6287 TRACE(("Cleanup %d\n", code)); 6288 6289 if (screen->pid > 1) { 6290 (void) kill_process_group(screen->pid, SIGHUP); 6291 } 6292 Exit(code); 6293} 6294 6295#ifndef S_IXOTH 6296#define S_IXOTH 1 6297#endif 6298 6299Boolean 6300validProgram(const char *pathname) 6301{ 6302 Boolean result = False; 6303 struct stat sb; 6304 6305 if (!IsEmpty(pathname) 6306 && *pathname == '/' 6307 && strstr(pathname, "/..") == 0 6308 && stat(pathname, &sb) == 0 6309 && (sb.st_mode & S_IFMT) == S_IFREG 6310 && (sb.st_mode & S_IXOTH) != 0) { 6311 result = True; 6312 } 6313 return result; 6314} 6315 6316#ifndef VMS 6317#ifndef PATH_MAX 6318#define PATH_MAX 512 /* ... is not defined consistently in Xos.h */ 6319#endif 6320char * 6321xtermFindShell(char *leaf, Bool warning) 6322{ 6323 char *s0; 6324 char *s; 6325 char *d; 6326 char *tmp; 6327 char *result = leaf; 6328 Bool allocated = False; 6329 6330 TRACE(("xtermFindShell(%s)\n", leaf)); 6331 6332 if (!strncmp("./", result, (size_t) 2) 6333 || !strncmp("../", result, (size_t) 3)) { 6334 size_t need = PATH_MAX; 6335 size_t used = strlen(result) + 2; 6336 char *buffer = malloc(used + need); 6337 if (buffer != 0) { 6338 if (getcwd(buffer, need) != 0) { 6339 sprintf(buffer + strlen(buffer), "/%s", result); 6340 result = buffer; 6341 allocated = True; 6342 } else { 6343 free(buffer); 6344 } 6345 } 6346 } else if (*result != '\0' && strchr("+/-", *result) == 0) { 6347 /* find it in $PATH */ 6348 if ((s = s0 = x_getenv("PATH")) != 0) { 6349 if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) { 6350 Bool found = False; 6351 while (*s != '\0') { 6352 strcpy(tmp, s); 6353 for (d = tmp;; ++d) { 6354 if (*d == ':' || *d == '\0') { 6355 int skip = (*d != '\0'); 6356 *d = '/'; 6357 strcpy(d + 1, leaf); 6358 if (skip) 6359 ++d; 6360 s += (d - tmp); 6361 if (validProgram(tmp)) { 6362 result = x_strdup(tmp); 6363 found = True; 6364 allocated = True; 6365 } 6366 break; 6367 } 6368 } 6369 if (found) 6370 break; 6371 } 6372 free(tmp); 6373 } 6374 free(s0); 6375 } 6376 } 6377 TRACE(("...xtermFindShell(%s)\n", result)); 6378 if (!validProgram(result)) { 6379 if (warning) 6380 xtermWarning("No absolute path found for shell: %s\n", result); 6381 if (allocated) 6382 free(result); 6383 result = 0; 6384 } 6385 /* be consistent, so that caller can always free the result */ 6386 if (result != 0 && !allocated) 6387 result = x_strdup(result); 6388 return result; 6389} 6390#endif /* VMS */ 6391 6392#define ENV_HUNK(n) (unsigned) ((((n) + 1) | 31) + 1) 6393 6394/* 6395 * If we do not have unsetenv(), make consistent updates for environ[]. 6396 * This could happen on some older machines due to the uneven standardization 6397 * process for the two functions. 6398 * 6399 * That is, putenv() makes a copy of environ, and some implementations do not 6400 * update the environ pointer, so the fallback when unsetenv() is missing would 6401 * not work as intended. Likewise, the reverse could be true, i.e., unsetenv 6402 * could copy environ. 6403 */ 6404#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV) 6405#undef HAVE_PUTENV 6406#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV) 6407#undef HAVE_UNSETENV 6408#endif 6409 6410/* 6411 * copy the environment before Setenv'ing. 6412 */ 6413void 6414xtermCopyEnv(char **oldenv) 6415{ 6416#ifdef HAVE_PUTENV 6417 (void) oldenv; 6418#else 6419 unsigned size; 6420 char **newenv; 6421 6422 for (size = 0; oldenv[size] != NULL; size++) { 6423 ; 6424 } 6425 6426 newenv = TypeCallocN(char *, ENV_HUNK(size)); 6427 memmove(newenv, oldenv, size * sizeof(char *)); 6428 environ = newenv; 6429#endif 6430} 6431 6432#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV) 6433static int 6434findEnv(const char *var, int *lengthp) 6435{ 6436 char *test; 6437 int envindex = 0; 6438 size_t len = strlen(var); 6439 int found = -1; 6440 6441 TRACE(("findEnv(%s=..)\n", var)); 6442 6443 while ((test = environ[envindex]) != NULL) { 6444 if (strncmp(test, var, len) == 0 && test[len] == '=') { 6445 found = envindex; 6446 break; 6447 } 6448 envindex++; 6449 } 6450 *lengthp = envindex; 6451 return found; 6452} 6453#endif 6454 6455/* 6456 * sets the value of var to be arg in the Unix 4.2 BSD environment env. 6457 * Var should end with '=' (bindings are of the form "var=value"). 6458 * This procedure assumes the memory for the first level of environ 6459 * was allocated using calloc, with enough extra room at the end so not 6460 * to have to do a realloc(). 6461 */ 6462void 6463xtermSetenv(const char *var, const char *value) 6464{ 6465 if (value != 0) { 6466#ifdef HAVE_PUTENV 6467 char *both = malloc(2 + strlen(var) + strlen(value)); 6468 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 6469 if (both) { 6470 sprintf(both, "%s=%s", var, value); 6471 putenv(both); 6472 } 6473#else 6474 size_t len = strlen(var); 6475 int envindex; 6476 int found = findEnv(var, &envindex); 6477 6478 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 6479 6480 if (found < 0) { 6481 unsigned need = ENV_HUNK(envindex + 1); 6482 unsigned have = ENV_HUNK(envindex); 6483 6484 if (need > have) { 6485 char **newenv; 6486 newenv = TypeMallocN(char *, need); 6487 if (newenv == 0) { 6488 xtermWarning("Cannot increase environment\n"); 6489 return; 6490 } 6491 memmove(newenv, environ, have * sizeof(*newenv)); 6492 free(environ); 6493 environ = newenv; 6494 } 6495 6496 found = envindex; 6497 environ[found + 1] = NULL; 6498 environ = environ; 6499 } 6500 6501 environ[found] = malloc(2 + len + strlen(value)); 6502 if (environ[found] == 0) { 6503 xtermWarning("Cannot allocate environment %s\n", var); 6504 return; 6505 } 6506 sprintf(environ[found], "%s=%s", var, value); 6507#endif 6508 } 6509} 6510 6511void 6512xtermUnsetenv(const char *var) 6513{ 6514 TRACE(("xtermUnsetenv(%s)\n", var)); 6515#ifdef HAVE_UNSETENV 6516 unsetenv(var); 6517#else 6518 { 6519 int ignore; 6520 int item = findEnv(var, &ignore); 6521 if (item >= 0) { 6522 while ((environ[item] = environ[item + 1]) != 0) { 6523 ++item; 6524 } 6525 } 6526 } 6527#endif 6528} 6529 6530/*ARGSUSED*/ 6531int 6532xerror(Display *d, XErrorEvent *ev) 6533{ 6534 xtermWarning("warning, error event received:\n"); 6535 TRACE_X_ERR(d, ev); 6536 (void) XmuPrintDefaultErrorMessage(d, ev, stderr); 6537 Exit(ERROR_XERROR); 6538 return 0; /* appease the compiler */ 6539} 6540 6541void 6542ice_error(IceConn iceConn) 6543{ 6544 (void) iceConn; 6545 6546 xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n", 6547 (long) getpid(), errno); 6548 6549 Exit(ERROR_ICEERROR); 6550} 6551 6552/*ARGSUSED*/ 6553int 6554xioerror(Display *dpy) 6555{ 6556 int the_error = errno; 6557 6558 xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", 6559 the_error, SysErrorMsg(the_error), 6560 DisplayString(dpy)); 6561 6562 Exit(ERROR_XIOERROR); 6563 return 0; /* appease the compiler */ 6564} 6565 6566void 6567xt_error(String message) 6568{ 6569 xtermWarning("Xt error: %s\n", message); 6570 6571 /* 6572 * Check for the obvious - Xt does a poor job of reporting this. 6573 */ 6574 if (x_getenv("DISPLAY") == 0) { 6575 xtermWarning("DISPLAY is not set\n"); 6576 } 6577 exit(1); 6578} 6579 6580int 6581XStrCmp(char *s1, char *s2) 6582{ 6583 if (s1 && s2) 6584 return (strcmp(s1, s2)); 6585 if (s1 && *s1) 6586 return (1); 6587 if (s2 && *s2) 6588 return (-1); 6589 return (0); 6590} 6591 6592#if OPT_TEK4014 6593static void 6594withdraw_window(Display *dpy, Window w, int scr) 6595{ 6596 TRACE(("withdraw_window %#lx\n", (long) w)); 6597 (void) XmuUpdateMapHints(dpy, w, NULL); 6598 XWithdrawWindow(dpy, w, scr); 6599 return; 6600} 6601#endif 6602 6603void 6604set_vt_visibility(Bool on) 6605{ 6606 XtermWidget xw = term; 6607 TScreen *screen = TScreenOf(xw); 6608 6609 TRACE(("set_vt_visibility(%d)\n", on)); 6610 if (on) { 6611 if (!screen->Vshow && xw) { 6612 VTInit(xw); 6613 XtMapWidget(XtParent(xw)); 6614#if OPT_TOOLBAR 6615 /* we need both of these during initialization */ 6616 XtMapWidget(SHELL_OF(xw)); 6617 ShowToolbar(resource.toolBar); 6618#endif 6619 screen->Vshow = True; 6620 } 6621 } 6622#if OPT_TEK4014 6623 else { 6624 if (screen->Vshow && xw) { 6625 withdraw_window(XtDisplay(xw), 6626 VShellWindow(xw), 6627 XScreenNumberOfScreen(XtScreen(xw))); 6628 screen->Vshow = False; 6629 } 6630 } 6631 set_vthide_sensitivity(); 6632 set_tekhide_sensitivity(); 6633 update_vttekmode(); 6634 update_tekshow(); 6635 update_vtshow(); 6636#endif 6637 return; 6638} 6639 6640#if OPT_TEK4014 6641void 6642set_tek_visibility(Bool on) 6643{ 6644 XtermWidget xw = term; 6645 6646 TRACE(("set_tek_visibility(%d)\n", on)); 6647 6648 if (on) { 6649 if (!TEK4014_SHOWN(xw)) { 6650 if (tekWidget == 0) { 6651 TekInit(); /* will exit on failure */ 6652 } 6653 if (tekWidget != 0) { 6654 Widget tekParent = SHELL_OF(tekWidget); 6655 XtRealizeWidget(tekParent); 6656 XtMapWidget(XtParent(tekWidget)); 6657#if OPT_TOOLBAR 6658 /* we need both of these during initialization */ 6659 XtMapWidget(tekParent); 6660 XtMapWidget(tekWidget); 6661#endif 6662 XtOverrideTranslations(tekParent, 6663 XtParseTranslationTable 6664 ("<Message>WM_PROTOCOLS: DeleteWindow()")); 6665 (void) XSetWMProtocols(XtDisplay(tekParent), 6666 XtWindow(tekParent), 6667 &wm_delete_window, 1); 6668 TEK4014_SHOWN(xw) = True; 6669 } 6670 } 6671 } else { 6672 if (TEK4014_SHOWN(xw) && tekWidget) { 6673 withdraw_window(XtDisplay(tekWidget), 6674 TShellWindow, 6675 XScreenNumberOfScreen(XtScreen(tekWidget))); 6676 TEK4014_SHOWN(xw) = False; 6677 } 6678 } 6679 set_tekhide_sensitivity(); 6680 set_vthide_sensitivity(); 6681 update_vtshow(); 6682 update_tekshow(); 6683 update_vttekmode(); 6684 return; 6685} 6686 6687void 6688end_tek_mode(void) 6689{ 6690 XtermWidget xw = term; 6691 6692 if (TEK4014_ACTIVE(xw)) { 6693 FlushLog(xw); 6694 TEK4014_ACTIVE(xw) = False; 6695 xtermSetWinSize(xw); 6696 longjmp(Tekend, 1); 6697 } 6698 return; 6699} 6700 6701void 6702end_vt_mode(void) 6703{ 6704 XtermWidget xw = term; 6705 6706 if (!TEK4014_ACTIVE(xw)) { 6707 FlushLog(xw); 6708 set_tek_visibility(True); 6709 TEK4014_ACTIVE(xw) = True; 6710 TekSetWinSize(tekWidget); 6711 longjmp(VTend, 1); 6712 } 6713 return; 6714} 6715 6716void 6717switch_modes(Bool tovt) /* if true, then become vt mode */ 6718{ 6719 if (tovt) { 6720 if (tekRefreshList) 6721 TekRefresh(tekWidget); 6722 end_tek_mode(); /* WARNING: this does a longjmp... */ 6723 } else { 6724 end_vt_mode(); /* WARNING: this does a longjmp... */ 6725 } 6726} 6727 6728void 6729hide_vt_window(void) 6730{ 6731 set_vt_visibility(False); 6732 if (!TEK4014_ACTIVE(term)) 6733 switch_modes(False); /* switch to tek mode */ 6734} 6735 6736void 6737hide_tek_window(void) 6738{ 6739 set_tek_visibility(False); 6740 tekRefreshList = (TekLink *) 0; 6741 if (TEK4014_ACTIVE(term)) 6742 switch_modes(True); /* does longjmp to vt mode */ 6743} 6744#endif /* OPT_TEK4014 */ 6745 6746static const char * 6747skip_punct(const char *s) 6748{ 6749 while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { 6750 ++s; 6751 } 6752 return s; 6753} 6754 6755static int 6756cmp_options(const void *a, const void *b) 6757{ 6758 const char *s1 = skip_punct(((const OptionHelp *) a)->opt); 6759 const char *s2 = skip_punct(((const OptionHelp *) b)->opt); 6760 return strcmp(s1, s2); 6761} 6762 6763static int 6764cmp_resources(const void *a, const void *b) 6765{ 6766 return strcmp(((const XrmOptionDescRec *) a)->option, 6767 ((const XrmOptionDescRec *) b)->option); 6768} 6769 6770XrmOptionDescRec * 6771sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) 6772{ 6773 static XrmOptionDescRec *res_array = 0; 6774 6775#ifdef NO_LEAKS 6776 if (descs == 0) { 6777 FreeAndNull(res_array); 6778 } else 6779#endif 6780 if (res_array == 0) { 6781 Cardinal j; 6782 6783 /* make a sorted index to 'resources' */ 6784 res_array = TypeCallocN(XrmOptionDescRec, res_count); 6785 if (res_array != 0) { 6786 for (j = 0; j < res_count; j++) 6787 res_array[j] = descs[j]; 6788 qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources); 6789 } 6790 } 6791 return res_array; 6792} 6793 6794/* 6795 * The first time this is called, construct sorted index to the main program's 6796 * list of options, taking into account the on/off options which will be 6797 * compressed into one token. It's a lot simpler to do it this way than 6798 * maintain the list in sorted form with lots of ifdef's. 6799 */ 6800OptionHelp * 6801sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) 6802{ 6803 static OptionHelp *opt_array = 0; 6804 6805#ifdef NO_LEAKS 6806 if (descs == 0 && opt_array != 0) { 6807 sortedOptDescs(descs, numDescs); 6808 FreeAndNull(opt_array); 6809 return 0; 6810 } else if (options == 0 || descs == 0) { 6811 return 0; 6812 } 6813#endif 6814 6815 if (opt_array == 0) { 6816 size_t opt_count, j; 6817#if OPT_TRACE 6818 Cardinal k; 6819 XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); 6820 int code; 6821 const char *mesg; 6822#else 6823 (void) descs; 6824 (void) numDescs; 6825#endif 6826 6827 /* count 'options' and make a sorted index to it */ 6828 for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { 6829 ; 6830 } 6831 opt_array = TypeCallocN(OptionHelp, opt_count + 1); 6832 for (j = 0; j < opt_count; j++) 6833 opt_array[j] = options[j]; 6834 qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); 6835 6836 /* supply the "turn on/off" strings if needed */ 6837#if OPT_TRACE 6838 for (j = 0; j < opt_count; j++) { 6839 if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) { 6840 char temp[80]; 6841 const char *name = opt_array[j].opt + 3; 6842 for (k = 0; k < numDescs; ++k) { 6843 const char *value = res_array[k].value; 6844 if (res_array[k].option[0] == '-') { 6845 code = -1; 6846 } else if (res_array[k].option[0] == '+') { 6847 code = 1; 6848 } else { 6849 code = 0; 6850 } 6851 sprintf(temp, "%.*s", 6852 (int) sizeof(temp) - 2, 6853 opt_array[j].desc); 6854 if (x_strindex(temp, "inhibit") != 0) 6855 code = -code; 6856 if (code != 0 6857 && res_array[k].value != 0 6858 && !strcmp(name, res_array[k].option + 1)) { 6859 if (((code < 0) && !strcmp(value, "on")) 6860 || ((code > 0) && !strcmp(value, "off")) 6861 || ((code > 0) && !strcmp(value, "0"))) { 6862 mesg = "turn on/off"; 6863 } else { 6864 mesg = "turn off/on"; 6865 } 6866 if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) { 6867 if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) { 6868 char *s = malloc(strlen(mesg) 6869 + strlen(opt_array[j].desc) 6870 + 2); 6871 if (s != 0) { 6872 sprintf(s, "%s %s", mesg, opt_array[j].desc); 6873 opt_array[j].desc = s; 6874 } 6875 } else { 6876 TRACE(("OOPS ")); 6877 } 6878 } 6879 TRACE(("%s: %s %s: %s (%s)\n", 6880 mesg, 6881 res_array[k].option, 6882 res_array[k].value, 6883 opt_array[j].opt, 6884 opt_array[j].desc)); 6885 break; 6886 } 6887 } 6888 } 6889 } 6890#endif 6891 } 6892 return opt_array; 6893} 6894 6895/* 6896 * Report the character-type locale that xterm was started in. 6897 */ 6898String 6899xtermEnvLocale(void) 6900{ 6901 static String result; 6902 6903 if (result == 0) { 6904 if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) { 6905 result = x_strdup("C"); 6906 } else { 6907 result = x_strdup(result); 6908 } 6909 TRACE(("xtermEnvLocale ->%s\n", result)); 6910 } 6911 return result; 6912} 6913 6914char * 6915xtermEnvEncoding(void) 6916{ 6917 static char *result; 6918 6919 if (result == 0) { 6920#ifdef HAVE_LANGINFO_CODESET 6921 result = nl_langinfo(CODESET); 6922#else 6923 const char *locale = xtermEnvLocale(); 6924 if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { 6925 result = x_strdup("ASCII"); 6926 } else { 6927 result = x_strdup("ISO-8859-1"); 6928 } 6929#endif 6930 TRACE(("xtermEnvEncoding ->%s\n", result)); 6931 } 6932 return result; 6933} 6934 6935#if OPT_WIDE_CHARS 6936/* 6937 * Tell whether xterm was started in a locale that uses UTF-8 encoding for 6938 * characters. That environment is inherited by subprocesses and used in 6939 * various library calls. 6940 */ 6941Bool 6942xtermEnvUTF8(void) 6943{ 6944 static Bool init = False; 6945 static Bool result = False; 6946 6947 if (!init) { 6948 init = True; 6949#ifdef HAVE_LANGINFO_CODESET 6950 result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); 6951#else 6952 { 6953 char *locale = x_strdup(xtermEnvLocale()); 6954 int n; 6955 for (n = 0; locale[n] != 0; ++n) { 6956 locale[n] = x_toupper(locale[n]); 6957 } 6958 if (strstr(locale, "UTF-8") != 0) 6959 result = True; 6960 else if (strstr(locale, "UTF8") != 0) 6961 result = True; 6962 free(locale); 6963 } 6964#endif 6965 TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); 6966 } 6967 return result; 6968} 6969#endif /* OPT_WIDE_CHARS */ 6970 6971/* 6972 * Check if the current widget, or any parent, is the VT100 "xterm" widget. 6973 */ 6974XtermWidget 6975getXtermWidget(Widget w) 6976{ 6977 XtermWidget xw; 6978 6979 if (w == 0) { 6980 xw = (XtermWidget) CURRENT_EMU(); 6981 if (!IsXtermWidget(xw)) { 6982 xw = 0; 6983 } 6984 } else if (IsXtermWidget(w)) { 6985 xw = (XtermWidget) w; 6986 } else { 6987 xw = getXtermWidget(XtParent(w)); 6988 } 6989 TRACE2(("getXtermWidget %p -> %p\n", w, xw)); 6990 return xw; 6991} 6992 6993#if OPT_SESSION_MGT 6994 6995#if OPT_TRACE 6996static void 6997trace_1_SM(const char *tag, String name) 6998{ 6999 Arg args[1]; 7000 char *buf = 0; 7001 7002 XtSetArg(args[0], name, &buf); 7003 XtGetValues(toplevel, args, 1); 7004 7005 if (strstr(name, "Path") || strstr(name, "Directory")) { 7006 TRACE(("%s %s: %s\n", tag, name, NonNull(buf))); 7007 } else if (strstr(name, "Command")) { 7008 if (buf != NULL) { 7009 char **vec = (char **) (void *) buf; 7010 int n; 7011 TRACE(("%s %s:\n", tag, name)); 7012 for (n = 0; vec[n] != NULL; ++n) { 7013 TRACE((" arg[%d] = %s\n", n, vec[n])); 7014 } 7015 } else { 7016 TRACE(("%s %s: %p\n", tag, name, buf)); 7017 } 7018 } else { 7019 TRACE(("%s %s: %p\n", tag, name, buf)); 7020 } 7021} 7022 7023static void 7024trace_SM_props(void) 7025{ 7026 /* *INDENT-OFF* */ 7027 static struct { String app, cls; } table[] = { 7028 { XtNcurrentDirectory, XtCCurrentDirectory }, 7029 { XtNdieCallback, XtNdiscardCommand }, 7030 { XtCDiscardCommand, XtNenvironment }, 7031 { XtCEnvironment, XtNinteractCallback }, 7032 { XtNjoinSession, XtCJoinSession }, 7033 { XtNprogramPath, XtCProgramPath }, 7034 { XtNresignCommand, XtCResignCommand }, 7035 { XtNrestartCommand, XtCRestartCommand }, 7036 { XtNrestartStyle, XtCRestartStyle }, 7037 { XtNsaveCallback, XtNsaveCompleteCallback }, 7038 { XtNsessionID, XtCSessionID }, 7039 { XtNshutdownCommand, XtCShutdownCommand }, 7040 }; 7041 /* *INDENT-ON* */ 7042 Cardinal n; 7043 TRACE(("Session properties:\n")); 7044 for (n = 0; n < XtNumber(table); ++n) { 7045 trace_1_SM("app", table[n].app); 7046 trace_1_SM("cls", table[n].cls); 7047 } 7048} 7049#define TRACE_SM_PROPS() trace_SM_props() 7050#else 7051#define TRACE_SM_PROPS() /* nothing */ 7052#endif 7053 7054static void 7055die_callback(Widget w GCC_UNUSED, 7056 XtPointer client_data GCC_UNUSED, 7057 XtPointer call_data GCC_UNUSED) 7058{ 7059 TRACE(("die_callback %p\n", die_callback)); 7060 TRACE_SM_PROPS(); 7061 NormalExit(); 7062} 7063 7064static void 7065save_callback(Widget w GCC_UNUSED, 7066 XtPointer client_data GCC_UNUSED, 7067 XtPointer call_data) 7068{ 7069 XtCheckpointToken token = (XtCheckpointToken) call_data; 7070 TRACE(("save_callback:\n")); 7071 TRACE(("... save_type <-%d\n", token->save_type)); 7072 TRACE(("... interact_style <-%d\n", token->interact_style)); 7073 TRACE(("... shutdown <-%s\n", BtoS(token->shutdown))); 7074 TRACE(("... fast <-%s\n", BtoS(token->fast))); 7075 TRACE(("... cancel_shutdown <-%s\n", BtoS(token->cancel_shutdown))); 7076 TRACE(("... phase <-%d\n", token->phase)); 7077 TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type)); 7078 TRACE(("... request_cancel ->%s\n", BtoS(token->request_cancel))); 7079 TRACE(("... request_next_phase ->%s\n", BtoS(token->request_next_phase))); 7080 TRACE(("... save_success ->%s\n", BtoS(token->save_success))); 7081 xtermUpdateRestartCommand(term); 7082 /* we have nothing more to save */ 7083 token->save_success = True; 7084} 7085 7086static void 7087icewatch(IceConn iceConn, 7088 IcePointer clientData GCC_UNUSED, 7089 Bool opening, 7090 IcePointer * watchData GCC_UNUSED) 7091{ 7092 if (opening) { 7093 ice_fd = IceConnectionNumber(iceConn); 7094 TRACE(("got IceConnectionNumber %d\n", ice_fd)); 7095 } else { 7096 ice_fd = -1; 7097 TRACE(("reset IceConnectionNumber\n")); 7098 } 7099} 7100 7101void 7102xtermOpenSession(void) 7103{ 7104 if (resource.sessionMgt) { 7105 TRACE(("Enabling session-management callbacks\n")); 7106 XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL); 7107 XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL); 7108 7109 TRACE_SM_PROPS(); 7110 } 7111} 7112 7113void 7114xtermCloseSession(void) 7115{ 7116 IceRemoveConnectionWatch(icewatch, NULL); 7117} 7118 7119typedef enum { 7120 B_ARG = 0, 7121 I_ARG, 7122 D_ARG, 7123 S_ARG 7124} ParamType; 7125 7126#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) } 7127#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) } 7128#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) } 7129#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) } 7130 7131typedef struct { 7132 const char name[30]; 7133 ParamType type; 7134 Cardinal offset; 7135} FontParams; 7136 7137/* *INDENT-OFF* */ 7138static const FontParams fontParams[] = { 7139 Iarg(XtNinitialFont, screen.menu_font_number), /* "-fc" */ 7140 Barg(XtNallowBoldFonts, screen.allowBoldFonts), /* menu */ 7141#if OPT_BOX_CHARS 7142 Barg(XtNforceBoxChars, screen.force_box_chars), /* "-fbx" */ 7143 Barg(XtNforcePackedFont, screen.force_packed), /* menu */ 7144#endif 7145#if OPT_DEC_CHRSET 7146 Barg(XtNfontDoublesize, screen.font_doublesize), /* menu */ 7147#endif 7148#if OPT_WIDE_CHARS 7149 Barg(XtNutf8Fonts, screen.utf8_fonts), /* menu */ 7150#endif 7151#if OPT_RENDERFONT 7152 Darg(XtNfaceSize, misc.face_size[0]), /* "-fs" */ 7153 Sarg(XtNfaceName, misc.default_xft.f_n), /* "-fa" */ 7154 Sarg(XtNrenderFont, misc.render_font_s), /* (resource) */ 7155#endif 7156}; 7157/* *INDENT-ON* */ 7158 7159#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2) 7160#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset) 7161 7162/* 7163 * If no widget is given, no value is used. 7164 */ 7165static char * 7166formatFontParam(char *result, XtermWidget xw, const FontParams * parameter) 7167{ 7168 sprintf(result, "%s*%s:", ProgramName, parameter->name); 7169 if (xw != None) { 7170 char *next = result + strlen(result); 7171 switch (parameter->type) { 7172 case B_ARG: 7173 sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset) 7174 ? "true" 7175 : "false"); 7176 break; 7177 case I_ARG: 7178 sprintf(next, "%d", TypedPtr(int)); 7179 break; 7180 case D_ARG: 7181 sprintf(next, "%.1f", TypedPtr(float)); 7182 break; 7183 case S_ARG: 7184 strcpy(next, TypedPtr(char *)); 7185#if OPT_RENDERFONT 7186 if (!strcmp(parameter->name, XtNfaceName)) { 7187 if (IsEmpty(next) 7188 && xw->work.render_font) { 7189 strcpy(next, DEFFACENAME_AUTO); 7190 } 7191 } else if (!strcmp(parameter->name, XtNrenderFont)) { 7192 if (xw->work.render_font == erDefault 7193 && IsEmpty(xw->misc.default_xft.f_n)) { 7194 strcpy(next, "DefaultOff"); 7195 } 7196 } 7197#endif 7198 break; 7199 } 7200 } 7201 return result; 7202} 7203 7204#if OPT_TRACE 7205static void 7206dumpFontParams(XtermWidget xw) 7207{ 7208 char buffer[1024]; 7209 Cardinal n; 7210 7211 TRACE(("FontParams:\n")); 7212 for (n = 0; n < XtNumber(fontParams); ++n) { 7213 TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n))); 7214 } 7215} 7216#else 7217#define dumpFontParams(xw) /* nothing */ 7218#endif 7219 7220static Boolean 7221findFontParams(int argc, char **argv) 7222{ 7223 Boolean result = False; 7224 7225 if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) { 7226 int n; 7227 7228 for (n = 0; n < RESTART_PARAMS; ++n) { 7229 int my_index = argc - restart_params - n - 1; 7230 int my_param = (RESTART_PARAMS - n - 1) / 2; 7231 char *actual = argv[my_index]; 7232 char expect[1024]; 7233 Boolean value = (Boolean) ((n % 2) == 0); 7234 7235 result = False; 7236 TRACE(("...index: %d\n", my_index)); 7237 TRACE(("...param: %d\n", my_param)); 7238 TRACE(("...actual %s\n", actual)); 7239 if (IsEmpty(actual)) 7240 break; 7241 7242 if (value) { 7243 formatFontParam(expect, None, fontParams + my_param); 7244 } else { 7245 strcpy(expect, "-xrm"); 7246 } 7247 7248 TRACE(("...expect %s\n", expect)); 7249 7250 if (value) { 7251 if (strlen(expect) >= strlen(actual)) 7252 break; 7253 if (strncmp(expect, actual, strlen(expect))) 7254 break; 7255 } else { 7256 if (strcmp(actual, expect)) 7257 break; 7258 } 7259 TRACE(("fixme/ok:%d\n", n)); 7260 result = True; 7261 } 7262 TRACE(("findFontParams: %s (tested %d of %d parameters)\n", 7263 BtoS(result), n + 1, RESTART_PARAMS)); 7264 } 7265 return result; 7266} 7267 7268static int 7269insertFontParams(XtermWidget xw, int *targetp, Boolean first) 7270{ 7271 int changed = 0; 7272 int n; 7273 int target = *targetp; 7274 char buffer[1024]; 7275 const char *option = "-xrm"; 7276 7277 for (n = 0; n < (int) XtNumber(fontParams); ++n) { 7278 formatFontParam(buffer, xw, fontParams + n); 7279 TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer)); 7280 if (restart_command[target] == NULL) 7281 restart_command[target] = x_strdup(option); 7282 ++target; 7283 if (first) { 7284 restart_command[target] = x_strdup(buffer); 7285 ++changed; 7286 } else if (restart_command[target] == NULL 7287 || strcmp(restart_command[target], buffer)) { 7288 free(restart_command[target]); 7289 restart_command[target] = x_strdup(buffer); 7290 ++changed; 7291 } 7292 ++target; 7293 } 7294 *targetp = target; 7295 return changed; 7296} 7297 7298void 7299xtermUpdateRestartCommand(XtermWidget xw) 7300{ 7301 if (resource.sessionMgt) { 7302 Arg args[1]; 7303 char **argv = 0; 7304 7305 XtSetArg(args[0], XtNrestartCommand, &argv); 7306 XtGetValues(toplevel, args, 1); 7307 if (argv != NULL) { 7308 static int my_params = 0; 7309 7310 int changes = 0; 7311 Boolean first = False; 7312 int argc; 7313 int want; 7314 int source, target; 7315 7316 TRACE(("xtermUpdateRestartCommand\n")); 7317 dumpFontParams(xw); 7318 for (argc = 0; argv[argc] != NULL; ++argc) { 7319 TRACE((" arg[%d] = %s\n", argc, argv[argc])); 7320 ; 7321 } 7322 want = argc - (restart_params + RESTART_PARAMS); 7323 7324 TRACE((" argc: %d\n", argc)); 7325 TRACE((" restart_params: %d\n", restart_params)); 7326 TRACE((" want to insert: %d\n", want)); 7327 7328 /* 7329 * If we already have the font-choice option, do not add it again. 7330 */ 7331 if (findFontParams(argc, argv)) { 7332 my_params = (want); 7333 } else { 7334 first = True; 7335 my_params = (argc - restart_params); 7336 } 7337 TRACE((" my_params: %d\n", my_params)); 7338 7339 if (my_params > argc) { 7340 TRACE((" re-allocate restartCommand\n")); 7341 FreeAndNull(restart_command); 7342 } 7343 7344 if (restart_command == NULL) { 7345 int need = argc + RESTART_PARAMS + 1; 7346 7347 restart_command = TypeCallocN(char *, need); 7348 7349 TRACE(("..inserting font-parameters\n")); 7350 for (source = target = 0; source < argc; ++source) { 7351 if (source == my_params) { 7352 changes += insertFontParams(xw, &target, first); 7353 if (!first) { 7354 source += (RESTART_PARAMS - 1); 7355 continue; 7356 } 7357 } 7358 if (argv[source] == NULL) 7359 break; 7360 restart_command[target++] = x_strdup(argv[source]); 7361 } 7362 restart_command[target] = NULL; 7363 } else { 7364 TRACE(("..replacing font-parameters\n")); 7365 target = my_params; 7366 changes += insertFontParams(xw, &target, first); 7367 } 7368 if (changes) { 7369 TRACE(("..%d parameters changed\n", changes)); 7370 XtSetArg(args[0], XtNrestartCommand, restart_command); 7371 XtSetValues(toplevel, args, 1); 7372 } else { 7373 TRACE(("..NO parameters changed\n")); 7374 } 7375 } 7376 TRACE_SM_PROPS(); 7377 } 7378} 7379#endif /* OPT_SESSION_MGT */ 7380 7381Widget 7382xtermOpenApplication(XtAppContext * app_context_return, 7383 String my_class, 7384 XrmOptionDescRec * options, 7385 Cardinal num_options, 7386 int *argc_in_out, 7387 char **argv_in_out, 7388 String *fallback_resources, 7389 WidgetClass widget_class, 7390 ArgList args, 7391 Cardinal num_args) 7392{ 7393 Widget result; 7394 7395 XtSetErrorHandler(xt_error); 7396#if OPT_SESSION_MGT 7397 result = XtOpenApplication(app_context_return, 7398 my_class, 7399 options, 7400 num_options, 7401 argc_in_out, 7402 argv_in_out, 7403 fallback_resources, 7404 widget_class, 7405 args, 7406 num_args); 7407 IceAddConnectionWatch(icewatch, NULL); 7408#else 7409 (void) widget_class; 7410 (void) args; 7411 (void) num_args; 7412 result = XtAppInitialize(app_context_return, 7413 my_class, 7414 options, 7415 num_options, 7416 argc_in_out, 7417 argv_in_out, 7418 fallback_resources, 7419 NULL, 0); 7420#endif /* OPT_SESSION_MGT */ 7421 XtSetErrorHandler(NULL); 7422 7423 return result; 7424} 7425 7426/* 7427 * Some calls to XGetAtom() will fail, and we don't want to stop. So we use 7428 * our own error-handler. 7429 */ 7430/* ARGSUSED */ 7431int 7432ignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED) 7433{ 7434 return 1; 7435} 7436 7437static int x11_errors; 7438 7439static int 7440catch_x11_error(Display *display, XErrorEvent *error_event) 7441{ 7442 (void) display; 7443 (void) error_event; 7444 ++x11_errors; 7445 return 0; 7446} 7447 7448Boolean 7449xtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs) 7450{ 7451 Boolean result = False; 7452 Status code; 7453 7454 memset(attrs, 0, sizeof(*attrs)); 7455 if (win != None) { 7456 XErrorHandler save = XSetErrorHandler(catch_x11_error); 7457 x11_errors = 0; 7458 code = XGetWindowAttributes(dpy, win, attrs); 7459 XSetErrorHandler(save); 7460 result = (Boolean) ((code != 0) && !x11_errors); 7461 if (result) { 7462 TRACE_WIN_ATTRS(attrs); 7463 } else { 7464 xtermWarning("invalid window-id %ld\n", (long) win); 7465 } 7466 } 7467 return result; 7468} 7469 7470Boolean 7471xtermGetWinProp(Display *display, 7472 Window win, 7473 Atom property, 7474 long long_offset, 7475 long long_length, 7476 Atom req_type, 7477 Atom *actual_type_return, 7478 int *actual_format_return, 7479 unsigned long *nitems_return, 7480 unsigned long *bytes_after_return, 7481 unsigned char **prop_return) 7482{ 7483 Boolean result = False; 7484 7485 if (win != None) { 7486 XErrorHandler save = XSetErrorHandler(catch_x11_error); 7487 x11_errors = 0; 7488 if (XGetWindowProperty(display, 7489 win, 7490 property, 7491 long_offset, 7492 long_length, 7493 False, 7494 req_type, 7495 actual_type_return, 7496 actual_format_return, 7497 nitems_return, 7498 bytes_after_return, 7499 prop_return) == Success 7500 && x11_errors == 0) { 7501 result = True; 7502 } 7503 XSetErrorHandler(save); 7504 } 7505 return result; 7506} 7507 7508void 7509xtermEmbedWindow(Window winToEmbedInto) 7510{ 7511 Display *dpy = XtDisplay(toplevel); 7512 XWindowAttributes attrs; 7513 7514 TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto)); 7515 if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) { 7516 XtermWidget xw = term; 7517 TScreen *screen = TScreenOf(xw); 7518 7519 XtRealizeWidget(toplevel); 7520 7521 TRACE(("...reparenting toplevel %#lx into %#lx\n", 7522 XtWindow(toplevel), 7523 winToEmbedInto)); 7524 XReparentWindow(dpy, 7525 XtWindow(toplevel), 7526 winToEmbedInto, 0, 0); 7527 7528 screen->embed_high = (Dimension) attrs.height; 7529 screen->embed_wide = (Dimension) attrs.width; 7530 } 7531} 7532 7533void 7534free_string(String value) 7535{ 7536 free((void *) value); 7537} 7538 7539/* Set tty's idea of window size, using the given file descriptor 'fd'. */ 7540int 7541update_winsize(int fd, int rows, int cols, int height, int width) 7542{ 7543 int code = -1; 7544#ifdef TTYSIZE_STRUCT 7545 static int last_rows = -1; 7546 static int last_cols = -1; 7547 static int last_high = -1; 7548 static int last_wide = -1; 7549 7550 TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n", 7551 last_rows, last_cols, last_high, last_wide, 7552 rows, cols, height, width)); 7553 7554 if (rows != last_rows 7555 || cols != last_cols 7556 || last_high != height 7557 || last_wide != width) { 7558 TTYSIZE_STRUCT ts; 7559 7560 last_rows = rows; 7561 last_cols = cols; 7562 last_high = height; 7563 last_wide = width; 7564 setup_winsize(ts, rows, cols, height, width); 7565 TRACE_RC(code, SET_TTYSIZE(fd, ts)); 7566 trace_winsize(ts, "from SET_TTYSIZE"); 7567 } 7568#endif 7569 7570 (void) rows; 7571 (void) cols; 7572 (void) height; 7573 (void) width; 7574 7575 return code; 7576} 7577 7578/* 7579 * Update stty settings to match the values returned by dtterm window 7580 * manipulation 18 and 19. 7581 */ 7582void 7583xtermSetWinSize(XtermWidget xw) 7584{ 7585#if OPT_TEK4014 7586 if (!TEK4014_ACTIVE(xw)) 7587#endif 7588 if (XtIsRealized((Widget) xw)) { 7589 TScreen *screen = TScreenOf(xw); 7590 7591 TRACE(("xtermSetWinSize\n")); 7592 update_winsize(screen->respond, 7593 MaxRows(screen), 7594 MaxCols(screen), 7595 Height(screen), 7596 Width(screen)); 7597 } 7598} 7599 7600#if OPT_XTERM_SGR 7601 7602#if OPT_TRACE 7603static char * 7604traceIFlags(IFlags flags) 7605{ 7606 static char result[1000]; 7607 result[0] = '\0'; 7608#define DATA(name) if (flags & name) { strcat(result, " " #name); } 7609 DATA(INVERSE); 7610 DATA(UNDERLINE); 7611 DATA(BOLD); 7612 DATA(BLINK); 7613 DATA(INVISIBLE); 7614 DATA(BG_COLOR); 7615 DATA(FG_COLOR); 7616 7617#if OPT_WIDE_ATTRS 7618 DATA(ATR_FAINT); 7619 DATA(ATR_ITALIC); 7620 DATA(ATR_STRIKEOUT); 7621 DATA(ATR_DBL_UNDER); 7622 DATA(ATR_DIRECT_FG); 7623 DATA(ATR_DIRECT_BG); 7624#endif 7625#undef DATA 7626 return result; 7627} 7628 7629static char * 7630traceIStack(unsigned flags) 7631{ 7632 static char result[1000]; 7633 result[0] = '\0'; 7634#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); } 7635 DATA(INVERSE); 7636 DATA(UNDERLINE); 7637 DATA(BOLD); 7638 DATA(BLINK); 7639 DATA(INVISIBLE); 7640#if OPT_ISO_COLORS 7641 DATA(BG_COLOR); 7642 DATA(FG_COLOR); 7643#endif 7644 7645#if OPT_WIDE_ATTRS 7646 DATA(ATR_FAINT); 7647 DATA(ATR_ITALIC); 7648 DATA(ATR_STRIKEOUT); 7649 DATA(ATR_DBL_UNDER); 7650 /* direct-colors are a special case of ISO-colors (see above) */ 7651#endif 7652#undef DATA 7653 return result; 7654} 7655#endif 7656 7657void 7658xtermPushSGR(XtermWidget xw, int value) 7659{ 7660 SavedSGR *s = &(xw->saved_sgr); 7661 7662 TRACE(("xtermPushSGR %d mask %#x %s\n", 7663 s->used + 1, (unsigned) value, traceIStack((unsigned) value))); 7664 7665 if (s->used < MAX_SAVED_SGR) { 7666 s->stack[s->used].mask = (IFlags) value; 7667#define PUSH_FLAG(name) \ 7668 s->stack[s->used].name = xw->name;\ 7669 TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name))) 7670#define PUSH_DATA(name) \ 7671 s->stack[s->used].name = xw->name;\ 7672 TRACE(("...may pop %s %d\n", #name, xw->name)) 7673 PUSH_FLAG(flags); 7674#if OPT_ISO_COLORS 7675 PUSH_DATA(sgr_foreground); 7676 PUSH_DATA(sgr_background); 7677 PUSH_DATA(sgr_38_xcolors); 7678#endif 7679 } 7680 s->used++; 7681} 7682 7683#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits) 7684 7685void 7686xtermReportSGR(XtermWidget xw, XTermRect *value) 7687{ 7688 TScreen *screen = TScreenOf(xw); 7689 char reply[BUFSIZ]; 7690 CellData working; 7691 int row, col; 7692 Boolean first = True; 7693 7694 TRACE(("xtermReportSGR %d,%d - %d,%d\n", 7695 value->top, value->left, 7696 value->bottom, value->right)); 7697 7698 memset(&working, 0, sizeof(working)); 7699 for (row = value->top - 1; row < value->bottom; ++row) { 7700 LineData *ld = getLineData(screen, row); 7701 if (ld == 0) 7702 continue; 7703 for (col = value->left - 1; col < value->right; ++col) { 7704 if (first) { 7705 first = False; 7706 saveCellData(screen, &working, 0, ld, NULL, col); 7707 } 7708 working.attribs &= ld->attribs[col]; 7709#if OPT_ISO_COLORS 7710 if (working.attribs & FG_COLOR 7711 && GetCellColorFG(working.color) 7712 != GetCellColorFG(ld->color[col])) { 7713 IAttrClr(working.attribs, FG_COLOR); 7714 } 7715 if (working.attribs & BG_COLOR 7716 && GetCellColorBG(working.color) 7717 != GetCellColorBG(ld->color[col])) { 7718 IAttrClr(working.attribs, BG_COLOR); 7719 } 7720#endif 7721 } 7722 } 7723 xtermFormatSGR(xw, reply, 7724 working.attribs, 7725 GetCellColorFG(working.color), 7726 GetCellColorBG(working.color)); 7727 unparseputc1(xw, ANSI_CSI); 7728 unparseputs(xw, reply); 7729 unparseputc(xw, 'm'); 7730 unparse_end(xw); 7731} 7732 7733void 7734xtermPopSGR(XtermWidget xw) 7735{ 7736 SavedSGR *s = &(xw->saved_sgr); 7737 7738 TRACE(("xtermPopSGR %d\n", s->used)); 7739 7740 if (s->used > 0) { 7741 if (s->used-- <= MAX_SAVED_SGR) { 7742 IFlags mask = s->stack[s->used].mask; 7743 Boolean changed = False; 7744 7745 TRACE(("...mask %#x %s\n", mask, traceIStack(mask))); 7746 TRACE(("...old: %s\n", traceIFlags(xw->flags))); 7747 TRACE(("...new: %s\n", traceIFlags(s->stack[s->used].flags))); 7748#define POP_FLAG(name) \ 7749 if (xBIT(ps##name - 1) & mask) { \ 7750 if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \ 7751 changed = True; \ 7752 UIntClr(xw->flags, name); \ 7753 UIntSet(xw->flags, (s->stack[s->used].flags & name)); \ 7754 TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \ 7755 } \ 7756 } 7757#define POP_FLAG2(name,part) \ 7758 if (xBIT(ps##name - 1) & mask) { \ 7759 if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \ 7760 changed = True; \ 7761 UIntClr(xw->flags, part); \ 7762 UIntSet(xw->flags, (s->stack[s->used].flags & part)); \ 7763 TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \ 7764 } \ 7765 } 7766#define POP_DATA(name,value) \ 7767 if (xBIT(ps##name - 1) & mask) { \ 7768 Bool always = False; \ 7769 if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \ 7770 always = changed = True; \ 7771 UIntClr(xw->flags, name); \ 7772 UIntSet(xw->flags, (s->stack[s->used].flags & name)); \ 7773 TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \ 7774 } \ 7775 if (always || (xw->value != s->stack[s->used].value)) { \ 7776 TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \ 7777 xw->value = s->stack[s->used].value; \ 7778 changed = True; \ 7779 } \ 7780 } 7781 POP_FLAG(BOLD); 7782 POP_FLAG(UNDERLINE); 7783 POP_FLAG(BLINK); 7784 POP_FLAG(INVERSE); 7785 POP_FLAG(INVISIBLE); 7786#if OPT_WIDE_ATTRS 7787 if (xBIT(psATR_ITALIC - 1) & mask) { 7788 xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags); 7789 } 7790 POP_FLAG(ATR_ITALIC); 7791 POP_FLAG(ATR_FAINT); 7792 POP_FLAG(ATR_STRIKEOUT); 7793 POP_FLAG(ATR_DBL_UNDER); 7794#endif 7795#if OPT_ISO_COLORS 7796 POP_DATA(FG_COLOR, sgr_foreground); 7797 POP_DATA(BG_COLOR, sgr_background); 7798 POP_DATA(BG_COLOR, sgr_38_xcolors); 7799#if OPT_DIRECT_COLOR 7800 POP_FLAG2(FG_COLOR, ATR_DIRECT_FG); 7801 POP_FLAG2(BG_COLOR, ATR_DIRECT_BG); 7802#endif 7803 if (changed) { 7804 setExtendedColors(xw); 7805 } 7806#else 7807 (void) changed; 7808#endif 7809 } 7810#if OPT_ISO_COLORS 7811 TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n", 7812 traceIFlags(xw->flags), 7813 xw->sgr_foreground, 7814 xw->sgr_background, 7815 xw->sgr_38_xcolors ? " (SGR 38)" : "")); 7816#else 7817 TRACE(("xtermP -> flags%s\n", 7818 traceIFlags(xw->flags))); 7819#endif 7820 } 7821} 7822 7823#if OPT_ISO_COLORS 7824static ColorSlot * 7825allocColorSlot(XtermWidget xw, int slot) 7826{ 7827 SavedColors *s = &(xw->saved_colors); 7828 ColorSlot *result = NULL; 7829 7830 if (slot >= 0 && slot < MAX_SAVED_SGR) { 7831 if (s->palettes[slot] == NULL) { 7832 s->palettes[slot] = (ColorSlot *) calloc((size_t) 1, 7833 sizeof(ColorSlot) 7834 + (sizeof(ColorRes) 7835 * MAXCOLORS)); 7836 } 7837 result = s->palettes[slot]; 7838 } 7839 return result; 7840} 7841 7842static void 7843popOldColors(XtermWidget xw, ScrnColors * source) 7844{ 7845 Boolean changed = False; 7846 ScrnColors *target = xw->work.oldColors; 7847 7848 if (source->which != target->which) { 7849 changed = True; 7850 } else { 7851 int n; 7852 for (n = 0; n < NCOLORS; ++n) { 7853 if (COLOR_DEFINED(source, n)) { 7854 if (COLOR_DEFINED(target, n)) { 7855 if (source->colors[n] != target->colors[n]) { 7856 changed = True; 7857 break; 7858 } 7859 } else { 7860 changed = True; 7861 break; 7862 } 7863 } else if (COLOR_DEFINED(target, n)) { 7864 changed = True; 7865 break; 7866 } 7867 } 7868 } 7869 if (changed) { 7870 ChangeColors(xw, source); 7871 UpdateOldColors(xw, source); 7872 } 7873} 7874#endif /* OPT_ISO_COLORS */ 7875 7876#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False) 7877#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes)) 7878 7879/* 7880 * By default, a "push" increments the stack after copying to the current 7881 * slot. But a specific target allows one to copy into a specific slot. 7882 */ 7883void 7884xtermPushColors(XtermWidget xw, int value) 7885{ 7886#if OPT_ISO_COLORS 7887 SavedColors *s = &(xw->saved_colors); 7888 int pushed = s->used; 7889 int actual = (value <= 0) ? pushed : (value - 1); 7890 7891 TRACE(("xtermPushColors %d:%d\n", actual, pushed)); 7892 if (actual < MAX_SAVED_SGR && actual >= 0) { 7893 TScreen *screen = TScreenOf(xw); 7894 ColorSlot *palette; 7895 7896 if ((palette = allocColorSlot(xw, actual)) != NULL) { 7897 GetColors(xw, &(palette->base)); 7898 CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS); 7899 if (value < 0) { 7900 s->used++; 7901 if (s->last < s->used) 7902 s->last = s->used; 7903 } else { 7904 s->used = value; 7905 } 7906 } 7907 } 7908#else 7909 (void) xw; 7910 (void) value; 7911#endif 7912} 7913 7914void 7915xtermPopColors(XtermWidget xw, int value) 7916{ 7917#if OPT_ISO_COLORS 7918 SavedColors *s = &(xw->saved_colors); 7919 int popped = (s->used - 1); 7920 int actual = (value <= 0) ? popped : (value - 1); 7921 7922 TRACE(("xtermPopColors %d:%d\n", actual, popped)); 7923 if (actual < MAX_SAVED_SGR && actual >= 0) { 7924 TScreen *screen = TScreenOf(xw); 7925 ColorSlot *palette; 7926 7927 if ((palette = s->palettes[actual]) != NULL) { 7928 Boolean changed = DiffColorSlot(screen->Acolors, 7929 palette->ansi, 7930 MAXCOLORS); 7931 7932 GetOldColors(xw); 7933 popOldColors(xw, &(palette->base)); 7934 CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS); 7935 s->used = actual; 7936 if (changed) 7937 xtermRepaint(xw); 7938 } 7939 } 7940#else 7941 (void) xw; 7942 (void) value; 7943#endif 7944} 7945 7946void 7947xtermReportColors(XtermWidget xw) 7948{ 7949 ANSI reply; 7950 SavedColors *s = &(xw->saved_colors); 7951 7952 memset(&reply, 0, sizeof(reply)); 7953 reply.a_type = ANSI_CSI; 7954 reply.a_pintro = '?'; 7955 reply.a_param[reply.a_nparam++] = (ParmType) s->used; 7956 reply.a_param[reply.a_nparam++] = (ParmType) s->last; 7957 reply.a_inters = '#'; 7958 reply.a_final = 'Q'; 7959 unparseseq(xw, &reply); 7960} 7961#endif /* OPT_XTERM_SGR */ 7962