misc.c revision c48a5815
1/* $XTermId: misc.c,v 1.1007 2021/11/12 09:28:19 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 2596XVisualInfo * 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 (vi->class == TrueColor 2639 || vi->class == DirectColor)); 2640 2641 if (resource.reportColors) { 2642 printf(MYFMT, MYARG); 2643 } 2644 TRACE((MYFMT, MYARG)); 2645 TRACE(("...shifts %u/%u/%u\n", 2646 xw->rgb_shifts[0], 2647 xw->rgb_shifts[1], 2648 xw->rgb_shifts[2])); 2649 TRACE(("...widths %u/%u/%u\n", 2650 xw->rgb_widths[0], 2651 xw->rgb_widths[1], 2652 xw->rgb_widths[2])); 2653 } 2654 } 2655 return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL; 2656#undef MYFMT 2657#undef MYARG 2658} 2659 2660#if OPT_ISO_COLORS 2661static Bool 2662ReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final) 2663{ 2664 Bool result = False; 2665 2666 if (AllowColorOps(xw, ecGetAnsiColor)) { 2667 XColor color; 2668 char buffer[80]; 2669 2670 TRACE(("ReportAnsiColorRequest %d\n", colornum)); 2671 color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]); 2672 (void) QueryOneColor(xw, &color); 2673 sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x", 2674 opcode, 2675 (opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum, 2676 color.red, 2677 color.green, 2678 color.blue); 2679 unparseputc1(xw, ANSI_OSC); 2680 unparseputs(xw, buffer); 2681 unparseputc1(xw, final); 2682 result = True; 2683 } 2684 return result; 2685} 2686 2687static void 2688getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep) 2689{ 2690 if (getVisualInfo(xw)) { 2691 *typep = (unsigned) xw->visInfo->class; 2692 *sizep = (unsigned) xw->visInfo->colormap_size; 2693 } else { 2694 *typep = 0; 2695 *sizep = 0; 2696 } 2697} 2698 2699#define MAX_COLORTABLE 4096 2700 2701/* 2702 * Make only one call to XQueryColors(), since it can be slow. 2703 */ 2704static Boolean 2705loadColorTable(XtermWidget xw, unsigned length) 2706{ 2707 Colormap cmap = xw->core.colormap; 2708 TScreen *screen = TScreenOf(xw); 2709 Boolean result = (screen->cmap_data != 0); 2710 2711 if (!result 2712 && length != 0 2713 && length < MAX_COLORTABLE) { 2714 screen->cmap_data = TypeMallocN(XColor, (size_t) length); 2715 2716 if (screen->cmap_data != 0) { 2717 unsigned i; 2718 unsigned shift; 2719 2720 if (getVisualInfo(xw)) 2721 shift = xw->rgb_shifts[2]; 2722 else 2723 shift = 0; 2724 2725 screen->cmap_size = length; 2726 2727 for (i = 0; i < screen->cmap_size; i++) { 2728 screen->cmap_data[i].pixel = (unsigned long) i << shift; 2729 } 2730 result = (Boolean) (XQueryColors(screen->display, 2731 cmap, 2732 screen->cmap_data, 2733 (int) screen->cmap_size) != 0); 2734 } 2735 } 2736 return result; 2737} 2738 2739/***====================================================================***/ 2740 2741/* 2742 * Call this function with def->{red,green,blue} initialized, to obtain a pixel 2743 * value. 2744 */ 2745Boolean 2746AllocOneColor(XtermWidget xw, XColor *def) 2747{ 2748 TScreen *screen = TScreenOf(xw); 2749 XVisualInfo *visInfo; 2750 Boolean result = True; 2751 2752#define MaskIt(name,nn) \ 2753 ((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \ 2754 << xw->rgb_shifts[nn]) \ 2755 & xw->visInfo->name ##_mask) 2756 2757 if ((visInfo = getVisualInfo(xw)) != NULL && xw->has_rgb) { 2758 def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2); 2759 } else { 2760 Display *dpy = screen->display; 2761 if (!XAllocColor(dpy, xw->core.colormap, def)) { 2762 /* 2763 * Decide between foreground and background by a grayscale 2764 * approximation. 2765 */ 2766 int bright = def->red * 3 + def->green * 10 + def->blue; 2767 int levels = 14 * 0x8000; 2768 def->pixel = ((bright >= levels) 2769 ? xw->dft_background 2770 : xw->dft_foreground); 2771 result = False; 2772 } 2773 } 2774 return result; 2775} 2776 2777/***====================================================================***/ 2778 2779/* 2780 * Call this function with def->pixel set to the color that we want to convert 2781 * to separate red/green/blue. 2782 */ 2783Boolean 2784QueryOneColor(XtermWidget xw, XColor *def) 2785{ 2786 XVisualInfo *visInfo; 2787 Boolean result = True; 2788 2789#define UnMaskIt(name,nn) \ 2790 ((unsigned short)((def->pixel & xw->visInfo->name ##_mask) >> xw->rgb_shifts[nn])) 2791#define UnMaskIt2(name,nn) \ 2792 (unsigned short)((((UnMaskIt(name,nn) << 8) \ 2793 |UnMaskIt(name,nn))) << (8 - xw->rgb_widths[nn])) 2794 2795 if ((visInfo = getVisualInfo(xw)) != NULL && xw->has_rgb) { 2796 /* *INDENT-EQLS* */ 2797 def->red = UnMaskIt2(red, 0); 2798 def->green = UnMaskIt2(green, 1); 2799 def->blue = UnMaskIt2(blue, 2); 2800 } else if (!XQueryColor(TScreenOf(xw)->display, xw->core.colormap, def)) { 2801 result = False; 2802 } 2803 return result; 2804} 2805 2806/***====================================================================***/ 2807 2808/* 2809 * Find closest color for "def" in "cmap". 2810 * Set "def" to the resulting color. 2811 * 2812 * Based on Monish Shah's "find_closest_color()" for Vim 6.0, 2813 * modified with ideas from David Tong's "noflash" library. 2814 * The code from Vim in turn was derived from FindClosestColor() in Tcl/Tk. 2815 * 2816 * Return False if not able to find or allocate a color. 2817 */ 2818static Boolean 2819allocateClosestRGB(XtermWidget xw, XColor *def) 2820{ 2821 TScreen *screen = TScreenOf(xw); 2822 Boolean result = False; 2823 unsigned cmap_type; 2824 unsigned cmap_size; 2825 2826 getColormapInfo(xw, &cmap_type, &cmap_size); 2827 2828 if ((cmap_type & 1) != 0) { 2829 2830 if (loadColorTable(xw, cmap_size)) { 2831 char *tried = TypeCallocN(char, (size_t) cmap_size); 2832 2833 if (tried != 0) { 2834 unsigned attempts; 2835 2836 /* 2837 * Try (possibly each entry in the color map) to find the best 2838 * approximation to the requested color. 2839 */ 2840 for (attempts = 0; attempts < cmap_size; attempts++) { 2841 Boolean first = True; 2842 double bestRGB = 0.0; 2843 unsigned bestInx = 0; 2844 unsigned i; 2845 2846 for (i = 0; i < cmap_size; i++) { 2847 if (!tried[bestInx]) { 2848 double diff, thisRGB = 0.0; 2849 2850 /* 2851 * Look for the best match based on luminance. 2852 * Measure this by the least-squares difference of 2853 * the weighted R/G/B components from the color map 2854 * versus the requested color. Use the Y (luma) 2855 * component of the YIQ color space model for 2856 * weights that correspond to the luminance. 2857 */ 2858#define AddColorWeight(weight, color) \ 2859 diff = weight * (int) ((def->color) - screen->cmap_data[i].color); \ 2860 thisRGB += diff * diff 2861 2862 AddColorWeight(0.30, red); 2863 AddColorWeight(0.61, green); 2864 AddColorWeight(0.11, blue); 2865 2866 if (first || (thisRGB < bestRGB)) { 2867 first = False; 2868 bestInx = i; 2869 bestRGB = thisRGB; 2870 } 2871 } 2872 } 2873 if (AllocOneColor(xw, &screen->cmap_data[bestInx])) { 2874 *def = screen->cmap_data[bestInx]; 2875 TRACE(("...closest %x/%x/%x\n", def->red, 2876 def->green, def->blue)); 2877 result = True; 2878 break; 2879 } 2880 /* 2881 * It failed - either the color map entry was readonly, or 2882 * another client has allocated the entry. Mark the entry 2883 * so we will ignore it 2884 */ 2885 tried[bestInx] = True; 2886 } 2887 free(tried); 2888 } 2889 } 2890 } 2891 return result; 2892} 2893 2894#ifndef ULONG_MAX 2895#define ULONG_MAX (unsigned long)(~(0L)) 2896#endif 2897 2898/* 2899 * Allocate a color for the "ANSI" colors. That actually includes colors up 2900 * to 256. 2901 * 2902 * Returns 2903 * -1 on error 2904 * 0 on no change 2905 * 1 if a new color was allocated. 2906 */ 2907static int 2908AllocateAnsiColor(XtermWidget xw, 2909 ColorRes * res, 2910 const char *spec) 2911{ 2912 int result; 2913 XColor def; 2914 2915 if (xtermAllocColor(xw, &def, spec)) { 2916 if (res->mode == True && 2917 EQL_COLOR_RES(res, def.pixel)) { 2918 result = 0; 2919 } else { 2920 result = 1; 2921 SET_COLOR_RES(res, def.pixel); 2922 res->red = def.red; 2923 res->green = def.green; 2924 res->blue = def.blue; 2925 TRACE(("AllocateAnsiColor[%d] %s (rgb:%04x/%04x/%04x, pixel 0x%06lx)\n", 2926 (int) (res - TScreenOf(xw)->Acolors), spec, 2927 def.red, 2928 def.green, 2929 def.blue, 2930 def.pixel)); 2931 if (!res->mode) 2932 result = 0; 2933 res->mode = True; 2934 } 2935 } else { 2936 TRACE(("AllocateAnsiColor %s (failed)\n", spec)); 2937 result = -1; 2938 } 2939 return (result); 2940} 2941 2942Pixel 2943xtermGetColorRes(XtermWidget xw, ColorRes * res) 2944{ 2945 Pixel result = 0; 2946 2947 if (res->mode) { 2948 result = res->value; 2949 } else { 2950 TRACE(("xtermGetColorRes for Acolors[%d]\n", 2951 (int) (res - TScreenOf(xw)->Acolors))); 2952 2953 if (res >= TScreenOf(xw)->Acolors) { 2954 assert(res - TScreenOf(xw)->Acolors < MAXCOLORS); 2955 2956 if (AllocateAnsiColor(xw, res, res->resource) < 0) { 2957 res->value = TScreenOf(xw)->Tcolors[TEXT_FG].value; 2958 res->mode = -True; 2959 xtermWarning("Cannot allocate color \"%s\"\n", 2960 NonNull(res->resource)); 2961 } 2962 result = res->value; 2963 } else { 2964 result = 0; 2965 } 2966 } 2967 return result; 2968} 2969 2970static int 2971ChangeOneAnsiColor(XtermWidget xw, int color, const char *name) 2972{ 2973 int code; 2974 2975 if (color < 0 || color >= MAXCOLORS) { 2976 code = -1; 2977 } else { 2978 ColorRes *res = &(TScreenOf(xw)->Acolors[color]); 2979 2980 TRACE(("ChangeAnsiColor for Acolors[%d]\n", color)); 2981 code = AllocateAnsiColor(xw, res, name); 2982 } 2983 return code; 2984} 2985 2986/* 2987 * Set or query entries in the Acolors[] array by parsing pairs of color/name 2988 * values from the given buffer. 2989 * 2990 * The color can be any legal index into Acolors[], which consists of the 2991 * 16/88/256 "ANSI" colors, followed by special color values for the various 2992 * colorXX resources. The indices for the special color values are not 2993 * simple to work with, so an alternative is to use the calls which pass in 2994 * 'first' set to the beginning of those indices. 2995 * 2996 * If the name is "?", report to the host the current value for the color. 2997 */ 2998static Bool 2999ChangeAnsiColorRequest(XtermWidget xw, 3000 int opcode, 3001 char *buf, 3002 int first, 3003 int final) 3004{ 3005 int repaint = False; 3006 int code; 3007 int last = (MAXCOLORS - first); 3008 int queried = 0; 3009 3010 TRACE(("ChangeAnsiColorRequest string='%s'\n", buf)); 3011 3012 while (buf && *buf) { 3013 int color; 3014 char *name = strchr(buf, ';'); 3015 3016 if (name == NULL) 3017 break; 3018 *name = '\0'; 3019 name++; 3020 color = atoi(buf); 3021 if (color < 0 || color >= last) 3022 break; /* quit on any error */ 3023 buf = strchr(name, ';'); 3024 if (buf) { 3025 *buf = '\0'; 3026 buf++; 3027 } 3028 if (!strcmp(name, "?")) { 3029 if (ReportAnsiColorRequest(xw, opcode, color + first, final)) 3030 ++queried; 3031 } else { 3032 code = ChangeOneAnsiColor(xw, color + first, name); 3033 if (code < 0) { 3034 /* stop on any error */ 3035 break; 3036 } else if (code > 0) { 3037 repaint = True; 3038 } 3039 /* FIXME: free old color somehow? We aren't for the other color 3040 * change style (dynamic colors). 3041 */ 3042 } 3043 } 3044 if (queried) 3045 unparse_end(xw); 3046 3047 return (repaint); 3048} 3049 3050static Bool 3051ResetOneAnsiColor(XtermWidget xw, int color, int start) 3052{ 3053 Bool repaint = False; 3054 int last = MAXCOLORS - start; 3055 3056 if (color >= 0 && color < last) { 3057 ColorRes *res = &(TScreenOf(xw)->Acolors[color + start]); 3058 3059 if (res->mode) { 3060 /* a color has been allocated for this slot - test further... */ 3061 if (ChangeOneAnsiColor(xw, color + start, res->resource) > 0) { 3062 repaint = True; 3063 } 3064 } 3065 } 3066 return repaint; 3067} 3068 3069int 3070ResetAnsiColorRequest(XtermWidget xw, char *buf, int start) 3071{ 3072 int repaint = 0; 3073 int color; 3074 3075 TRACE(("ResetAnsiColorRequest(%s)\n", buf)); 3076 if (*buf != '\0') { 3077 /* reset specific colors */ 3078 while (!IsEmpty(buf)) { 3079 char *next; 3080 3081 color = (int) (strtol) (buf, &next, 10); 3082 if (!PartS2L(buf, next) || (color < 0)) 3083 break; /* no number at all */ 3084 if (next != 0) { 3085 if (strchr(";", *next) == 0) 3086 break; /* unexpected delimiter */ 3087 ++next; 3088 } 3089 3090 if (ResetOneAnsiColor(xw, color, start)) { 3091 ++repaint; 3092 } 3093 buf = next; 3094 } 3095 } else { 3096 TRACE(("...resetting all %d colors\n", MAXCOLORS)); 3097 for (color = 0; color < MAXCOLORS; ++color) { 3098 if (ResetOneAnsiColor(xw, color, start)) { 3099 ++repaint; 3100 } 3101 } 3102 } 3103 TRACE(("...ResetAnsiColorRequest ->%d\n", repaint)); 3104 return repaint; 3105} 3106#else 3107#define allocateClosestRGB(xw, def) 0 3108#endif /* OPT_ISO_COLORS */ 3109 3110Boolean 3111allocateBestRGB(XtermWidget xw, XColor *def) 3112{ 3113 (void) xw; 3114 (void) def; 3115 return AllocOneColor(xw, def) || allocateClosestRGB(xw, def); 3116} 3117 3118static Boolean 3119xtermAllocColor(XtermWidget xw, XColor *def, const char *spec) 3120{ 3121 Boolean result = False; 3122 TScreen *screen = TScreenOf(xw); 3123 Colormap cmap = xw->core.colormap; 3124 size_t have = strlen(spec); 3125 3126 if (have == 0 || have > MAX_U_STRING) { 3127 if (resource.reportColors) { 3128 printf("color (ignored, length %lu)\n", (unsigned long) have); 3129 } 3130 } else if (XParseColor(screen->display, cmap, spec, def)) { 3131 XColor save_def = *def; 3132 if (resource.reportColors) { 3133 printf("color %04x/%04x/%04x = \"%s\"\n", 3134 def->red, def->green, def->blue, 3135 spec); 3136 } 3137 if (allocateBestRGB(xw, def)) { 3138 if (resource.reportColors) { 3139 if (def->red != save_def.red || 3140 def->green != save_def.green || 3141 def->blue != save_def.blue) { 3142 printf("color %04x/%04x/%04x ~ \"%s\"\n", 3143 def->red, def->green, def->blue, 3144 spec); 3145 } 3146 } 3147 TRACE(("xtermAllocColor -> %x/%x/%x\n", 3148 def->red, def->green, def->blue)); 3149 result = True; 3150 } 3151 } 3152 return result; 3153} 3154 3155/* 3156 * This provides an approximation (the closest color from xterm's palette) 3157 * rather than the "exact" color (whatever the display could provide, actually) 3158 * because of the context in which it is used. 3159 */ 3160#define ColorDiff(given,cache) ((long) ((cache) >> 8) - (long) (given)) 3161int 3162xtermClosestColor(XtermWidget xw, int find_red, int find_green, int find_blue) 3163{ 3164 int result = -1; 3165#if OPT_ISO_COLORS 3166 int n; 3167 int best_index = -1; 3168 unsigned long best_value = 0; 3169 unsigned long this_value; 3170 long diff_red, diff_green, diff_blue; 3171 3172 TRACE(("xtermClosestColor(%x/%x/%x)\n", find_red, find_green, find_blue)); 3173 3174 for (n = NUM_ANSI_COLORS - 1; n >= 0; --n) { 3175 ColorRes *res = &(TScreenOf(xw)->Acolors[n]); 3176 3177 /* ensure that we have a value for each of the colors */ 3178 if (!res->mode) { 3179 (void) AllocateAnsiColor(xw, res, res->resource); 3180 } 3181 3182 /* find the closest match */ 3183 if (res->mode == True) { 3184 TRACE2(("...lookup %lx -> %x/%x/%x\n", 3185 res->value, res->red, res->green, res->blue)); 3186 diff_red = ColorDiff(find_red, res->red); 3187 diff_green = ColorDiff(find_green, res->green); 3188 diff_blue = ColorDiff(find_blue, res->blue); 3189 this_value = (unsigned long) ((diff_red * diff_red) 3190 + (diff_green * diff_green) 3191 + (diff_blue * diff_blue)); 3192 if (best_index < 0 || this_value < best_value) { 3193 best_index = n; 3194 best_value = this_value; 3195 } 3196 } 3197 } 3198 TRACE(("...best match at %d with diff %lx\n", best_index, best_value)); 3199 result = best_index; 3200 3201#else 3202 (void) xw; 3203 (void) find_red; 3204 (void) find_green; 3205 (void) find_blue; 3206#endif 3207 return result; 3208} 3209 3210#if OPT_DIRECT_COLOR 3211int 3212getDirectColor(XtermWidget xw, int red, int green, int blue) 3213{ 3214 Pixel result = 0; 3215 3216#define getRGB(name,shift) \ 3217 do { \ 3218 Pixel value = (Pixel) name & 0xff; \ 3219 if (xw->rgb_widths[shift] < 8) { \ 3220 value >>= (int) (8 - xw->rgb_widths[shift]); \ 3221 } \ 3222 value <<= xw->rgb_shifts[shift]; \ 3223 value &= xw->visInfo->name ##_mask; \ 3224 result |= value; \ 3225 } while (0) 3226 3227 getRGB(red, 0); 3228 getRGB(green, 1); 3229 getRGB(blue, 2); 3230 3231#undef getRGB 3232 3233 return (int) result; 3234} 3235 3236static void 3237formatDirectColor(char *target, XtermWidget xw, unsigned value) 3238{ 3239 Pixel result[3]; 3240 3241#define getRGB(name, shift) \ 3242 do { \ 3243 result[shift] = value & xw->visInfo->name ## _mask; \ 3244 result[shift] >>= xw->rgb_shifts[shift]; \ 3245 if (xw->rgb_widths[shift] < 8) \ 3246 result[shift] <<= (int) (8 - xw->rgb_widths[shift]); \ 3247 } while(0) 3248 3249 getRGB(red, 0); 3250 getRGB(green, 1); 3251 getRGB(blue, 2); 3252 3253#undef getRGB 3254 3255 sprintf(target, "%lu:%lu:%lu", result[0], result[1], result[2]); 3256} 3257#endif /* OPT_DIRECT_COLOR */ 3258 3259#define fg2SGR(n) \ 3260 (n) >= 8 ? 9 : 3, \ 3261 (n) >= 8 ? (n) - 8 : (n) 3262#define bg2SGR(n) \ 3263 (n) >= 8 ? 10 : 4, \ 3264 (n) >= 8 ? (n) - 8 : (n) 3265 3266#define EndOf(s) (s) + strlen(s) 3267 3268char * 3269xtermFormatSGR(XtermWidget xw, char *target, unsigned attr, int fg, int bg) 3270{ 3271 TScreen *screen = TScreenOf(xw); 3272 char *msg = target; 3273 3274 strcpy(target, "0"); 3275 if (attr & BOLD) 3276 strcat(msg, ";1"); 3277 if (attr & UNDERLINE) 3278 strcat(msg, ";4"); 3279 if (attr & BLINK) 3280 strcat(msg, ";5"); 3281 if (attr & INVERSE) 3282 strcat(msg, ";7"); 3283 if (attr & INVISIBLE) 3284 strcat(msg, ";8"); 3285#if OPT_WIDE_ATTRS 3286 if (attr & ATR_FAINT) 3287 strcat(msg, ";2"); 3288 if (attr & ATR_ITALIC) 3289 strcat(msg, ";3"); 3290 if (attr & ATR_STRIKEOUT) 3291 strcat(msg, ";9"); 3292 if (attr & ATR_DBL_UNDER) 3293 strcat(msg, ";21"); 3294#endif 3295#if OPT_256_COLORS || OPT_88_COLORS 3296 if_OPT_ISO_COLORS(screen, { 3297 if (attr & FG_COLOR) { 3298 if_OPT_DIRECT_COLOR2_else(screen, hasDirectFG(attr), { 3299 strcat(msg, ";38:2::"); 3300 formatDirectColor(EndOf(msg), xw, (unsigned) fg); 3301 }) if (fg >= 16) { 3302 sprintf(EndOf(msg), ";38:5:%d", fg); 3303 } else { 3304 sprintf(EndOf(msg), ";%d%d", fg2SGR(fg)); 3305 } 3306 } 3307 if (attr & BG_COLOR) { 3308 if_OPT_DIRECT_COLOR2_else(screen, hasDirectBG(attr), { 3309 strcat(msg, ";48:2::"); 3310 formatDirectColor(EndOf(msg), xw, (unsigned) bg); 3311 }) if (bg >= 16) { 3312 sprintf(EndOf(msg), ";48:5:%d", bg); 3313 } else { 3314 sprintf(EndOf(msg), ";%d%d", bg2SGR(bg)); 3315 } 3316 } 3317 }); 3318#elif OPT_ISO_COLORS 3319 if_OPT_ISO_COLORS(screen, { 3320 if (attr & FG_COLOR) { 3321 sprintf(EndOf(msg), ";%d%d", fg2SGR(fg)); 3322 } 3323 if (attr & BG_COLOR) { 3324 sprintf(EndOf(msg), ";%d%d", bg2SGR(bg)); 3325 } 3326 }); 3327#else 3328 (void) screen; 3329 (void) fg; 3330 (void) bg; 3331#endif 3332 return target; 3333} 3334 3335#if OPT_PASTE64 3336static void 3337ManipulateSelectionData(XtermWidget xw, TScreen *screen, char *buf, int final) 3338{ 3339#define PDATA(a,b) { a, #b } 3340 static struct { 3341 char given; 3342 String result; 3343 } table[] = { 3344 PDATA('s', SELECT), 3345 PDATA('p', PRIMARY), 3346 PDATA('q', SECONDARY), 3347 PDATA('c', CLIPBOARD), 3348 PDATA('0', CUT_BUFFER0), 3349 PDATA('1', CUT_BUFFER1), 3350 PDATA('2', CUT_BUFFER2), 3351 PDATA('3', CUT_BUFFER3), 3352 PDATA('4', CUT_BUFFER4), 3353 PDATA('5', CUT_BUFFER5), 3354 PDATA('6', CUT_BUFFER6), 3355 PDATA('7', CUT_BUFFER7), 3356 }; 3357 3358 const char *base = buf; 3359 Cardinal j, n = 0; 3360 3361 TRACE(("Manipulate selection data\n")); 3362 3363 while (*buf != ';' && *buf != '\0') { 3364 ++buf; 3365 } 3366 3367 if (*buf == ';') { 3368 char *used; 3369 3370 *buf++ = '\0'; 3371 3372 if (*base == '\0') 3373 base = "s0"; 3374 3375 if ((used = x_strdup(base)) != 0) { 3376 String *select_args; 3377 3378 if ((select_args = TypeCallocN(String, 2 + strlen(base))) != 0) { 3379 while (*base != '\0') { 3380 for (j = 0; j < XtNumber(table); ++j) { 3381 if (*base == table[j].given) { 3382 used[n] = *base; 3383 select_args[n++] = table[j].result; 3384 TRACE(("atom[%d] %s\n", n, table[j].result)); 3385 break; 3386 } 3387 } 3388 ++base; 3389 } 3390 used[n] = 0; 3391 3392 if (!strcmp(buf, "?")) { 3393 if (AllowWindowOps(xw, ewGetSelection)) { 3394 TRACE(("Getting selection\n")); 3395 unparseputc1(xw, ANSI_OSC); 3396 unparseputs(xw, "52"); 3397 unparseputc(xw, ';'); 3398 3399 unparseputs(xw, used); 3400 unparseputc(xw, ';'); 3401 3402 /* Tell xtermGetSelection data is base64 encoded */ 3403 screen->base64_paste = n; 3404 screen->base64_final = final; 3405 3406 screen->selection_time = 3407 XtLastTimestampProcessed(TScreenOf(xw)->display); 3408 3409 /* terminator will be written in this call */ 3410 xtermGetSelection((Widget) xw, 3411 screen->selection_time, 3412 select_args, n, 3413 NULL); 3414 /* 3415 * select_args is used via SelectionReceived, cannot 3416 * free it here. 3417 */ 3418 } else { 3419 free(select_args); 3420 } 3421 } else { 3422 if (AllowWindowOps(xw, ewSetSelection)) { 3423 char *old = buf; 3424 3425 TRACE(("Setting selection(%s) with %s\n", used, buf)); 3426 screen->selection_time = 3427 XtLastTimestampProcessed(TScreenOf(xw)->display); 3428 3429 for (j = 0; j < n; ++j) { 3430 buf = old; 3431 ClearSelectionBuffer(screen, select_args[j]); 3432 while (*buf != '\0') { 3433 AppendToSelectionBuffer(screen, 3434 CharOf(*buf++), 3435 select_args[j]); 3436 } 3437 } 3438 CompleteSelection(xw, select_args, n); 3439 } 3440 free(select_args); 3441 } 3442 } 3443 free(used); 3444 } 3445 } 3446} 3447#endif /* OPT_PASTE64 */ 3448 3449/***====================================================================***/ 3450 3451#define IsSetUtf8Title(xw) (IsTitleMode(xw, tmSetUtf8) \ 3452 || (xw->screen.utf8_title) \ 3453 || (xw->screen.c1_printable)) 3454 3455static Bool 3456xtermIsPrintable(XtermWidget xw, Char **bufp, Char *last) 3457{ 3458 TScreen *screen = TScreenOf(xw); 3459 Bool result = False; 3460 Char *cp = *bufp; 3461 Char *next = cp; 3462 3463 (void) screen; 3464 (void) last; 3465 3466#if OPT_WIDE_CHARS 3467 if (xtermEnvUTF8() && IsSetUtf8Title(xw)) { 3468 PtyData data; 3469 3470 if (decodeUtf8(screen, fakePtyData(&data, cp, last))) { 3471 if (data.utf_data != UCS_REPL 3472 && (data.utf_data >= 128 || 3473 ansi_table[data.utf_data] == CASE_PRINT)) { 3474 next += (data.utf_size - 1); 3475 result = True; 3476 } else { 3477 result = False; 3478 } 3479 } else { 3480 result = False; 3481 } 3482 } else 3483#endif 3484#if OPT_C1_PRINT 3485 if (screen->c1_printable 3486 && (*cp >= 128 && *cp < 160)) { 3487 result = True; 3488 } else 3489#endif 3490 if (ansi_table[*cp] == CASE_PRINT) { 3491 result = True; 3492 } 3493 *bufp = next; 3494 return result; 3495} 3496 3497/***====================================================================***/ 3498 3499/* 3500 * Enum corresponding to the actual OSC codes rather than the internal 3501 * array indices. Compare with TermColors. 3502 */ 3503typedef enum { 3504 OSC_TEXT_FG = 10 3505 ,OSC_TEXT_BG 3506 ,OSC_TEXT_CURSOR 3507 ,OSC_MOUSE_FG 3508 ,OSC_MOUSE_BG 3509#if OPT_TEK4014 3510 ,OSC_TEK_FG = 15 3511 ,OSC_TEK_BG 3512#endif 3513#if OPT_HIGHLIGHT_COLOR 3514 ,OSC_HIGHLIGHT_BG = 17 3515#endif 3516#if OPT_TEK4014 3517 ,OSC_TEK_CURSOR = 18 3518#endif 3519#if OPT_HIGHLIGHT_COLOR 3520 ,OSC_HIGHLIGHT_FG = 19 3521#endif 3522 ,OSC_NCOLORS 3523} OscTextColors; 3524 3525/* 3526 * Map codes to OSC controls that can reset colors. 3527 */ 3528#define OSC_RESET 100 3529#define OSC_Reset(code) (code) + OSC_RESET 3530 3531static Bool 3532GetOldColors(XtermWidget xw) 3533{ 3534 if (xw->work.oldColors == NULL) { 3535 int i; 3536 3537 xw->work.oldColors = TypeXtMalloc(ScrnColors); 3538 if (xw->work.oldColors == NULL) { 3539 xtermWarning("allocation failure in GetOldColors\n"); 3540 return (False); 3541 } 3542 xw->work.oldColors->which = 0; 3543 for (i = 0; i < NCOLORS; i++) { 3544 xw->work.oldColors->colors[i] = 0; 3545 xw->work.oldColors->names[i] = NULL; 3546 } 3547 GetColors(xw, xw->work.oldColors); 3548 } 3549 return (True); 3550} 3551 3552static int 3553oppositeColor(XtermWidget xw, int n) 3554{ 3555 Boolean reversed = (xw->misc.re_verse); 3556 3557 switch (n) { 3558 case TEXT_FG: 3559 n = reversed ? TEXT_FG : TEXT_BG; 3560 break; 3561 case TEXT_BG: 3562 n = reversed ? TEXT_BG : TEXT_FG; 3563 break; 3564 case MOUSE_FG: 3565 n = MOUSE_BG; 3566 break; 3567 case MOUSE_BG: 3568 n = MOUSE_FG; 3569 break; 3570#if OPT_TEK4014 3571 case TEK_FG: 3572 n = reversed ? TEK_FG : TEK_BG; 3573 break; 3574 case TEK_BG: 3575 n = reversed ? TEK_BG : TEK_FG; 3576 break; 3577#endif 3578#if OPT_HIGHLIGHT_COLOR 3579 case HIGHLIGHT_FG: 3580 n = HIGHLIGHT_BG; 3581 break; 3582 case HIGHLIGHT_BG: 3583 n = HIGHLIGHT_FG; 3584 break; 3585#endif 3586 default: 3587 break; 3588 } 3589 return n; 3590} 3591 3592static Bool 3593ReportColorRequest(XtermWidget xw, int ndx, int final) 3594{ 3595 Bool result = False; 3596 3597 if (AllowColorOps(xw, ecGetColor)) { 3598 XColor color; 3599 char buffer[80]; 3600 3601 /* 3602 * ChangeColorsRequest() has "always" chosen the opposite color when 3603 * reverse-video is set. Report this as the original color index, but 3604 * reporting the opposite color which would be used. 3605 */ 3606 int i = (xw->misc.re_verse) ? oppositeColor(xw, ndx) : ndx; 3607 3608 GetOldColors(xw); 3609 color.pixel = xw->work.oldColors->colors[ndx]; 3610 (void) QueryOneColor(xw, &color); 3611 sprintf(buffer, "%d;rgb:%04x/%04x/%04x", i + 10, 3612 color.red, 3613 color.green, 3614 color.blue); 3615 TRACE(("ReportColorRequest #%d: 0x%06lx as %s\n", 3616 ndx, xw->work.oldColors->colors[ndx], buffer)); 3617 unparseputc1(xw, ANSI_OSC); 3618 unparseputs(xw, buffer); 3619 unparseputc1(xw, final); 3620 result = True; 3621 } 3622 return result; 3623} 3624 3625static Bool 3626UpdateOldColors(XtermWidget xw, ScrnColors * pNew) 3627{ 3628 int i; 3629 3630 /* if we were going to free old colors, this would be the place to 3631 * do it. I've decided not to (for now), because it seems likely 3632 * that we'd have a small set of colors we use over and over, and that 3633 * we could save some overhead this way. The only case in which this 3634 * (clearly) fails is if someone is trying a boatload of colors, in 3635 * which case they can restart xterm 3636 */ 3637 for (i = 0; i < NCOLORS; i++) { 3638 if (COLOR_DEFINED(pNew, i)) { 3639 if (xw->work.oldColors->names[i] != NULL) { 3640 XtFree(xw->work.oldColors->names[i]); 3641 xw->work.oldColors->names[i] = NULL; 3642 } 3643 if (pNew->names[i]) { 3644 xw->work.oldColors->names[i] = pNew->names[i]; 3645 } 3646 xw->work.oldColors->colors[i] = pNew->colors[i]; 3647 } 3648 } 3649 return (True); 3650} 3651 3652/* 3653 * OSC codes are constant, but the indices for the color arrays depend on how 3654 * xterm is compiled. 3655 */ 3656static int 3657OscToColorIndex(OscTextColors mode) 3658{ 3659 int result = 0; 3660 3661#define CASE(name) case OSC_##name: result = name; break 3662 switch (mode) { 3663 CASE(TEXT_FG); 3664 CASE(TEXT_BG); 3665 CASE(TEXT_CURSOR); 3666 CASE(MOUSE_FG); 3667 CASE(MOUSE_BG); 3668#if OPT_TEK4014 3669 CASE(TEK_FG); 3670 CASE(TEK_BG); 3671#endif 3672#if OPT_HIGHLIGHT_COLOR 3673 CASE(HIGHLIGHT_BG); 3674 CASE(HIGHLIGHT_FG); 3675#endif 3676#if OPT_TEK4014 3677 CASE(TEK_CURSOR); 3678#endif 3679 case OSC_NCOLORS: 3680 break; 3681 } 3682 return result; 3683} 3684 3685static Bool 3686ChangeColorsRequest(XtermWidget xw, 3687 int start, 3688 char *names, 3689 int final) 3690{ 3691 Bool result = False; 3692 ScrnColors newColors; 3693 3694 TRACE(("ChangeColorsRequest start=%d, names='%s'\n", start, names)); 3695 3696 if (GetOldColors(xw)) { 3697 int i; 3698 int queried = 0; 3699 3700 newColors.which = 0; 3701 for (i = 0; i < NCOLORS; i++) { 3702 newColors.names[i] = NULL; 3703 } 3704 for (i = start; i < OSC_NCOLORS; i++) { 3705 int ndx = OscToColorIndex((OscTextColors) i); 3706 if (xw->misc.re_verse) 3707 ndx = oppositeColor(xw, ndx); 3708 3709 if (IsEmpty(names)) { 3710 newColors.names[ndx] = NULL; 3711 } else { 3712 char *thisName = ((names[0] == ';') ? NULL : names); 3713 3714 names = strchr(names, ';'); 3715 if (names != NULL) { 3716 *names++ = '\0'; 3717 } 3718 if (thisName != 0) { 3719 if (!strcmp(thisName, "?")) { 3720 if (ReportColorRequest(xw, ndx, final)) 3721 ++queried; 3722 } else if (!xw->work.oldColors->names[ndx] 3723 || strcmp(thisName, xw->work.oldColors->names[ndx])) { 3724 AllocateTermColor(xw, &newColors, ndx, thisName, False); 3725 } 3726 } 3727 } 3728 } 3729 3730 if (newColors.which != 0) { 3731 ChangeColors(xw, &newColors); 3732 UpdateOldColors(xw, &newColors); 3733 } else if (queried) { 3734 unparse_end(xw); 3735 } 3736 result = True; 3737 } 3738 return result; 3739} 3740 3741static Bool 3742ResetColorsRequest(XtermWidget xw, 3743 int code) 3744{ 3745 Bool result = False; 3746 3747 (void) xw; 3748 (void) code; 3749 3750 TRACE(("ResetColorsRequest code=%d\n", code)); 3751 if (GetOldColors(xw)) { 3752 ScrnColors newColors; 3753 const char *thisName; 3754 int ndx = OscToColorIndex((OscTextColors) (code - OSC_RESET)); 3755 3756 if (xw->misc.re_verse) 3757 ndx = oppositeColor(xw, ndx); 3758 3759 thisName = xw->screen.Tcolors[ndx].resource; 3760 3761 newColors.which = 0; 3762 newColors.names[ndx] = NULL; 3763 3764 if (thisName != 0 3765 && xw->work.oldColors->names[ndx] != 0 3766 && strcmp(thisName, xw->work.oldColors->names[ndx])) { 3767 AllocateTermColor(xw, &newColors, ndx, thisName, False); 3768 3769 if (newColors.which != 0) { 3770 ChangeColors(xw, &newColors); 3771 UpdateOldColors(xw, &newColors); 3772 } 3773 } 3774 result = True; 3775 } 3776 return result; 3777} 3778 3779#if OPT_SHIFT_FONTS 3780/* 3781 * Initially, 'source' points to '#' or '?'. 3782 * 3783 * Look for an optional sign and optional number. If those are found, lookup 3784 * the corresponding menu font entry. 3785 */ 3786static int 3787ParseShiftedFont(XtermWidget xw, String source, String *target) 3788{ 3789 TScreen *screen = TScreenOf(xw); 3790 int num = screen->menu_font_number; 3791 int rel = 0; 3792 3793 if (*++source == '+') { 3794 rel = 1; 3795 source++; 3796 } else if (*source == '-') { 3797 rel = -1; 3798 source++; 3799 } 3800 3801 if (isdigit(CharOf(*source))) { 3802 int val = atoi(source); 3803 if (rel > 0) 3804 rel = val; 3805 else if (rel < 0) 3806 rel = -val; 3807 else 3808 num = val; 3809 } 3810 3811 if (rel != 0) { 3812 num = lookupRelativeFontSize(xw, 3813 screen->menu_font_number, rel); 3814 3815 } 3816 TRACE(("ParseShiftedFont(%s) ->%d (%s)\n", *target, num, source)); 3817 *target = source; 3818 return num; 3819} 3820 3821static void 3822QueryFontRequest(XtermWidget xw, String buf, int final) 3823{ 3824 if (AllowFontOps(xw, efGetFont)) { 3825 TScreen *screen = TScreenOf(xw); 3826 Bool success = True; 3827 int num; 3828 String base = buf + 1; 3829 const char *name = 0; 3830 3831 num = ParseShiftedFont(xw, buf, &buf); 3832 if (num < 0 3833 || num > fontMenu_lastBuiltin) { 3834 Bell(xw, XkbBI_MinorError, 0); 3835 success = False; 3836 } else { 3837#if OPT_RENDERFONT 3838 if (UsingRenderFont(xw)) { 3839 name = getFaceName(xw, False); 3840 } else 3841#endif 3842 if ((name = screen->MenuFontName(num)) == 0) { 3843 success = False; 3844 } 3845 } 3846 3847 unparseputc1(xw, ANSI_OSC); 3848 unparseputs(xw, "50"); 3849 3850 if (success) { 3851 unparseputc(xw, ';'); 3852 if (buf >= base) { 3853 /* identify the font-entry, unless it is the current one */ 3854 if (*buf != '\0') { 3855 char temp[10]; 3856 3857 unparseputc(xw, '#'); 3858 sprintf(temp, "%d", num); 3859 unparseputs(xw, temp); 3860 if (*name != '\0') 3861 unparseputc(xw, ' '); 3862 } 3863 } 3864 unparseputs(xw, name); 3865 } 3866 3867 unparseputc1(xw, final); 3868 unparse_end(xw); 3869 } 3870} 3871 3872static void 3873ChangeFontRequest(XtermWidget xw, String buf) 3874{ 3875 if (AllowFontOps(xw, efSetFont)) { 3876 TScreen *screen = TScreenOf(xw); 3877 Bool success = True; 3878 int num; 3879 VTFontNames fonts; 3880 char *name; 3881 3882 /* 3883 * If the font specification is a "#", followed by an optional sign and 3884 * optional number, lookup the corresponding menu font entry. 3885 * 3886 * Further, if the "#", etc., is followed by a font name, use that 3887 * to load the font entry. 3888 */ 3889 if (*buf == '#') { 3890 num = ParseShiftedFont(xw, buf, &buf); 3891 3892 if (num < 0 3893 || num > fontMenu_lastBuiltin) { 3894 Bell(xw, XkbBI_MinorError, 0); 3895 success = False; 3896 } else { 3897 /* 3898 * Skip past the optional number, and any whitespace to look 3899 * for a font specification within the control. 3900 */ 3901 while (isdigit(CharOf(*buf))) { 3902 ++buf; 3903 } 3904 while (isspace(CharOf(*buf))) { 3905 ++buf; 3906 } 3907#if OPT_RENDERFONT 3908 if (UsingRenderFont(xw)) { 3909 /* EMPTY */ 3910 /* there is only one font entry to load */ 3911 ; 3912 } else 3913#endif 3914 { 3915 /* 3916 * Normally there is no font specified in the control. 3917 * But if there is, simply overwrite the font entry. 3918 */ 3919 if (*buf == '\0') { 3920 if ((buf = screen->MenuFontName(num)) == 0) { 3921 success = False; 3922 } 3923 } 3924 } 3925 } 3926 } else { 3927 num = screen->menu_font_number; 3928 } 3929 name = x_strtrim(buf); 3930 if (screen->EscapeFontName()) { 3931 FREE_STRING(screen->EscapeFontName()); 3932 screen->EscapeFontName() = 0; 3933 } 3934 if (success && !IsEmpty(name)) { 3935#if OPT_RENDERFONT 3936 if (UsingRenderFont(xw)) { 3937 setFaceName(xw, name); 3938 xtermUpdateFontInfo(xw, True); 3939 } else 3940#endif 3941 { 3942 memset(&fonts, 0, sizeof(fonts)); 3943 fonts.f_n = name; 3944 SetVTFont(xw, num, True, &fonts); 3945 if (num == screen->menu_font_number && 3946 num != fontMenu_fontescape) { 3947 screen->EscapeFontName() = x_strdup(name); 3948 } 3949 } 3950 } else { 3951 Bell(xw, XkbBI_MinorError, 0); 3952 } 3953 update_font_escape(); 3954 free(name); 3955 } 3956} 3957#endif /* OPT_SHIFT_FONTS */ 3958 3959/***====================================================================***/ 3960 3961void 3962do_osc(XtermWidget xw, Char *oscbuf, size_t len, int final) 3963{ 3964 TScreen *screen = TScreenOf(xw); 3965 int mode; 3966 Char *cp; 3967 int state = 0; 3968 char *buf = 0; 3969 char temp[2]; 3970#if OPT_ISO_COLORS 3971 int ansi_colors = 0; 3972#endif 3973 Bool need_data = True; 3974 Bool optional_data = False; 3975 3976 TRACE(("do_osc %s\n", oscbuf)); 3977 3978 (void) screen; 3979 3980 /* 3981 * Lines should be of the form <OSC> number ; string <ST>, however 3982 * older xterms can accept <BEL> as a final character. We will respond 3983 * with the same final character as the application sends to make this 3984 * work better with shell scripts, which may have trouble reading an 3985 * <ESC><backslash>, which is the 7-bit equivalent to <ST>. 3986 */ 3987 mode = 0; 3988 for (cp = oscbuf; *cp != '\0'; cp++) { 3989 switch (state) { 3990 case 0: 3991 if (isdigit(*cp)) { 3992 mode = 10 * mode + (*cp - '0'); 3993 if (mode > 65535) { 3994 TRACE(("do_osc found unknown mode %d\n", mode)); 3995 return; 3996 } 3997 break; 3998 } else { 3999 switch (*cp) { 4000 case 'I': 4001 xtermLoadIcon(xw, (char *) ++cp); 4002 return; 4003 case 'l': 4004 ChangeTitle(xw, (char *) ++cp); 4005 return; 4006 case 'L': 4007 ChangeIconName(xw, (char *) ++cp); 4008 return; 4009 } 4010 } 4011 /* FALLTHRU */ 4012 case 1: 4013 if (*cp != ';') { 4014 TRACE(("do_osc did not find semicolon offset %d\n", 4015 (int) (cp - oscbuf))); 4016 return; 4017 } 4018 state = 2; 4019 break; 4020 case 2: 4021 buf = (char *) cp; 4022 state = 3; 4023 /* FALLTHRU */ 4024 default: 4025 if (!xtermIsPrintable(xw, &cp, oscbuf + len)) { 4026 switch (mode) { 4027 case 0: 4028 case 1: 4029 case 2: 4030 break; 4031 default: 4032 TRACE(("do_osc found nonprinting char %02X offset %d\n", 4033 CharOf(*cp), 4034 (int) (cp - oscbuf))); 4035 return; 4036 } 4037 } 4038 } 4039 } 4040 4041 /* 4042 * Check if the palette changed and there are no more immediate changes 4043 * that could be deferred to the next repaint. 4044 */ 4045 if (xw->work.palette_changed) { 4046 switch (mode) { 4047 case 03: /* change X property */ 4048 case 30: /* Konsole (unused) */ 4049 case 31: /* Konsole (unused) */ 4050 case 50: /* font operations */ 4051 case 51: /* Emacs (unused) */ 4052#if OPT_PASTE64 4053 case 52: /* selection data */ 4054#endif 4055 TRACE(("forced repaint after palette changed\n")); 4056 xw->work.palette_changed = False; 4057 xtermRepaint(xw); 4058 break; 4059 default: 4060 xtermNeedSwap(xw, 1); 4061 break; 4062 } 4063 } 4064 4065 /* 4066 * Most OSC controls other than resets require data. Handle the others as 4067 * a special case. 4068 */ 4069 switch (mode) { 4070 case 50: 4071#if OPT_ISO_COLORS 4072 case OSC_Reset(4): 4073 case OSC_Reset(5): 4074 need_data = False; 4075 optional_data = True; 4076 break; 4077 case OSC_Reset(OSC_TEXT_FG): 4078 case OSC_Reset(OSC_TEXT_BG): 4079 case OSC_Reset(OSC_TEXT_CURSOR): 4080 case OSC_Reset(OSC_MOUSE_FG): 4081 case OSC_Reset(OSC_MOUSE_BG): 4082#if OPT_HIGHLIGHT_COLOR 4083 case OSC_Reset(OSC_HIGHLIGHT_BG): 4084 case OSC_Reset(OSC_HIGHLIGHT_FG): 4085#endif 4086#if OPT_TEK4014 4087 case OSC_Reset(OSC_TEK_FG): 4088 case OSC_Reset(OSC_TEK_BG): 4089 case OSC_Reset(OSC_TEK_CURSOR): 4090#endif 4091 need_data = False; 4092 break; 4093#endif 4094 default: 4095 break; 4096 } 4097 4098 /* 4099 * Check if we have data when we want, and not when we do not want it. 4100 * Either way, that is a malformed control sequence, and will be ignored. 4101 */ 4102 if (IsEmpty(buf)) { 4103 if (need_data) { 4104 TRACE(("do_osc found no data\n")); 4105 return; 4106 } 4107 temp[0] = '\0'; 4108 buf = temp; 4109 } else if (!need_data && !optional_data) { 4110 TRACE(("do_osc found unwanted data\n")); 4111 return; 4112 } 4113 4114 switch (mode) { 4115 case 0: /* new icon name and title */ 4116 ChangeIconName(xw, buf); 4117 ChangeTitle(xw, buf); 4118 break; 4119 4120 case 1: /* new icon name only */ 4121 ChangeIconName(xw, buf); 4122 break; 4123 4124 case 2: /* new title only */ 4125 ChangeTitle(xw, buf); 4126 break; 4127 4128#ifdef notdef 4129 case 3: /* change X property */ 4130 if (AllowWindowOps(xw, ewSetXprop)) 4131 ChangeXprop(buf); 4132 break; 4133#endif 4134#if OPT_ISO_COLORS 4135 case 5: 4136 ansi_colors = NUM_ANSI_COLORS; 4137 /* FALLTHRU */ 4138 case 4: 4139 if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final)) 4140 xw->work.palette_changed = True; 4141 break; 4142 case 6: 4143 /* FALLTHRU */ 4144 case OSC_Reset(6): 4145 TRACE(("parse colorXXMode:%s\n", buf)); 4146 while (*buf != '\0') { 4147 long which = 0; 4148 long value = 0; 4149 char *next; 4150 if (*buf == ';') { 4151 ++buf; 4152 } else { 4153 which = strtol(buf, &next, 10); 4154 if (!PartS2L(buf, next) || (which < 0)) 4155 break; 4156 buf = next; 4157 if (*buf == ';') 4158 ++buf; 4159 } 4160 if (*buf == ';') { 4161 ++buf; 4162 } else { 4163 value = strtol(buf, &next, 10); 4164 if (!PartS2L(buf, next) || (value < 0)) 4165 break; 4166 buf = next; 4167 if (*buf == ';') 4168 ++buf; 4169 } 4170 TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value)); 4171 switch (which) { 4172 case 0: 4173 screen->colorBDMode = (value != 0); 4174 break; 4175 case 1: 4176 screen->colorULMode = (value != 0); 4177 break; 4178 case 2: 4179 screen->colorBLMode = (value != 0); 4180 break; 4181 case 3: 4182 screen->colorRVMode = (value != 0); 4183 break; 4184#if OPT_WIDE_ATTRS 4185 case 4: 4186 screen->colorITMode = (value != 0); 4187 break; 4188#endif 4189 default: 4190 TRACE(("...unknown colorXXMode\n")); 4191 break; 4192 } 4193 } 4194 break; 4195 case OSC_Reset(5): 4196 ansi_colors = NUM_ANSI_COLORS; 4197 /* FALLTHRU */ 4198 case OSC_Reset(4): 4199 if (ResetAnsiColorRequest(xw, buf, ansi_colors)) 4200 xw->work.palette_changed = True; 4201 break; 4202#endif 4203 case OSC_TEXT_FG: 4204 case OSC_TEXT_BG: 4205 case OSC_TEXT_CURSOR: 4206 case OSC_MOUSE_FG: 4207 case OSC_MOUSE_BG: 4208#if OPT_HIGHLIGHT_COLOR 4209 case OSC_HIGHLIGHT_BG: 4210 case OSC_HIGHLIGHT_FG: 4211#endif 4212#if OPT_TEK4014 4213 case OSC_TEK_FG: 4214 case OSC_TEK_BG: 4215 case OSC_TEK_CURSOR: 4216#endif 4217 if (xw->misc.dynamicColors) { 4218 ChangeColorsRequest(xw, mode, buf, final); 4219 } 4220 break; 4221 case OSC_Reset(OSC_TEXT_FG): 4222 case OSC_Reset(OSC_TEXT_BG): 4223 case OSC_Reset(OSC_TEXT_CURSOR): 4224 case OSC_Reset(OSC_MOUSE_FG): 4225 case OSC_Reset(OSC_MOUSE_BG): 4226#if OPT_HIGHLIGHT_COLOR 4227 case OSC_Reset(OSC_HIGHLIGHT_BG): 4228 case OSC_Reset(OSC_HIGHLIGHT_FG): 4229#endif 4230#if OPT_TEK4014 4231 case OSC_Reset(OSC_TEK_FG): 4232 case OSC_Reset(OSC_TEK_BG): 4233 case OSC_Reset(OSC_TEK_CURSOR): 4234#endif 4235 if (xw->misc.dynamicColors) { 4236 ResetColorsRequest(xw, mode); 4237 } 4238 break; 4239 4240 case 22: 4241 xtermSetupPointer(xw, buf); 4242 break; 4243 4244 case 30: 4245 case 31: 4246 /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ 4247 break; 4248 4249#ifdef ALLOWLOGGING 4250 case 46: /* new log file */ 4251#ifdef ALLOWLOGFILECHANGES 4252 /* 4253 * Warning, enabling this feature allows people to overwrite 4254 * arbitrary files accessible to the person running xterm. 4255 */ 4256 if (strcmp(buf, "?")) { 4257 char *bp; 4258 if ((bp = x_strdup(buf)) != NULL) { 4259 free(screen->logfile); 4260 screen->logfile = bp; 4261 break; 4262 } 4263 } 4264#endif 4265 Bell(xw, XkbBI_Info, 0); 4266 Bell(xw, XkbBI_Info, 0); 4267 break; 4268#endif /* ALLOWLOGGING */ 4269 4270 case 50: 4271#if OPT_SHIFT_FONTS 4272 if (*buf == '?') { 4273 QueryFontRequest(xw, buf, final); 4274 } else if (xw->misc.shift_fonts) { 4275 ChangeFontRequest(xw, buf); 4276 } 4277#endif /* OPT_SHIFT_FONTS */ 4278 break; 4279 case 51: 4280 /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ 4281 break; 4282 4283#if OPT_PASTE64 4284 case 52: 4285 ManipulateSelectionData(xw, screen, buf, final); 4286 break; 4287#endif 4288 /* 4289 * One could write code to send back the display and host names, 4290 * but that could potentially open a fairly nasty security hole. 4291 */ 4292 default: 4293 TRACE(("do_osc - unrecognized code\n")); 4294 break; 4295 } 4296 unparse_end(xw); 4297} 4298 4299/* 4300 * Parse one nibble of a hex byte from the OSC string. We have removed the 4301 * string-terminator (replacing it with a null), so the only other delimiter 4302 * that is expected is semicolon. Ignore other characters (Ray Neuman says 4303 * "real" terminals accept commas in the string definitions). 4304 */ 4305static int 4306udk_value(const char **cp) 4307{ 4308 int result = -1; 4309 4310 for (;;) { 4311 int c; 4312 4313 if ((c = **cp) != '\0') 4314 *cp = *cp + 1; 4315 if (c == ';' || c == '\0') 4316 break; 4317 if ((result = x_hex2int(c)) >= 0) 4318 break; 4319 } 4320 4321 return result; 4322} 4323 4324void 4325reset_decudk(XtermWidget xw) 4326{ 4327 int n; 4328 for (n = 0; n < MAX_UDK; n++) { 4329 FreeAndNull(xw->work.user_keys[n].str); 4330 xw->work.user_keys[n].len = 0; 4331 } 4332} 4333 4334/* 4335 * Parse the data for DECUDK (user-defined keys). 4336 */ 4337static void 4338parse_decudk(XtermWidget xw, const char *cp) 4339{ 4340 while (*cp) { 4341 const char *base = cp; 4342 char *str = malloc(strlen(cp) + 3); 4343 unsigned key = 0; 4344 int len = 0; 4345 4346 if (str == NULL) 4347 break; 4348 4349 while (isdigit(CharOf(*cp))) 4350 key = (key * 10) + (unsigned) (*cp++ - '0'); 4351 4352 if (*cp == '/') { 4353 int lo, hi; 4354 4355 cp++; 4356 while ((hi = udk_value(&cp)) >= 0 4357 && (lo = udk_value(&cp)) >= 0) { 4358 str[len++] = (char) ((hi << 4) | lo); 4359 } 4360 } 4361 if (len > 0 && key < MAX_UDK) { 4362 str[len] = '\0'; 4363 free(xw->work.user_keys[key].str); 4364 xw->work.user_keys[key].str = str; 4365 xw->work.user_keys[key].len = len; 4366 TRACE(("parse_decudk %d:%.*s\n", key, len, str)); 4367 } else { 4368 free(str); 4369 } 4370 if (*cp == ';') 4371 cp++; 4372 if (cp == base) /* badly-formed sequence - bail out */ 4373 break; 4374 } 4375} 4376 4377/* 4378 * Parse numeric parameters. Normally we use a state machine to simplify 4379 * interspersing with control characters, but have the string already. 4380 */ 4381static void 4382parse_ansi_params(ANSI *params, const char **string) 4383{ 4384 const char *cp = *string; 4385 ParmType nparam = 0; 4386 int last_empty = 1; 4387 4388 memset(params, 0, sizeof(*params)); 4389 while (*cp != '\0') { 4390 Char ch = CharOf(*cp++); 4391 4392 if (isdigit(ch)) { 4393 last_empty = 0; 4394 if (nparam < NPARAM) { 4395 params->a_param[nparam] = 4396 (ParmType) ((params->a_param[nparam] * 10) 4397 + (ch - '0')); 4398 } 4399 } else if (ch == ';') { 4400 last_empty = 1; 4401 nparam++; 4402 } else if (ch < 32) { 4403 /* EMPTY */ ; 4404 } else { 4405 /* should be 0x30 to 0x7e */ 4406 params->a_final = ch; 4407 break; 4408 } 4409 } 4410 4411 *string = cp; 4412 if (!last_empty) 4413 nparam++; 4414 if (nparam > NPARAM) 4415 params->a_nparam = NPARAM; 4416 else 4417 params->a_nparam = nparam; 4418} 4419 4420#if OPT_TRACE 4421#define SOFT_WIDE 10 4422#define SOFT_HIGH 20 4423 4424static void 4425parse_decdld(ANSI *params, const char *string) 4426{ 4427 char DscsName[8]; 4428 int len; 4429 int Pfn = params->a_param[0]; 4430 int Pcn = params->a_param[1]; 4431 int Pe = params->a_param[2]; 4432 int Pcmw = params->a_param[3]; 4433 int Pw = params->a_param[4]; 4434 int Pt = params->a_param[5]; 4435 int Pcmh = params->a_param[6]; 4436 int Pcss = params->a_param[7]; 4437 4438 int start_char = Pcn + 0x20; 4439 int char_wide = ((Pcmw == 0) 4440 ? (Pcss ? 6 : 10) 4441 : (Pcmw > 4 4442 ? Pcmw 4443 : (Pcmw + 3))); 4444 int char_high = ((Pcmh == 0) 4445 ? ((Pcmw >= 2 && Pcmw <= 4) 4446 ? 10 4447 : 20) 4448 : Pcmh); 4449 Char ch; 4450 Char bits[SOFT_HIGH][SOFT_WIDE]; 4451 Bool first = True; 4452 Bool prior = False; 4453 int row = 0, col = 0; 4454 4455 TRACE(("Parsing DECDLD\n")); 4456 TRACE((" font number %d\n", Pfn)); 4457 TRACE((" starting char %d\n", Pcn)); 4458 TRACE((" erase control %d\n", Pe)); 4459 TRACE((" char-width %d\n", Pcmw)); 4460 TRACE((" font-width %d\n", Pw)); 4461 TRACE((" text/full %d\n", Pt)); 4462 TRACE((" char-height %d\n", Pcmh)); 4463 TRACE((" charset-size %d\n", Pcss)); 4464 4465 if (Pfn > 1 4466 || Pcn > 95 4467 || Pe > 2 4468 || Pcmw > 10 4469 || Pcmw == 1 4470 || Pt > 2 4471 || Pcmh > 20 4472 || Pcss > 1 4473 || char_wide > SOFT_WIDE 4474 || char_high > SOFT_HIGH) { 4475 TRACE(("DECDLD illegal parameter\n")); 4476 return; 4477 } 4478 4479 len = 0; 4480 while (*string != '\0') { 4481 ch = CharOf(*string++); 4482 if (ch >= ANSI_SPA && ch <= 0x2f) { 4483 if (len < 2) 4484 DscsName[len++] = (char) ch; 4485 } else if (ch >= 0x30 && ch <= 0x7e) { 4486 DscsName[len++] = (char) ch; 4487 break; 4488 } 4489 } 4490 DscsName[len] = 0; 4491 TRACE((" Dscs name '%s'\n", DscsName)); 4492 4493 TRACE((" character matrix %dx%d\n", char_high, char_wide)); 4494 while (*string != '\0') { 4495 if (first) { 4496 TRACE(("Char %d:\n", start_char)); 4497 if (prior) { 4498 for (row = 0; row < char_high; ++row) { 4499 TRACE(("%.*s\n", char_wide, bits[row])); 4500 } 4501 } 4502 prior = False; 4503 first = False; 4504 for (row = 0; row < char_high; ++row) { 4505 for (col = 0; col < char_wide; ++col) { 4506 bits[row][col] = '.'; 4507 } 4508 } 4509 row = col = 0; 4510 } 4511 ch = CharOf(*string++); 4512 if (ch >= 0x3f && ch <= 0x7e) { 4513 int n; 4514 4515 ch = CharOf(ch - 0x3f); 4516 for (n = 0; n < 6; ++n) { 4517 bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.'); 4518 } 4519 col += 1; 4520 prior = True; 4521 } else if (ch == '/') { 4522 row += 6; 4523 col = 0; 4524 } else if (ch == ';') { 4525 first = True; 4526 ++start_char; 4527 } 4528 } 4529} 4530#else 4531#define parse_decdld(p,q) /* nothing */ 4532#endif 4533 4534#if OPT_DEC_RECTOPS 4535static const char * 4536skip_params(const char *cp) 4537{ 4538 while (*cp == ';' || (*cp >= '0' && *cp <= '9')) 4539 ++cp; 4540 return cp; 4541} 4542 4543static int 4544parse_int_param(const char **cp) 4545{ 4546 int result = 0; 4547 const char *s = *cp; 4548 while (*s != '\0') { 4549 if (*s == ';') { 4550 ++s; 4551 break; 4552 } else if (*s >= '0' && *s <= '9') { 4553 result = (result * 10) + (*s++ - '0'); 4554 } else { 4555 s += strlen(s); 4556 } 4557 } 4558 TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s)); 4559 *cp = s; 4560 return result; 4561} 4562 4563static int 4564parse_chr_param(const char **cp) 4565{ 4566 int result = 0; 4567 const char *s = *cp; 4568 if (*s != '\0') { 4569 if ((result = CharOf(*s++)) != 0) { 4570 if (*s == ';') { 4571 ++s; 4572 } else if (*s != '\0') { 4573 result = 0; 4574 } 4575 } 4576 } 4577 TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s)); 4578 *cp = s; 4579 return result; 4580} 4581 4582static void 4583restore_DECCIR(XtermWidget xw, const char *cp) 4584{ 4585 TScreen *screen = TScreenOf(xw); 4586 int value; 4587 4588 /* row */ 4589 if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen)) 4590 return; 4591 screen->cur_row = (value - 1); 4592 4593 /* column */ 4594 if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen)) 4595 return; 4596 screen->cur_col = (value - 1); 4597 4598 /* page */ 4599 if (parse_int_param(&cp) != 1) 4600 return; 4601 4602 /* rendition */ 4603 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) 4604 return; 4605 UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD)); 4606 xw->flags |= (value & 8) ? INVERSE : 0; 4607 xw->flags |= (value & 4) ? BLINK : 0; 4608 xw->flags |= (value & 2) ? UNDERLINE : 0; 4609 xw->flags |= (value & 1) ? BOLD : 0; 4610 4611 /* attributes */ 4612 if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40) 4613 return; 4614 screen->protected_mode &= ~DEC_PROTECT; 4615 screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0; 4616 4617 /* flags */ 4618 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) 4619 return; 4620 screen->do_wrap = (value & 8) ? True : False; 4621 screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0)); 4622 UIntClr(xw->flags, ORIGIN); 4623 xw->flags |= (value & 1) ? ORIGIN : 0; 4624 4625 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS) 4626 return; 4627 screen->curgl = (Char) value; 4628 4629 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS) 4630 return; 4631 screen->curgr = (Char) value; 4632 4633 /* character-set size */ 4634 if (parse_chr_param(&cp) != 0x4f) /* works for xterm */ 4635 return; 4636 4637 /* SCS designators */ 4638 for (value = 0; value < NUM_GSETS; ++value) { 4639 if (*cp == '%') { 4640 xtermDecodeSCS(xw, value, 0, '%', *++cp); 4641 } else if (*cp != '\0') { 4642 xtermDecodeSCS(xw, value, 0, '\0', *cp); 4643 } else { 4644 return; 4645 } 4646 cp++; 4647 } 4648 4649 TRACE(("...done DECCIR\n")); 4650} 4651 4652static void 4653restore_DECTABSR(XtermWidget xw, const char *cp) 4654{ 4655 int stop = 0; 4656 Bool fail = False; 4657 4658 TabZonk(xw->tabs); 4659 while (*cp != '\0' && !fail) { 4660 if ((*cp) >= '0' && (*cp) <= '9') { 4661 stop = (stop * 10) + ((*cp) - '0'); 4662 } else if (*cp == '/') { 4663 --stop; 4664 if (OkTAB(stop)) { 4665 TabSet(xw->tabs, stop); 4666 stop = 0; 4667 } else { 4668 fail = True; 4669 } 4670 } else { 4671 fail = True; 4672 } 4673 ++cp; 4674 } 4675 --stop; 4676 if (OkTAB(stop)) 4677 TabSet(xw->tabs, stop); 4678 4679 TRACE(("...done DECTABSR\n")); 4680} 4681#endif 4682 4683void 4684do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen) 4685{ 4686 TScreen *screen = TScreenOf(xw); 4687 char reply[BUFSIZ]; 4688 const char *cp = (const char *) dcsbuf; 4689 Bool okay; 4690 ANSI params; 4691#if OPT_DEC_RECTOPS 4692 char psarg = '0'; 4693#endif 4694 4695 TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen)); 4696 4697 if (dcslen != strlen(cp)) 4698 /* shouldn't have nulls in the string */ 4699 return; 4700 4701 switch (*cp) { /* intermediate character, or parameter */ 4702 case '$': /* DECRQSS */ 4703 okay = True; 4704 4705 cp++; 4706 if (*cp == 'q') { 4707 *reply = '\0'; 4708 cp++; 4709 if (!strcmp(cp, "\"q")) { /* DECSCA */ 4710 TRACE(("DECRQSS -> DECSCA\n")); 4711 sprintf(reply, "%d%s", 4712 (screen->protected_mode == DEC_PROTECT) 4713 && (xw->flags & PROTECTED) ? 1 : 0, 4714 cp); 4715 } else if (!strcmp(cp, "\"p")) { /* DECSCL */ 4716 if (screen->vtXX_level < 2) { 4717 /* actually none of DECRQSS is valid for vt100's */ 4718 break; 4719 } 4720 TRACE(("DECRQSS -> DECSCL\n")); 4721 sprintf(reply, "%d%s%s", 4722 (screen->vtXX_level ? 4723 screen->vtXX_level : 1) + 60, 4724 (screen->vtXX_level >= 2) 4725 ? (screen->control_eight_bits 4726 ? ";0" : ";1") 4727 : "", 4728 cp); 4729 } else if (!strcmp(cp, "r")) { /* DECSTBM */ 4730 TRACE(("DECRQSS -> DECSTBM\n")); 4731 sprintf(reply, "%d;%dr", 4732 screen->top_marg + 1, 4733 screen->bot_marg + 1); 4734 } else if (!strcmp(cp, "s")) { /* DECSLRM */ 4735 if (screen->vtXX_level >= 4) { /* VT420 */ 4736 TRACE(("DECRQSS -> DECSLRM\n")); 4737 sprintf(reply, "%d;%ds", 4738 screen->lft_marg + 1, 4739 screen->rgt_marg + 1); 4740 } else { 4741 okay = False; 4742 } 4743 } else if (!strcmp(cp, "m")) { /* SGR */ 4744 TRACE(("DECRQSS -> SGR\n")); 4745 xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background); 4746 strcat(reply, "m"); 4747 } else if (!strcmp(cp, " q")) { /* DECSCUSR */ 4748 int code = STEADY_BLOCK; 4749 if (isCursorUnderline(screen)) 4750 code = STEADY_UNDERLINE; 4751 else if (isCursorBar(screen)) 4752 code = STEADY_BAR; 4753#if OPT_BLINK_CURS 4754 if (screen->cursor_blink_esc != 0) 4755 code -= 1; 4756#endif 4757 TRACE(("reply DECSCUSR\n")); 4758 sprintf(reply, "%d%s", code, cp); 4759 } else if (!strcmp(cp, "t")) { /* DECSLPP */ 4760 sprintf(reply, "%d%s", 4761 ((screen->max_row > 24) ? screen->max_row : 24), 4762 cp); 4763 TRACE(("reply DECSLPP\n")); 4764 } else if (!strcmp(cp, "$|")) { /* DECSCPP */ 4765 TRACE(("reply DECSCPP\n")); 4766 sprintf(reply, "%d%s", 4767 ((xw->flags & IN132COLUMNS) ? 132 : 80), 4768 cp); 4769 } else if (!strcmp(cp, "*|")) { /* DECSNLS */ 4770 TRACE(("reply DECSNLS\n")); 4771 sprintf(reply, "%d%s", 4772 screen->max_row + 1, 4773 cp); 4774 } else { 4775 okay = False; 4776 } 4777 4778 unparseputc1(xw, ANSI_DCS); 4779 unparseputc(xw, okay ? '1' : '0'); 4780 unparseputc(xw, '$'); 4781 unparseputc(xw, 'r'); 4782 cp = reply; 4783 unparseputs(xw, cp); 4784 unparseputc1(xw, ANSI_ST); 4785 } else { 4786 unparseputc(xw, ANSI_CAN); 4787 } 4788 break; 4789 case '+': 4790 cp++; 4791 switch (*cp) { 4792#if OPT_TCAP_QUERY 4793 case 'p': 4794 if (AllowTcapOps(xw, etSetTcap)) { 4795 set_termcap(xw, cp + 1); 4796 } 4797 break; 4798 case 'q': 4799 if (AllowTcapOps(xw, etGetTcap)) { 4800 Bool fkey; 4801 unsigned state; 4802 int code; 4803 const char *tmp; 4804 const char *parsed = ++cp; 4805 4806 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4807 4808 unparseputc1(xw, ANSI_DCS); 4809 4810 unparseputc(xw, code >= 0 ? '1' : '0'); 4811 4812 unparseputc(xw, '+'); 4813 unparseputc(xw, 'r'); 4814 4815 while (*cp != 0 && (code >= -1)) { 4816 if (cp == parsed) 4817 break; /* no data found, error */ 4818 4819 for (tmp = cp; tmp != parsed; ++tmp) 4820 unparseputc(xw, *tmp); 4821 4822 if (code >= 0) { 4823 unparseputc(xw, '='); 4824 screen->tc_query_code = code; 4825 screen->tc_query_fkey = fkey; 4826#if OPT_ISO_COLORS 4827 /* XK_COLORS is a fake code for the "Co" entry (maximum 4828 * number of colors) */ 4829 if (code == XK_COLORS) { 4830 unparseputn(xw, (unsigned) NUM_ANSI_COLORS); 4831 } else 4832#if OPT_DIRECT_COLOR 4833 if (code == XK_RGB) { 4834 if (TScreenOf(xw)->direct_color && xw->has_rgb) { 4835 if (xw->rgb_widths[0] == xw->rgb_widths[1] && 4836 xw->rgb_widths[1] == xw->rgb_widths[2]) { 4837 unparseputn(xw, xw->rgb_widths[0]); 4838 } else { 4839 char temp[1024]; 4840 sprintf(temp, "%d/%d/%d", 4841 xw->rgb_widths[0], 4842 xw->rgb_widths[1], 4843 xw->rgb_widths[2]); 4844 unparseputs(xw, temp); 4845 } 4846 } else { 4847 unparseputs(xw, "-1"); 4848 } 4849 } else 4850#endif 4851#endif 4852 if (code == XK_TCAPNAME) { 4853 unparseputs(xw, resource.term_name); 4854 } else { 4855 XKeyEvent event; 4856 memset(&event, 0, sizeof(event)); 4857 event.state = state; 4858 Input(xw, &event, False); 4859 } 4860 screen->tc_query_code = -1; 4861 } else { 4862 break; /* no match found, error */ 4863 } 4864 4865 cp = parsed; 4866 if (*parsed == ';') { 4867 unparseputc(xw, *parsed++); 4868 cp = parsed; 4869 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4870 } 4871 } 4872 unparseputc1(xw, ANSI_ST); 4873 } 4874 break; 4875#endif 4876#if OPT_XRES_QUERY 4877 case 'Q': 4878 ++cp; 4879 if (AllowXResOps(xw)) { 4880 Boolean first = True; 4881 while (*cp != '\0') { 4882 const char *parsed = 0; 4883 const char *tmp; 4884 char *name = x_decode_hex(cp, &parsed); 4885 char *value; 4886 char *result; 4887 if (cp == parsed || name == NULL) { 4888 free(name); 4889 break; /* no data found, error */ 4890 } 4891 TRACE(("query-feature '%s'\n", name)); 4892 if ((value = vt100ResourceToString(xw, name)) != 0) { 4893 okay = True; /* valid */ 4894 } else { 4895 okay = False; /* invalid */ 4896 } 4897 if (first) { 4898 unparseputc1(xw, ANSI_DCS); 4899 unparseputc(xw, okay ? '1' : '0'); 4900 unparseputc(xw, '+'); 4901 unparseputc(xw, 'R'); 4902 first = False; 4903 } 4904 4905 for (tmp = cp; tmp != parsed; ++tmp) 4906 unparseputc(xw, *tmp); 4907 4908 if (value != 0) { 4909 unparseputc1(xw, '='); 4910 result = x_encode_hex(value); 4911 unparseputs(xw, result); 4912 } else { 4913 result = NULL; 4914 } 4915 4916 free(name); 4917 free(value); 4918 free(result); 4919 4920 cp = parsed; 4921 if (*parsed == ';') { 4922 unparseputc(xw, *parsed++); 4923 cp = parsed; 4924 } 4925 } 4926 if (!first) 4927 unparseputc1(xw, ANSI_ST); 4928 } 4929 break; 4930#endif 4931 } 4932 break; 4933#if OPT_DEC_RECTOPS 4934 case '1': 4935 /* FALLTHRU */ 4936 case '2': 4937 if (*skip_params(cp) == '$') { 4938 psarg = *cp++; 4939 if ((*cp++ == '$') 4940 && (*cp++ == 't') 4941 && (screen->vtXX_level >= 3)) { 4942 switch (psarg) { 4943 case '1': 4944 TRACE(("DECRSPS (DECCIR)\n")); 4945 restore_DECCIR(xw, cp); 4946 break; 4947 case '2': 4948 TRACE(("DECRSPS (DECTABSR)\n")); 4949 restore_DECTABSR(xw, cp); 4950 break; 4951 } 4952 } 4953 break; 4954 } 4955#endif 4956 /* FALLTHRU */ 4957 default: 4958 if (optRegisGraphics(screen) || 4959 optSixelGraphics(screen) || 4960 screen->vtXX_level >= 2) { /* VT220 */ 4961 parse_ansi_params(¶ms, &cp); 4962 switch (params.a_final) { 4963 case 'p': /* ReGIS */ 4964#if OPT_REGIS_GRAPHICS 4965 if (optRegisGraphics(screen)) { 4966 parse_regis(xw, ¶ms, cp); 4967 } 4968#else 4969 TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n")); 4970#endif 4971 break; 4972 case 'q': /* sixel */ 4973#if OPT_SIXEL_GRAPHICS 4974 if (optSixelGraphics(screen)) { 4975 (void) parse_sixel(xw, ¶ms, cp); 4976 } 4977#else 4978 TRACE(("ignoring sixel graphic (compilation flag not enabled)\n")); 4979#endif 4980 break; 4981 case '|': /* DECUDK */ 4982 if (screen->vtXX_level >= 2) { /* VT220 */ 4983 if (params.a_param[0] == 0) 4984 reset_decudk(xw); 4985 parse_decudk(xw, cp); 4986 } 4987 break; 4988 case L_CURL: /* DECDLD */ 4989 if (screen->vtXX_level >= 2) { /* VT220 */ 4990 parse_decdld(¶ms, cp); 4991 } 4992 break; 4993 } 4994 } 4995 break; 4996 } 4997 unparse_end(xw); 4998} 4999 5000#if OPT_DEC_RECTOPS 5001enum { 5002 mdUnknown = 0, 5003 mdMaybeSet = 1, 5004 mdMaybeReset = 2, 5005 mdAlwaysSet = 3, 5006 mdAlwaysReset = 4 5007}; 5008 5009#define MdBool(bool) ((bool) ? mdMaybeSet : mdMaybeReset) 5010#define MdFlag(mode,flag) MdBool((mode) & (flag)) 5011 5012/* 5013 * Reply is the same format as the query, with pair of mode/value: 5014 * 0 - not recognized 5015 * 1 - set 5016 * 2 - reset 5017 * 3 - permanently set 5018 * 4 - permanently reset 5019 * Only one mode can be reported at a time. 5020 */ 5021void 5022do_ansi_rqm(XtermWidget xw, int nparams, int *params) 5023{ 5024 ANSI reply; 5025 int count = 0; 5026 5027 TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0])); 5028 memset(&reply, 0, sizeof(reply)); 5029 5030 if (nparams >= 1) { 5031 int result = mdUnknown; 5032 5033 /* DECRQM can only ask about one mode at a time */ 5034 switch (params[0]) { 5035 case 1: /* GATM */ 5036 result = mdAlwaysReset; 5037 break; 5038 case 2: 5039 result = MdFlag(xw->keyboard.flags, MODE_KAM); 5040 break; 5041 case 3: /* CRM */ 5042 result = mdMaybeReset; 5043 break; 5044 case 4: 5045 result = MdFlag(xw->flags, INSERT); 5046 break; 5047 case 5: /* SRTM */ 5048 case 7: /* VEM */ 5049 case 10: /* HEM */ 5050 case 11: /* PUM */ 5051 result = mdAlwaysReset; 5052 break; 5053 case 12: 5054 result = MdFlag(xw->keyboard.flags, MODE_SRM); 5055 break; 5056 case 13: /* FEAM */ 5057 case 14: /* FETM */ 5058 case 15: /* MATM */ 5059 case 16: /* TTM */ 5060 case 17: /* SATM */ 5061 case 18: /* TSM */ 5062 case 19: /* EBM */ 5063 result = mdAlwaysReset; 5064 break; 5065 case 20: 5066 result = MdFlag(xw->flags, LINEFEED); 5067 break; 5068 } 5069 reply.a_param[count++] = (ParmType) params[0]; 5070 reply.a_param[count++] = (ParmType) result; 5071 } 5072 reply.a_type = ANSI_CSI; 5073 reply.a_nparam = (ParmType) count; 5074 reply.a_inters = '$'; 5075 reply.a_final = 'y'; 5076 unparseseq(xw, &reply); 5077} 5078 5079void 5080do_dec_rqm(XtermWidget xw, int nparams, int *params) 5081{ 5082 ANSI reply; 5083 int count = 0; 5084 5085 TRACE(("do_dec_rqm %d:%d\n", nparams, params[0])); 5086 memset(&reply, 0, sizeof(reply)); 5087 5088 if (nparams >= 1) { 5089 TScreen *screen = TScreenOf(xw); 5090 int result = mdUnknown; 5091 5092 /* DECRQM can only ask about one mode at a time */ 5093 switch ((DECSET_codes) params[0]) { 5094 case srm_DECCKM: 5095 result = MdFlag(xw->keyboard.flags, MODE_DECCKM); 5096 break; 5097 case srm_DECANM: /* ANSI/VT52 mode */ 5098#if OPT_VT52_MODE 5099 result = MdBool(screen->vtXX_level >= 1); 5100#else 5101 result = mdMaybeSet; 5102#endif 5103 break; 5104 case srm_DECCOLM: 5105 result = MdFlag(xw->flags, IN132COLUMNS); 5106 break; 5107 case srm_DECSCLM: /* (slow scroll) */ 5108 result = MdFlag(xw->flags, SMOOTHSCROLL); 5109 break; 5110 case srm_DECSCNM: 5111 result = MdFlag(xw->flags, REVERSE_VIDEO); 5112 break; 5113 case srm_DECOM: 5114 result = MdFlag(xw->flags, ORIGIN); 5115 break; 5116 case srm_DECAWM: 5117 result = MdFlag(xw->flags, WRAPAROUND); 5118 break; 5119 case srm_DECARM: 5120 result = mdAlwaysReset; 5121 break; 5122 case srm_X10_MOUSE: /* X10 mouse */ 5123 result = MdBool(screen->send_mouse_pos == X10_MOUSE); 5124 break; 5125#if OPT_TOOLBAR 5126 case srm_RXVT_TOOLBAR: 5127 result = MdBool(resource.toolBar); 5128 break; 5129#endif 5130#if OPT_BLINK_CURS 5131 case srm_ATT610_BLINK: /* AT&T 610: Start/stop blinking cursor */ 5132 result = MdBool(screen->cursor_blink_esc); 5133 break; 5134 case srm_CURSOR_BLINK_OPS: 5135 switch (screen->cursor_blink) { 5136 case cbTrue: 5137 result = mdMaybeSet; 5138 break; 5139 case cbFalse: 5140 result = mdMaybeReset; 5141 break; 5142 case cbAlways: 5143 result = mdAlwaysSet; 5144 break; 5145 case cbLAST: 5146 /* FALLTHRU */ 5147 case cbNever: 5148 result = mdAlwaysReset; 5149 break; 5150 } 5151 break; 5152 case srm_XOR_CURSOR_BLINKS: 5153 result = (screen->cursor_blink_xor 5154 ? mdAlwaysSet 5155 : mdAlwaysReset); 5156 break; 5157#endif 5158 case srm_DECPFF: /* print form feed */ 5159 result = MdBool(PrinterOf(screen).printer_formfeed); 5160 break; 5161 case srm_DECPEX: /* print extent */ 5162 result = MdBool(PrinterOf(screen).printer_extent); 5163 break; 5164 case srm_DECTCEM: /* Show/hide cursor (VT200) */ 5165 result = MdBool(screen->cursor_set); 5166 break; 5167 case srm_RXVT_SCROLLBAR: 5168 result = MdBool(screen->fullVwin.sb_info.width != OFF); 5169 break; 5170#if OPT_SHIFT_FONTS 5171 case srm_RXVT_FONTSIZE: 5172 result = MdBool(xw->misc.shift_fonts); 5173 break; 5174#endif 5175#if OPT_TEK4014 5176 case srm_DECTEK: 5177 result = MdBool(TEK4014_ACTIVE(xw)); 5178 break; 5179#endif 5180 case srm_132COLS: 5181 result = MdBool(screen->c132); 5182 break; 5183 case srm_CURSES_HACK: 5184 result = MdBool(screen->curses); 5185 break; 5186 case srm_DECNRCM: /* national charset (VT220) */ 5187 if (screen->vtXX_level >= 2) { 5188 result = MdFlag(xw->flags, NATIONAL); 5189 } else { 5190 result = 0; 5191 } 5192 break; 5193 case srm_MARGIN_BELL: /* margin bell */ 5194 result = MdBool(screen->marginbell); 5195 break; 5196#if OPT_PRINT_GRAPHICS 5197 case srm_DECGEPM: /* Graphics Expanded Print Mode */ 5198 result = MdBool(screen->graphics_expanded_print_mode); 5199 break; 5200#endif 5201 case srm_REVERSEWRAP: /* reverse wraparound */ 5202 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax)) 5203 result = MdFlag(xw->flags, REVERSEWRAP); 5204 break; 5205#if defined(ALLOWLOGGING) 5206 case srm_ALLOWLOGGING: /* logging */ 5207 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode)) 5208#if defined(ALLOWLOGFILEONOFF) 5209 result = MdBool(screen->logging); 5210#else 5211 result = ((MdBool(screen->logging) == mdMaybeSet) 5212 ? mdAlwaysSet 5213 : mdAlwaysReset); 5214#endif 5215 break; 5216#elif OPT_PRINT_GRAPHICS 5217 case srm_DECGPBM: /* Graphics Print Background Mode */ 5218 result = MdBool(screen->graphics_print_background_mode); 5219 break; 5220#endif 5221 case srm_OPT_ALTBUF_CURSOR: /* alternate buffer & cursor */ 5222 /* FALLTHRU */ 5223 case srm_OPT_ALTBUF: 5224 result = MdBool(screen->whichBuf); 5225 break; 5226 case srm_ALTBUF: 5227 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode)) 5228 result = MdBool(screen->whichBuf); 5229 break; 5230 case srm_DECNKM: 5231 result = MdFlag(xw->keyboard.flags, MODE_DECKPAM); 5232 break; 5233 case srm_DECBKM: 5234 result = MdFlag(xw->keyboard.flags, MODE_DECBKM); 5235 break; 5236 case srm_DECLRMM: 5237 if (screen->vtXX_level >= 4) { /* VT420 */ 5238 result = MdFlag(xw->flags, LEFT_RIGHT); 5239 } else { 5240 result = 0; 5241 } 5242 break; 5243#if OPT_SIXEL_GRAPHICS 5244 case srm_DECSDM: 5245 result = MdFlag(xw->keyboard.flags, MODE_DECSDM); 5246 break; 5247#endif 5248 case srm_DECNCSM: 5249 if (screen->vtXX_level >= 5) { /* VT510 */ 5250 result = MdFlag(xw->flags, NOCLEAR_COLM); 5251 } else { 5252 result = 0; 5253 } 5254 break; 5255 case srm_VT200_MOUSE: /* xterm bogus sequence */ 5256 result = MdBool(screen->send_mouse_pos == VT200_MOUSE); 5257 break; 5258 case srm_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */ 5259 result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE); 5260 break; 5261 case srm_BTN_EVENT_MOUSE: 5262 result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE); 5263 break; 5264 case srm_ANY_EVENT_MOUSE: 5265 result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE); 5266 break; 5267#if OPT_FOCUS_EVENT 5268 case srm_FOCUS_EVENT_MOUSE: 5269 result = MdBool(screen->send_focus_pos); 5270 break; 5271#endif 5272 case srm_EXT_MODE_MOUSE: 5273 /* FALLTHRU */ 5274 case srm_SGR_EXT_MODE_MOUSE: 5275 /* FALLTHRU */ 5276 case srm_URXVT_EXT_MODE_MOUSE: 5277 /* FALLTHRU */ 5278 case srm_PIXEL_POSITION_MOUSE: 5279 result = MdBool(screen->extend_coords == params[0]); 5280 break; 5281 case srm_ALTERNATE_SCROLL: 5282 result = MdBool(screen->alternateScroll); 5283 break; 5284 case srm_RXVT_SCROLL_TTY_OUTPUT: 5285 result = MdBool(screen->scrollttyoutput); 5286 break; 5287 case srm_RXVT_SCROLL_TTY_KEYPRESS: 5288 result = MdBool(screen->scrollkey); 5289 break; 5290 case srm_EIGHT_BIT_META: 5291 result = MdBool(screen->eight_bit_meta); 5292 break; 5293#if OPT_NUM_LOCK 5294 case srm_REAL_NUMLOCK: 5295 result = MdBool(xw->misc.real_NumLock); 5296 break; 5297 case srm_META_SENDS_ESC: 5298 result = MdBool(screen->meta_sends_esc); 5299 break; 5300#endif 5301 case srm_DELETE_IS_DEL: 5302 result = MdBool(xtermDeleteIsDEL(xw)); 5303 break; 5304#if OPT_NUM_LOCK 5305 case srm_ALT_SENDS_ESC: 5306 result = MdBool(screen->alt_sends_esc); 5307 break; 5308#endif 5309 case srm_KEEP_SELECTION: 5310 result = MdBool(screen->keepSelection); 5311 break; 5312 case srm_SELECT_TO_CLIPBOARD: 5313 result = MdBool(screen->selectToClipboard); 5314 break; 5315 case srm_BELL_IS_URGENT: 5316 result = MdBool(screen->bellIsUrgent); 5317 break; 5318 case srm_POP_ON_BELL: 5319 result = MdBool(screen->poponbell); 5320 break; 5321 case srm_KEEP_CLIPBOARD: 5322 result = MdBool(screen->keepClipboard); 5323 break; 5324 case srm_ALLOW_ALTBUF: 5325 result = MdBool(xw->misc.titeInhibit); 5326 break; 5327 case srm_SAVE_CURSOR: 5328 result = MdBool(screen->sc[screen->whichBuf].saved); 5329 break; 5330#if OPT_TCAP_FKEYS 5331 case srm_TCAP_FKEYS: 5332 result = MdBool(xw->keyboard.type == keyboardIsTermcap); 5333 break; 5334#endif 5335#if OPT_SUN_FUNC_KEYS 5336 case srm_SUN_FKEYS: 5337 result = MdBool(xw->keyboard.type == keyboardIsSun); 5338 break; 5339#endif 5340#if OPT_HP_FUNC_KEYS 5341 case srm_HP_FKEYS: 5342 result = MdBool(xw->keyboard.type == keyboardIsHP); 5343 break; 5344#endif 5345#if OPT_SCO_FUNC_KEYS 5346 case srm_SCO_FKEYS: 5347 result = MdBool(xw->keyboard.type == keyboardIsSCO); 5348 break; 5349#endif 5350 case srm_LEGACY_FKEYS: 5351 result = MdBool(xw->keyboard.type == keyboardIsLegacy); 5352 break; 5353#if OPT_SUNPC_KBD 5354 case srm_VT220_FKEYS: 5355 result = MdBool(xw->keyboard.type == keyboardIsVT220); 5356 break; 5357#endif 5358#if OPT_PASTE64 || OPT_READLINE 5359 case srm_PASTE_IN_BRACKET: 5360 result = MdBool(SCREEN_FLAG(screen, paste_brackets)); 5361 break; 5362#endif 5363#if OPT_READLINE 5364 case srm_BUTTON1_MOVE_POINT: 5365 result = MdBool(SCREEN_FLAG(screen, click1_moves)); 5366 break; 5367 case srm_BUTTON2_MOVE_POINT: 5368 result = MdBool(SCREEN_FLAG(screen, paste_moves)); 5369 break; 5370 case srm_DBUTTON3_DELETE: 5371 result = MdBool(SCREEN_FLAG(screen, dclick3_deletes)); 5372 break; 5373 case srm_PASTE_QUOTE: 5374 result = MdBool(SCREEN_FLAG(screen, paste_quotes)); 5375 break; 5376 case srm_PASTE_LITERAL_NL: 5377 result = MdBool(SCREEN_FLAG(screen, paste_literal_nl)); 5378 break; 5379#endif /* OPT_READLINE */ 5380#if OPT_GRAPHICS 5381 case srm_PRIVATE_COLOR_REGISTERS: 5382 result = MdBool(screen->privatecolorregisters); 5383 break; 5384#endif 5385#if OPT_SIXEL_GRAPHICS 5386 case srm_SIXEL_SCROLLS_RIGHT: 5387 result = MdBool(screen->sixel_scrolls_right); 5388 break; 5389#endif 5390 default: 5391 TRACE(("DATA_ERROR: requested report for unknown private mode %d\n", 5392 params[0])); 5393 } 5394 reply.a_param[count++] = (ParmType) params[0]; 5395 reply.a_param[count++] = (ParmType) result; 5396 TRACE(("DECRPM(%d) = %d\n", params[0], result)); 5397 } 5398 reply.a_type = ANSI_CSI; 5399 reply.a_pintro = '?'; 5400 reply.a_nparam = (ParmType) count; 5401 reply.a_inters = '$'; 5402 reply.a_final = 'y'; 5403 unparseseq(xw, &reply); 5404} 5405#endif /* OPT_DEC_RECTOPS */ 5406 5407char * 5408udk_lookup(XtermWidget xw, int keycode, int *len) 5409{ 5410 char *result = NULL; 5411 if (keycode >= 0 && keycode < MAX_UDK) { 5412 *len = xw->work.user_keys[keycode].len; 5413 result = xw->work.user_keys[keycode].str; 5414 TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result)); 5415 } else { 5416 TRACE(("udk_lookup(%d) = <null>\n", keycode)); 5417 } 5418 return result; 5419} 5420 5421#if OPT_REPORT_ICONS 5422void 5423report_icons(const char *fmt, ...) 5424{ 5425 if (resource.reportIcons) { 5426 va_list ap; 5427 va_start(ap, fmt); 5428 vfprintf(stdout, fmt, ap); 5429 va_end(ap); 5430#if OPT_TRACE 5431 va_start(ap, fmt); 5432 TraceVA(fmt, ap); 5433 va_end(ap); 5434#endif 5435 } 5436} 5437#endif 5438 5439#ifdef HAVE_LIBXPM 5440 5441#ifndef PIXMAP_ROOTDIR 5442#define PIXMAP_ROOTDIR "/usr/share/pixmaps/" 5443#endif 5444 5445typedef struct { 5446 const char *name; 5447 const char *const *data; 5448} XPM_DATA; 5449 5450static char * 5451x_find_icon(char **work, int *state, const char *filename, const char *suffix) 5452{ 5453 const char *prefix = PIXMAP_ROOTDIR; 5454 const char *larger = "_48x48"; 5455 char *result = 0; 5456 5457 if (*state >= 0) { 5458 if ((*state & 1) == 0) 5459 suffix = ""; 5460 if ((*state & 2) == 0) 5461 larger = ""; 5462 if ((*state & 4) == 0) { 5463 prefix = ""; 5464 } else if (!strncmp(filename, "/", (size_t) 1) || 5465 !strncmp(filename, "./", (size_t) 2) || 5466 !strncmp(filename, "../", (size_t) 3)) { 5467 *state = -1; 5468 } else if (*state >= 8) { 5469 *state = -1; 5470 } 5471 } 5472 5473 if (*state >= 0) { 5474 size_t length; 5475 5476 FreeAndNull(*work); 5477 length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) + 5478 strlen(suffix); 5479 if ((result = malloc(length)) != 0) { 5480 sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix); 5481 *work = result; 5482 } 5483 *state += 1; 5484 } 5485 TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result))); 5486 return result; 5487} 5488 5489#if OPT_BUILTIN_XPMS 5490 5491static const XPM_DATA * 5492built_in_xpm(const XPM_DATA * table, Cardinal length, const char *find) 5493{ 5494 const XPM_DATA *result = 0; 5495 if (!IsEmpty(find)) { 5496 Cardinal n; 5497 for (n = 0; n < length; ++n) { 5498 if (!x_strcasecmp(find, table[n].name)) { 5499 result = table + n; 5500 ReportIcons(("use builtin-icon %s\n", table[n].name)); 5501 break; 5502 } 5503 } 5504 5505 /* 5506 * As a fallback, check if the icon name matches without the lengths, 5507 * which are all _HHxWW format. 5508 */ 5509 if (result == 0) { 5510 const char *base = table[0].name; 5511 const char *last = strchr(base, '_'); 5512 if (last != 0 5513 && !x_strncasecmp(find, base, (unsigned) (last - base))) { 5514 result = table + length - 1; 5515 ReportIcons(("use builtin-icon %s\n", table[0].name)); 5516 } 5517 } 5518 } 5519 return result; 5520} 5521#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint) 5522#endif /* OPT_BUILTIN_XPMS */ 5523 5524typedef enum { 5525 eHintDefault = 0 /* use the largest builtin-icon */ 5526 ,eHintNone 5527 ,eHintSearch 5528} ICON_HINT; 5529#endif /* HAVE_LIBXPM */ 5530 5531int 5532getVisualDepth(XtermWidget xw) 5533{ 5534 int result = 0; 5535 5536 if (getVisualInfo(xw)) { 5537 result = xw->visInfo->depth; 5538 } 5539 return result; 5540} 5541 5542/* 5543 * WM_ICON_SIZE should be honored if possible. 5544 */ 5545void 5546xtermLoadIcon(XtermWidget xw, const char *icon_hint) 5547{ 5548#ifdef HAVE_LIBXPM 5549 Display *dpy = XtDisplay(xw); 5550 Pixmap myIcon = 0; 5551 Pixmap myMask = 0; 5552 char *workname = 0; 5553 ICON_HINT hint = eHintDefault; 5554#include <builtin_icons.h> 5555 5556 ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint))); 5557 if (!IsEmpty(icon_hint)) { 5558 if (!x_strcasecmp(icon_hint, "none")) { 5559 hint = eHintNone; 5560 } else { 5561 hint = eHintSearch; 5562 } 5563 } 5564 5565 if (hint == eHintSearch) { 5566 int state = 0; 5567 while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) { 5568 Pixmap resIcon = 0; 5569 Pixmap shapemask = 0; 5570 XpmAttributes attributes; 5571 struct stat sb; 5572 5573 attributes.depth = (unsigned) getVisualDepth(xw); 5574 attributes.valuemask = XpmDepth; 5575 5576 if (IsEmpty(workname) 5577 || lstat(workname, &sb) != 0 5578 || !S_ISREG(sb.st_mode)) { 5579 TRACE(("...failure (no such file)\n")); 5580 } else { 5581 int rc = XpmReadFileToPixmap(dpy, 5582 DefaultRootWindow(dpy), 5583 workname, 5584 &resIcon, 5585 &shapemask, 5586 &attributes); 5587 if (rc == XpmSuccess) { 5588 myIcon = resIcon; 5589 myMask = shapemask; 5590 TRACE(("...success\n")); 5591 ReportIcons(("found/loaded icon-file %s\n", workname)); 5592 break; 5593 } else { 5594 TRACE(("...failure (%s)\n", XpmGetErrorString(rc))); 5595 } 5596 } 5597 } 5598 } 5599 5600 /* 5601 * If no external file was found, look for the name in the built-in table. 5602 * If that fails, just use the biggest mini-icon. 5603 */ 5604 if (myIcon == 0 && hint != eHintNone) { 5605 char **data; 5606#if OPT_BUILTIN_XPMS 5607 const XPM_DATA *myData = 0; 5608 myData = BuiltInXPM(mini_xterm_xpms); 5609 if (myData == 0) 5610 myData = BuiltInXPM(filled_xterm_xpms); 5611 if (myData == 0) 5612 myData = BuiltInXPM(xterm_color_xpms); 5613 if (myData == 0) 5614 myData = BuiltInXPM(xterm_xpms); 5615 if (myData == 0) 5616 myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1]; 5617 data = (char **) myData->data; 5618#else 5619 data = (char **) &mini_xterm_48x48_xpm; 5620#endif 5621 if (XpmCreatePixmapFromData(dpy, 5622 DefaultRootWindow(dpy), 5623 data, 5624 &myIcon, &myMask, 0) == 0) { 5625 ReportIcons(("loaded built-in pixmap icon\n")); 5626 } else { 5627 myIcon = 0; 5628 myMask = 0; 5629 } 5630 } 5631 5632 if (myIcon != 0) { 5633 XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw)); 5634 if (!hints) 5635 hints = XAllocWMHints(); 5636 5637 if (hints) { 5638 hints->flags |= IconPixmapHint; 5639 hints->icon_pixmap = myIcon; 5640 if (myMask) { 5641 hints->flags |= IconMaskHint; 5642 hints->icon_mask = myMask; 5643 } 5644 5645 XSetWMHints(dpy, VShellWindow(xw), hints); 5646 XFree(hints); 5647 ReportIcons(("updated window-manager hints\n")); 5648 } 5649 } 5650 5651 free(workname); 5652 5653#else 5654 (void) xw; 5655 (void) icon_hint; 5656#endif 5657} 5658 5659void 5660ChangeGroup(XtermWidget xw, const char *attribute, char *value) 5661{ 5662 Arg args[1]; 5663 Boolean changed = True; 5664 Widget w = CURRENT_EMU(); 5665 Widget top = SHELL_OF(w); 5666 5667 char *my_attr = NULL; 5668 char *old_value = value; 5669#if OPT_WIDE_CHARS 5670 Boolean titleIsUTF8; 5671#endif 5672 5673 if (!AllowTitleOps(xw)) 5674 return; 5675 5676 /* 5677 * Ignore empty or too-long requests. 5678 */ 5679 if (value == 0 || strlen(value) > 1000) 5680 return; 5681 5682 if (IsTitleMode(xw, tmSetBase16)) { 5683 const char *temp; 5684 char *test; 5685 5686 /* this allocates a new string, if no error is detected */ 5687 value = x_decode_hex(value, &temp); 5688 if (value == 0 || *temp != '\0') { 5689 free(value); 5690 return; 5691 } 5692 for (test = value; *test != '\0'; ++test) { 5693 if (CharOf(*test) < 32) { 5694 *test = '\0'; 5695 break; 5696 } 5697 } 5698 } 5699#if OPT_WIDE_CHARS 5700 /* 5701 * By design, xterm uses the XtNtitle resource of the X Toolkit for setting 5702 * the WM_NAME property, rather than doing this directly. That relies on 5703 * the application to tell it if the format should be something other than 5704 * STRING, i.e., by setting the XtNtitleEncoding resource. 5705 * 5706 * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted). In X11R6, 5707 * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86 5708 * added UTF8_STRING (the documentation for that was discarded by an Xorg 5709 * developer, although the source-code provides this feature). 5710 * 5711 * Since X11R5, if the X11 library fails to store a text property as 5712 * STRING, it falls back to COMPOUND_TEXT. For best interoperability, we 5713 * prefer to use STRING if the data fits, or COMPOUND_TEXT. In either 5714 * case, limit the resulting characters to the printable ISO-8859-1 set. 5715 */ 5716 titleIsUTF8 = isValidUTF8((Char *) value); 5717 if (IsSetUtf8Title(xw) && titleIsUTF8) { 5718 char *testc = malloc(strlen(value) + 1); 5719 Char *nextc = (Char *) value; 5720 Boolean ok8bit = True; 5721 5722 if (testc != NULL) { 5723 /* 5724 * Check if the data fits in STRING. Along the way, replace 5725 * control characters. 5726 */ 5727 Char *lastc = (Char *) testc; 5728 while (*nextc != '\0') { 5729 unsigned ch; 5730 nextc = convertFromUTF8(nextc, &ch); 5731 if (ch > 255) { 5732 ok8bit = False; 5733 } else if (!IsLatin1(ch)) { 5734 ch = OnlyLatin1(ch); 5735 } 5736 *lastc++ = (Char) ch; 5737 } 5738 *lastc = '\0'; 5739 if (ok8bit) { 5740 TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n")); 5741 if (value != old_value) 5742 free(value); 5743 value = testc; 5744 titleIsUTF8 = False; 5745 } else { 5746 TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n" 5747 "\t%s\n", value)); 5748 free(testc); 5749 nextc = (Char *) value; 5750 while (*nextc != '\0') { 5751 unsigned ch; 5752 Char *skip = convertFromUTF8(nextc, &ch); 5753 if (iswcntrl((wint_t) ch)) { 5754 memset(nextc, BAD_ASCII, (size_t) (skip - nextc)); 5755 } 5756 nextc = skip; 5757 } 5758 } 5759 } 5760 } else 5761#endif 5762 { 5763 Char *c1 = (Char *) value; 5764 5765 TRACE(("ChangeGroup: assume ISO-8859-1\n")); 5766 for (c1 = (Char *) value; *c1 != '\0'; ++c1) { 5767 *c1 = (Char) OnlyLatin1(*c1); 5768 } 5769 } 5770 5771 my_attr = x_strdup(attribute); 5772 5773 ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value)); 5774 5775#if OPT_WIDE_CHARS 5776 /* 5777 * If we're running in UTF-8 mode, and have not been told that the 5778 * title string is in UTF-8, it is likely that non-ASCII text in the 5779 * string will be rejected because it is not printable in the current 5780 * locale. So we convert it to UTF-8, allowing the X library to 5781 * convert it back. 5782 */ 5783 TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT ")); 5784 if (xtermEnvUTF8() && !titleIsUTF8) { 5785 size_t limit = strlen(value); 5786 Char *c1 = (Char *) value; 5787 int n; 5788 5789 for (n = 0; c1[n] != '\0'; ++n) { 5790 if (c1[n] > 127) { 5791 Char *converted; 5792 if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) { 5793 Char *temp = converted; 5794 while (*c1 != 0) { 5795 temp = convertToUTF8(temp, *c1++); 5796 } 5797 *temp = 0; 5798 if (value != old_value) 5799 free(value); 5800 value = (char *) converted; 5801 ReportIcons(("...converted{%s}\n", value)); 5802 } 5803 break; 5804 } 5805 } 5806 } 5807#endif 5808 5809#if OPT_SAME_NAME 5810 /* If the attribute isn't going to change, then don't bother... */ 5811 if (resource.sameName) { 5812 char *buf = 0; 5813 XtSetArg(args[0], my_attr, &buf); 5814 XtGetValues(top, args, 1); 5815 TRACE(("...comparing{%s}\n", NonNull(buf))); 5816 if (buf != 0 && strcmp(value, buf) == 0) 5817 changed = False; 5818 } 5819#endif /* OPT_SAME_NAME */ 5820 5821 if (changed) { 5822 ReportIcons(("...updating %s\n", my_attr)); 5823 ReportIcons(("...value is %s\n", value)); 5824 XtSetArg(args[0], my_attr, value); 5825 XtSetValues(top, args, 1); 5826 } 5827#if OPT_WIDE_CHARS 5828 if (xtermEnvUTF8()) { 5829 Display *dpy = XtDisplay(xw); 5830 const char *propname = (!strcmp(my_attr, XtNtitle) 5831 ? "_NET_WM_NAME" 5832 : "_NET_WM_ICON_NAME"); 5833 Atom my_atom = XInternAtom(dpy, propname, False); 5834 5835 if (my_atom != None) { 5836 changed = True; 5837 5838 if (IsSetUtf8Title(xw)) { 5839#if OPT_SAME_NAME 5840 if (resource.sameName) { 5841 Atom actual_type; 5842 Atom requested_type = XA_UTF8_STRING(dpy); 5843 int actual_format = 0; 5844 long long_length = 1024; 5845 unsigned long nitems = 0; 5846 unsigned long bytes_after = 0; 5847 unsigned char *prop = 0; 5848 5849 if (xtermGetWinProp(dpy, 5850 VShellWindow(xw), 5851 my_atom, 5852 0L, 5853 long_length, 5854 requested_type, 5855 &actual_type, 5856 &actual_format, 5857 &nitems, 5858 &bytes_after, 5859 &prop) 5860 && actual_type == requested_type 5861 && actual_format == 8 5862 && prop != 0 5863 && nitems == strlen(value) 5864 && memcmp(value, prop, nitems) == 0) { 5865 changed = False; 5866 } 5867 } 5868#endif /* OPT_SAME_NAME */ 5869 if (changed) { 5870 ReportIcons(("...updating %s\n", propname)); 5871 ReportIcons(("...value is %s\n", value)); 5872 XChangeProperty(dpy, VShellWindow(xw), my_atom, 5873 XA_UTF8_STRING(dpy), 8, 5874 PropModeReplace, 5875 (Char *) value, 5876 (int) strlen(value)); 5877 } 5878 } else { 5879 ReportIcons(("...deleting %s\n", propname)); 5880 XDeleteProperty(dpy, VShellWindow(xw), my_atom); 5881 } 5882 } 5883 } 5884#endif 5885 if (value != old_value) { 5886 free(value); 5887 } 5888 free(my_attr); 5889 5890 return; 5891} 5892 5893void 5894ChangeIconName(XtermWidget xw, char *name) 5895{ 5896 if (name == 0) { 5897 name = emptyString; 5898 } 5899 if (!showZIconBeep(xw, name)) 5900 ChangeGroup(xw, XtNiconName, name); 5901} 5902 5903void 5904ChangeTitle(XtermWidget xw, char *name) 5905{ 5906 ChangeGroup(xw, XtNtitle, name); 5907} 5908 5909#define Strlen(s) strlen((const char *)(s)) 5910 5911void 5912ChangeXprop(char *buf) 5913{ 5914 Display *dpy = XtDisplay(toplevel); 5915 Window w = XtWindow(toplevel); 5916 XTextProperty text_prop; 5917 Atom aprop; 5918 Char *pchEndPropName = (Char *) strchr(buf, '='); 5919 5920 if (pchEndPropName) 5921 *pchEndPropName = '\0'; 5922 aprop = XInternAtom(dpy, buf, False); 5923 if (pchEndPropName == NULL) { 5924 /* no "=value" given, so delete the property */ 5925 XDeleteProperty(dpy, w, aprop); 5926 } else { 5927 text_prop.value = pchEndPropName + 1; 5928 text_prop.encoding = XA_STRING; 5929 text_prop.format = 8; 5930 text_prop.nitems = Strlen(text_prop.value); 5931 XSetTextProperty(dpy, w, &text_prop, aprop); 5932 } 5933} 5934 5935/***====================================================================***/ 5936 5937/* 5938 * This is part of ReverseVideo(). It reverses the data stored for the old 5939 * "dynamic" colors that might have been retrieved using OSC 10-18. 5940 */ 5941void 5942ReverseOldColors(XtermWidget xw) 5943{ 5944 ScrnColors *pOld = xw->work.oldColors; 5945 Pixel tmpPix; 5946 char *tmpName; 5947 5948 if (pOld) { 5949 /* change text cursor, if necessary */ 5950 if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { 5951 pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; 5952 if (pOld->names[TEXT_CURSOR]) { 5953 XtFree(xw->work.oldColors->names[TEXT_CURSOR]); 5954 pOld->names[TEXT_CURSOR] = NULL; 5955 } 5956 if (pOld->names[TEXT_BG]) { 5957 if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { 5958 pOld->names[TEXT_CURSOR] = tmpName; 5959 } 5960 } 5961 } 5962 5963 EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); 5964 EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); 5965 5966 EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); 5967 EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); 5968 5969#if OPT_TEK4014 5970 EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); 5971 EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); 5972#endif 5973 FreeMarkGCs(xw); 5974 } 5975 return; 5976} 5977 5978Bool 5979AllocateTermColor(XtermWidget xw, 5980 ScrnColors * pNew, 5981 int ndx, 5982 const char *name, 5983 Bool always) 5984{ 5985 Bool result = False; 5986 5987 if (always || AllowColorOps(xw, ecSetColor)) { 5988 XColor def; 5989 char *newName; 5990 5991 result = True; 5992 if (!x_strcasecmp(name, XtDefaultForeground)) { 5993 def.pixel = xw->old_foreground; 5994 } else if (!x_strcasecmp(name, XtDefaultBackground)) { 5995 def.pixel = xw->old_background; 5996 } else if (!xtermAllocColor(xw, &def, name)) { 5997 result = False; 5998 } 5999 6000 if (result 6001 && (newName = x_strdup(name)) != 0) { 6002 if (COLOR_DEFINED(pNew, ndx)) { 6003 free(pNew->names[ndx]); 6004 } 6005 SET_COLOR_VALUE(pNew, ndx, def.pixel); 6006 SET_COLOR_NAME(pNew, ndx, newName); 6007 TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n", 6008 ndx, newName, def.pixel)); 6009 } else { 6010 TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); 6011 result = False; 6012 } 6013 } 6014 return result; 6015} 6016/***====================================================================***/ 6017 6018/* ARGSUSED */ 6019void 6020Panic(const char *s GCC_UNUSED, int a GCC_UNUSED) 6021{ 6022 if_DEBUG({ 6023 xtermWarning(s, a); 6024 }); 6025} 6026 6027const char * 6028SysErrorMsg(int code) 6029{ 6030 static const char unknown[] = "unknown error"; 6031 const char *s = strerror(code); 6032 return s ? s : unknown; 6033} 6034 6035const char * 6036SysReasonMsg(int code) 6037{ 6038 /* *INDENT-OFF* */ 6039 static const struct { 6040 int code; 6041 const char *name; 6042 } table[] = { 6043 { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" }, 6044 { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" }, 6045 { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", }, 6046 { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", }, 6047 { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", }, 6048 { ERROR_PTSNAME, "spawn: ptsname() failed", }, 6049 { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", }, 6050 { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" }, 6051 { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" }, 6052 { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" }, 6053 { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" }, 6054 { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" }, 6055 { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" }, 6056 { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" }, 6057 { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" }, 6058 { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" }, 6059 { ERROR_INIGROUPS, "spawn: initgroups() failed" }, 6060 { ERROR_FORK, "spawn: fork() failed" }, 6061 { ERROR_EXEC, "spawn: exec() failed" }, 6062 { ERROR_PTYS, "get_pty: not enough ptys" }, 6063 { ERROR_PTY_EXEC, "waiting for initial map" }, 6064 { ERROR_SETUID, "spawn: setuid() failed" }, 6065 { ERROR_INIT, "spawn: can't initialize window" }, 6066 { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" }, 6067 { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" }, 6068 { ERROR_LUMALLOC, "luit: command-line malloc failed" }, 6069 { ERROR_SELECT, "in_put: select() failed" }, 6070 { ERROR_VINIT, "VTInit: can't initialize window" }, 6071 { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" }, 6072 { ERROR_TSELECT, "Tinput: select() failed" }, 6073 { ERROR_TINIT, "TekInit: can't initialize window" }, 6074 { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" }, 6075 { ERROR_LOGEXEC, "StartLog: exec() failed" }, 6076 { ERROR_XERROR, "xerror: XError event" }, 6077 { ERROR_XIOERROR, "xioerror: X I/O error" }, 6078 { ERROR_SCALLOC, "Alloc: calloc() failed on base" }, 6079 { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" }, 6080 { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" }, 6081 }; 6082 /* *INDENT-ON* */ 6083 6084 Cardinal n; 6085 const char *result = "?"; 6086 6087 for (n = 0; n < XtNumber(table); ++n) { 6088 if (code == table[n].code) { 6089 result = table[n].name; 6090 break; 6091 } 6092 } 6093 return result; 6094} 6095 6096void 6097SysError(int code) 6098{ 6099 int oerrno = errno; 6100 6101 fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno); 6102 fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); 6103 fprintf(stderr, "Reason: %s\n", SysReasonMsg(code)); 6104 6105 Cleanup(code); 6106} 6107 6108void 6109NormalExit(void) 6110{ 6111 static Bool cleaning; 6112 6113 /* 6114 * Process "-hold" and session cleanup only for a normal exit. 6115 */ 6116 if (cleaning) { 6117 hold_screen = 0; 6118 return; 6119 } 6120 6121 cleaning = True; 6122 need_cleanup = False; 6123 6124 if (hold_screen) { 6125 hold_screen = 2; 6126 while (hold_screen) { 6127 xtermFlushDbe(term); 6128 xevents(term); 6129 Sleep(EVENT_DELAY); 6130 } 6131 } 6132#if OPT_SESSION_MGT 6133 if (resource.sessionMgt) { 6134 XtVaSetValues(toplevel, 6135 XtNjoinSession, False, 6136 (void *) 0); 6137 } 6138#endif 6139 Cleanup(0); 6140} 6141 6142#if USE_DOUBLE_BUFFER 6143void 6144xtermFlushDbe(XtermWidget xw) 6145{ 6146 TScreen *screen = TScreenOf(xw); 6147 if (resource.buffered && screen->needSwap) { 6148 XdbeSwapInfo swap; 6149 swap.swap_window = VWindow(screen); 6150 swap.swap_action = XdbeCopied; 6151 XdbeSwapBuffers(XtDisplay(xw), &swap, 1); 6152 XFlush(XtDisplay(xw)); 6153 screen->needSwap = 0; 6154 ScrollBarDrawThumb(xw, 2); 6155 X_GETTIMEOFDAY(&screen->buffered_at); 6156 } 6157} 6158 6159void 6160xtermTimedDbe(XtermWidget xw) 6161{ 6162 if (resource.buffered) { 6163 TScreen *screen = TScreenOf(xw); 6164 struct timeval now; 6165 long elapsed; 6166 long limit = DbeMsecs(xw); 6167 6168 X_GETTIMEOFDAY(&now); 6169 if (screen->buffered_at.tv_sec) { 6170 elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec) 6171 + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L); 6172 } else { 6173 elapsed = limit; 6174 } 6175 if (elapsed >= limit) { 6176 xtermNeedSwap(xw, 1); 6177 xtermFlushDbe(xw); 6178 } 6179 } 6180} 6181#endif 6182 6183/* 6184 * cleanup by sending SIGHUP to client processes 6185 */ 6186void 6187Cleanup(int code) 6188{ 6189 TScreen *screen = TScreenOf(term); 6190 6191 TRACE(("Cleanup %d\n", code)); 6192 6193 if (screen->pid > 1) { 6194 (void) kill_process_group(screen->pid, SIGHUP); 6195 } 6196 Exit(code); 6197} 6198 6199#ifndef S_IXOTH 6200#define S_IXOTH 1 6201#endif 6202 6203Boolean 6204validProgram(const char *pathname) 6205{ 6206 Boolean result = False; 6207 struct stat sb; 6208 6209 if (!IsEmpty(pathname) 6210 && *pathname == '/' 6211 && strstr(pathname, "/..") == 0 6212 && stat(pathname, &sb) == 0 6213 && (sb.st_mode & S_IFMT) == S_IFREG 6214 && (sb.st_mode & S_IXOTH) != 0) { 6215 result = True; 6216 } 6217 return result; 6218} 6219 6220#ifndef VMS 6221#ifndef PATH_MAX 6222#define PATH_MAX 512 /* ... is not defined consistently in Xos.h */ 6223#endif 6224char * 6225xtermFindShell(char *leaf, Bool warning) 6226{ 6227 char *s0; 6228 char *s; 6229 char *d; 6230 char *tmp; 6231 char *result = leaf; 6232 Bool allocated = False; 6233 6234 TRACE(("xtermFindShell(%s)\n", leaf)); 6235 6236 if (!strncmp("./", result, (size_t) 2) 6237 || !strncmp("../", result, (size_t) 3)) { 6238 size_t need = PATH_MAX; 6239 size_t used = strlen(result) + 2; 6240 char *buffer = malloc(used + need); 6241 if (buffer != 0) { 6242 if (getcwd(buffer, need) != 0) { 6243 sprintf(buffer + strlen(buffer), "/%s", result); 6244 result = buffer; 6245 allocated = True; 6246 } else { 6247 free(buffer); 6248 } 6249 } 6250 } else if (*result != '\0' && strchr("+/-", *result) == 0) { 6251 /* find it in $PATH */ 6252 if ((s = s0 = x_getenv("PATH")) != 0) { 6253 if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) { 6254 Bool found = False; 6255 while (*s != '\0') { 6256 strcpy(tmp, s); 6257 for (d = tmp;; ++d) { 6258 if (*d == ':' || *d == '\0') { 6259 int skip = (*d != '\0'); 6260 *d = '/'; 6261 strcpy(d + 1, leaf); 6262 if (skip) 6263 ++d; 6264 s += (d - tmp); 6265 if (validProgram(tmp)) { 6266 result = x_strdup(tmp); 6267 found = True; 6268 allocated = True; 6269 } 6270 break; 6271 } 6272 } 6273 if (found) 6274 break; 6275 } 6276 free(tmp); 6277 } 6278 free(s0); 6279 } 6280 } 6281 TRACE(("...xtermFindShell(%s)\n", result)); 6282 if (!validProgram(result)) { 6283 if (warning) 6284 xtermWarning("No absolute path found for shell: %s\n", result); 6285 if (allocated) 6286 free(result); 6287 result = 0; 6288 } 6289 /* be consistent, so that caller can always free the result */ 6290 if (result != 0 && !allocated) 6291 result = x_strdup(result); 6292 return result; 6293} 6294#endif /* VMS */ 6295 6296#define ENV_HUNK(n) (unsigned) ((((n) + 1) | 31) + 1) 6297 6298/* 6299 * If we do not have unsetenv(), make consistent updates for environ[]. 6300 * This could happen on some older machines due to the uneven standardization 6301 * process for the two functions. 6302 * 6303 * That is, putenv() makes a copy of environ, and some implementations do not 6304 * update the environ pointer, so the fallback when unsetenv() is missing would 6305 * not work as intended. Likewise, the reverse could be true, i.e., unsetenv 6306 * could copy environ. 6307 */ 6308#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV) 6309#undef HAVE_PUTENV 6310#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV) 6311#undef HAVE_UNSETENV 6312#endif 6313 6314/* 6315 * copy the environment before Setenv'ing. 6316 */ 6317void 6318xtermCopyEnv(char **oldenv) 6319{ 6320#ifdef HAVE_PUTENV 6321 (void) oldenv; 6322#else 6323 unsigned size; 6324 char **newenv; 6325 6326 for (size = 0; oldenv[size] != NULL; size++) { 6327 ; 6328 } 6329 6330 newenv = TypeCallocN(char *, ENV_HUNK(size)); 6331 memmove(newenv, oldenv, size * sizeof(char *)); 6332 environ = newenv; 6333#endif 6334} 6335 6336#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV) 6337static int 6338findEnv(const char *var, int *lengthp) 6339{ 6340 char *test; 6341 int envindex = 0; 6342 size_t len = strlen(var); 6343 int found = -1; 6344 6345 TRACE(("findEnv(%s=..)\n", var)); 6346 6347 while ((test = environ[envindex]) != NULL) { 6348 if (strncmp(test, var, len) == 0 && test[len] == '=') { 6349 found = envindex; 6350 break; 6351 } 6352 envindex++; 6353 } 6354 *lengthp = envindex; 6355 return found; 6356} 6357#endif 6358 6359/* 6360 * sets the value of var to be arg in the Unix 4.2 BSD environment env. 6361 * Var should end with '=' (bindings are of the form "var=value"). 6362 * This procedure assumes the memory for the first level of environ 6363 * was allocated using calloc, with enough extra room at the end so not 6364 * to have to do a realloc(). 6365 */ 6366void 6367xtermSetenv(const char *var, const char *value) 6368{ 6369 if (value != 0) { 6370#ifdef HAVE_PUTENV 6371 char *both = malloc(2 + strlen(var) + strlen(value)); 6372 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 6373 if (both) { 6374 sprintf(both, "%s=%s", var, value); 6375 putenv(both); 6376 } 6377#else 6378 size_t len = strlen(var); 6379 int envindex; 6380 int found = findEnv(var, &envindex); 6381 6382 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 6383 6384 if (found < 0) { 6385 unsigned need = ENV_HUNK(envindex + 1); 6386 unsigned have = ENV_HUNK(envindex); 6387 6388 if (need > have) { 6389 char **newenv; 6390 newenv = TypeMallocN(char *, need); 6391 if (newenv == 0) { 6392 xtermWarning("Cannot increase environment\n"); 6393 return; 6394 } 6395 memmove(newenv, environ, have * sizeof(*newenv)); 6396 free(environ); 6397 environ = newenv; 6398 } 6399 6400 found = envindex; 6401 environ[found + 1] = NULL; 6402 environ = environ; 6403 } 6404 6405 environ[found] = malloc(2 + len + strlen(value)); 6406 if (environ[found] == 0) { 6407 xtermWarning("Cannot allocate environment %s\n", var); 6408 return; 6409 } 6410 sprintf(environ[found], "%s=%s", var, value); 6411#endif 6412 } 6413} 6414 6415void 6416xtermUnsetenv(const char *var) 6417{ 6418 TRACE(("xtermUnsetenv(%s)\n", var)); 6419#ifdef HAVE_UNSETENV 6420 unsetenv(var); 6421#else 6422 { 6423 int ignore; 6424 int item = findEnv(var, &ignore); 6425 if (item >= 0) { 6426 while ((environ[item] = environ[item + 1]) != 0) { 6427 ++item; 6428 } 6429 } 6430 } 6431#endif 6432} 6433 6434/*ARGSUSED*/ 6435int 6436xerror(Display *d, XErrorEvent *ev) 6437{ 6438 xtermWarning("warning, error event received:\n"); 6439 TRACE_X_ERR(d, ev); 6440 (void) XmuPrintDefaultErrorMessage(d, ev, stderr); 6441 Exit(ERROR_XERROR); 6442 return 0; /* appease the compiler */ 6443} 6444 6445void 6446ice_error(IceConn iceConn) 6447{ 6448 (void) iceConn; 6449 6450 xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n", 6451 (long) getpid(), errno); 6452 6453 Exit(ERROR_ICEERROR); 6454} 6455 6456/*ARGSUSED*/ 6457int 6458xioerror(Display *dpy) 6459{ 6460 int the_error = errno; 6461 6462 xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", 6463 the_error, SysErrorMsg(the_error), 6464 DisplayString(dpy)); 6465 6466 Exit(ERROR_XIOERROR); 6467 return 0; /* appease the compiler */ 6468} 6469 6470void 6471xt_error(String message) 6472{ 6473 xtermWarning("Xt error: %s\n", message); 6474 6475 /* 6476 * Check for the obvious - Xt does a poor job of reporting this. 6477 */ 6478 if (x_getenv("DISPLAY") == 0) { 6479 xtermWarning("DISPLAY is not set\n"); 6480 } 6481 exit(1); 6482} 6483 6484int 6485XStrCmp(char *s1, char *s2) 6486{ 6487 if (s1 && s2) 6488 return (strcmp(s1, s2)); 6489 if (s1 && *s1) 6490 return (1); 6491 if (s2 && *s2) 6492 return (-1); 6493 return (0); 6494} 6495 6496#if OPT_TEK4014 6497static void 6498withdraw_window(Display *dpy, Window w, int scr) 6499{ 6500 TRACE(("withdraw_window %#lx\n", (long) w)); 6501 (void) XmuUpdateMapHints(dpy, w, NULL); 6502 XWithdrawWindow(dpy, w, scr); 6503 return; 6504} 6505#endif 6506 6507void 6508set_vt_visibility(Bool on) 6509{ 6510 XtermWidget xw = term; 6511 TScreen *screen = TScreenOf(xw); 6512 6513 TRACE(("set_vt_visibility(%d)\n", on)); 6514 if (on) { 6515 if (!screen->Vshow && xw) { 6516 VTInit(xw); 6517 XtMapWidget(XtParent(xw)); 6518#if OPT_TOOLBAR 6519 /* we need both of these during initialization */ 6520 XtMapWidget(SHELL_OF(xw)); 6521 ShowToolbar(resource.toolBar); 6522#endif 6523 screen->Vshow = True; 6524 } 6525 } 6526#if OPT_TEK4014 6527 else { 6528 if (screen->Vshow && xw) { 6529 withdraw_window(XtDisplay(xw), 6530 VShellWindow(xw), 6531 XScreenNumberOfScreen(XtScreen(xw))); 6532 screen->Vshow = False; 6533 } 6534 } 6535 set_vthide_sensitivity(); 6536 set_tekhide_sensitivity(); 6537 update_vttekmode(); 6538 update_tekshow(); 6539 update_vtshow(); 6540#endif 6541 return; 6542} 6543 6544#if OPT_TEK4014 6545void 6546set_tek_visibility(Bool on) 6547{ 6548 XtermWidget xw = term; 6549 6550 TRACE(("set_tek_visibility(%d)\n", on)); 6551 6552 if (on) { 6553 if (!TEK4014_SHOWN(xw)) { 6554 if (tekWidget == 0) { 6555 TekInit(); /* will exit on failure */ 6556 } 6557 if (tekWidget != 0) { 6558 Widget tekParent = SHELL_OF(tekWidget); 6559 XtRealizeWidget(tekParent); 6560 XtMapWidget(XtParent(tekWidget)); 6561#if OPT_TOOLBAR 6562 /* we need both of these during initialization */ 6563 XtMapWidget(tekParent); 6564 XtMapWidget(tekWidget); 6565#endif 6566 XtOverrideTranslations(tekParent, 6567 XtParseTranslationTable 6568 ("<Message>WM_PROTOCOLS: DeleteWindow()")); 6569 (void) XSetWMProtocols(XtDisplay(tekParent), 6570 XtWindow(tekParent), 6571 &wm_delete_window, 1); 6572 TEK4014_SHOWN(xw) = True; 6573 } 6574 } 6575 } else { 6576 if (TEK4014_SHOWN(xw) && tekWidget) { 6577 withdraw_window(XtDisplay(tekWidget), 6578 TShellWindow, 6579 XScreenNumberOfScreen(XtScreen(tekWidget))); 6580 TEK4014_SHOWN(xw) = False; 6581 } 6582 } 6583 set_tekhide_sensitivity(); 6584 set_vthide_sensitivity(); 6585 update_vtshow(); 6586 update_tekshow(); 6587 update_vttekmode(); 6588 return; 6589} 6590 6591void 6592end_tek_mode(void) 6593{ 6594 XtermWidget xw = term; 6595 6596 if (TEK4014_ACTIVE(xw)) { 6597 FlushLog(xw); 6598 TEK4014_ACTIVE(xw) = False; 6599 xtermSetWinSize(xw); 6600 longjmp(Tekend, 1); 6601 } 6602 return; 6603} 6604 6605void 6606end_vt_mode(void) 6607{ 6608 XtermWidget xw = term; 6609 6610 if (!TEK4014_ACTIVE(xw)) { 6611 FlushLog(xw); 6612 set_tek_visibility(True); 6613 TEK4014_ACTIVE(xw) = True; 6614 TekSetWinSize(tekWidget); 6615 longjmp(VTend, 1); 6616 } 6617 return; 6618} 6619 6620void 6621switch_modes(Bool tovt) /* if true, then become vt mode */ 6622{ 6623 if (tovt) { 6624 if (tekRefreshList) 6625 TekRefresh(tekWidget); 6626 end_tek_mode(); /* WARNING: this does a longjmp... */ 6627 } else { 6628 end_vt_mode(); /* WARNING: this does a longjmp... */ 6629 } 6630} 6631 6632void 6633hide_vt_window(void) 6634{ 6635 set_vt_visibility(False); 6636 if (!TEK4014_ACTIVE(term)) 6637 switch_modes(False); /* switch to tek mode */ 6638} 6639 6640void 6641hide_tek_window(void) 6642{ 6643 set_tek_visibility(False); 6644 tekRefreshList = (TekLink *) 0; 6645 if (TEK4014_ACTIVE(term)) 6646 switch_modes(True); /* does longjmp to vt mode */ 6647} 6648#endif /* OPT_TEK4014 */ 6649 6650static const char * 6651skip_punct(const char *s) 6652{ 6653 while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { 6654 ++s; 6655 } 6656 return s; 6657} 6658 6659static int 6660cmp_options(const void *a, const void *b) 6661{ 6662 const char *s1 = skip_punct(((const OptionHelp *) a)->opt); 6663 const char *s2 = skip_punct(((const OptionHelp *) b)->opt); 6664 return strcmp(s1, s2); 6665} 6666 6667static int 6668cmp_resources(const void *a, const void *b) 6669{ 6670 return strcmp(((const XrmOptionDescRec *) a)->option, 6671 ((const XrmOptionDescRec *) b)->option); 6672} 6673 6674XrmOptionDescRec * 6675sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) 6676{ 6677 static XrmOptionDescRec *res_array = 0; 6678 6679#ifdef NO_LEAKS 6680 if (descs == 0) { 6681 FreeAndNull(res_array); 6682 } else 6683#endif 6684 if (res_array == 0) { 6685 Cardinal j; 6686 6687 /* make a sorted index to 'resources' */ 6688 res_array = TypeCallocN(XrmOptionDescRec, res_count); 6689 if (res_array != 0) { 6690 for (j = 0; j < res_count; j++) 6691 res_array[j] = descs[j]; 6692 qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources); 6693 } 6694 } 6695 return res_array; 6696} 6697 6698/* 6699 * The first time this is called, construct sorted index to the main program's 6700 * list of options, taking into account the on/off options which will be 6701 * compressed into one token. It's a lot simpler to do it this way than 6702 * maintain the list in sorted form with lots of ifdef's. 6703 */ 6704OptionHelp * 6705sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) 6706{ 6707 static OptionHelp *opt_array = 0; 6708 6709#ifdef NO_LEAKS 6710 if (descs == 0 && opt_array != 0) { 6711 sortedOptDescs(descs, numDescs); 6712 FreeAndNull(opt_array); 6713 return 0; 6714 } else if (options == 0 || descs == 0) { 6715 return 0; 6716 } 6717#endif 6718 6719 if (opt_array == 0) { 6720 size_t opt_count, j; 6721#if OPT_TRACE 6722 Cardinal k; 6723 XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); 6724 int code; 6725 const char *mesg; 6726#else 6727 (void) descs; 6728 (void) numDescs; 6729#endif 6730 6731 /* count 'options' and make a sorted index to it */ 6732 for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { 6733 ; 6734 } 6735 opt_array = TypeCallocN(OptionHelp, opt_count + 1); 6736 for (j = 0; j < opt_count; j++) 6737 opt_array[j] = options[j]; 6738 qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); 6739 6740 /* supply the "turn on/off" strings if needed */ 6741#if OPT_TRACE 6742 for (j = 0; j < opt_count; j++) { 6743 if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) { 6744 char temp[80]; 6745 const char *name = opt_array[j].opt + 3; 6746 for (k = 0; k < numDescs; ++k) { 6747 const char *value = res_array[k].value; 6748 if (res_array[k].option[0] == '-') { 6749 code = -1; 6750 } else if (res_array[k].option[0] == '+') { 6751 code = 1; 6752 } else { 6753 code = 0; 6754 } 6755 sprintf(temp, "%.*s", 6756 (int) sizeof(temp) - 2, 6757 opt_array[j].desc); 6758 if (x_strindex(temp, "inhibit") != 0) 6759 code = -code; 6760 if (code != 0 6761 && res_array[k].value != 0 6762 && !strcmp(name, res_array[k].option + 1)) { 6763 if (((code < 0) && !strcmp(value, "on")) 6764 || ((code > 0) && !strcmp(value, "off")) 6765 || ((code > 0) && !strcmp(value, "0"))) { 6766 mesg = "turn on/off"; 6767 } else { 6768 mesg = "turn off/on"; 6769 } 6770 if (strncmp(mesg, opt_array[j].desc, strlen(mesg))) { 6771 if (strncmp(opt_array[j].desc, "turn ", (size_t) 5)) { 6772 char *s = malloc(strlen(mesg) 6773 + strlen(opt_array[j].desc) 6774 + 2); 6775 if (s != 0) { 6776 sprintf(s, "%s %s", mesg, opt_array[j].desc); 6777 opt_array[j].desc = s; 6778 } 6779 } else { 6780 TRACE(("OOPS ")); 6781 } 6782 } 6783 TRACE(("%s: %s %s: %s (%s)\n", 6784 mesg, 6785 res_array[k].option, 6786 res_array[k].value, 6787 opt_array[j].opt, 6788 opt_array[j].desc)); 6789 break; 6790 } 6791 } 6792 } 6793 } 6794#endif 6795 } 6796 return opt_array; 6797} 6798 6799/* 6800 * Report the character-type locale that xterm was started in. 6801 */ 6802String 6803xtermEnvLocale(void) 6804{ 6805 static String result; 6806 6807 if (result == 0) { 6808 if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) { 6809 result = x_strdup("C"); 6810 } else { 6811 result = x_strdup(result); 6812 } 6813 TRACE(("xtermEnvLocale ->%s\n", result)); 6814 } 6815 return result; 6816} 6817 6818char * 6819xtermEnvEncoding(void) 6820{ 6821 static char *result; 6822 6823 if (result == 0) { 6824#ifdef HAVE_LANGINFO_CODESET 6825 result = nl_langinfo(CODESET); 6826#else 6827 const char *locale = xtermEnvLocale(); 6828 if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { 6829 result = x_strdup("ASCII"); 6830 } else { 6831 result = x_strdup("ISO-8859-1"); 6832 } 6833#endif 6834 TRACE(("xtermEnvEncoding ->%s\n", result)); 6835 } 6836 return result; 6837} 6838 6839#if OPT_WIDE_CHARS 6840/* 6841 * Tell whether xterm was started in a locale that uses UTF-8 encoding for 6842 * characters. That environment is inherited by subprocesses and used in 6843 * various library calls. 6844 */ 6845Bool 6846xtermEnvUTF8(void) 6847{ 6848 static Bool init = False; 6849 static Bool result = False; 6850 6851 if (!init) { 6852 init = True; 6853#ifdef HAVE_LANGINFO_CODESET 6854 result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); 6855#else 6856 { 6857 char *locale = x_strdup(xtermEnvLocale()); 6858 int n; 6859 for (n = 0; locale[n] != 0; ++n) { 6860 locale[n] = x_toupper(locale[n]); 6861 } 6862 if (strstr(locale, "UTF-8") != 0) 6863 result = True; 6864 else if (strstr(locale, "UTF8") != 0) 6865 result = True; 6866 free(locale); 6867 } 6868#endif 6869 TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); 6870 } 6871 return result; 6872} 6873#endif /* OPT_WIDE_CHARS */ 6874 6875/* 6876 * Check if the current widget, or any parent, is the VT100 "xterm" widget. 6877 */ 6878XtermWidget 6879getXtermWidget(Widget w) 6880{ 6881 XtermWidget xw; 6882 6883 if (w == 0) { 6884 xw = (XtermWidget) CURRENT_EMU(); 6885 if (!IsXtermWidget(xw)) { 6886 xw = 0; 6887 } 6888 } else if (IsXtermWidget(w)) { 6889 xw = (XtermWidget) w; 6890 } else { 6891 xw = getXtermWidget(XtParent(w)); 6892 } 6893 TRACE2(("getXtermWidget %p -> %p\n", w, xw)); 6894 return xw; 6895} 6896 6897#if OPT_SESSION_MGT 6898 6899#if OPT_TRACE 6900static void 6901trace_1_SM(const char *tag, String name) 6902{ 6903 Arg args[1]; 6904 char *buf = 0; 6905 6906 XtSetArg(args[0], name, &buf); 6907 XtGetValues(toplevel, args, 1); 6908 6909 if (strstr(name, "Path") || strstr(name, "Directory")) { 6910 TRACE(("%s %s: %s\n", tag, name, NonNull(buf))); 6911 } else if (strstr(name, "Command")) { 6912 if (buf != NULL) { 6913 char **vec = (char **) (void *) buf; 6914 int n; 6915 TRACE(("%s %s:\n", tag, name)); 6916 for (n = 0; vec[n] != NULL; ++n) { 6917 TRACE((" arg[%d] = %s\n", n, vec[n])); 6918 } 6919 } else { 6920 TRACE(("%s %s: %p\n", tag, name, buf)); 6921 } 6922 } else { 6923 TRACE(("%s %s: %p\n", tag, name, buf)); 6924 } 6925} 6926 6927static void 6928trace_SM_props(void) 6929{ 6930 /* *INDENT-OFF* */ 6931 static struct { String app, cls; } table[] = { 6932 { XtNcurrentDirectory, XtCCurrentDirectory }, 6933 { XtNdieCallback, XtNdiscardCommand }, 6934 { XtCDiscardCommand, XtNenvironment }, 6935 { XtCEnvironment, XtNinteractCallback }, 6936 { XtNjoinSession, XtCJoinSession }, 6937 { XtNprogramPath, XtCProgramPath }, 6938 { XtNresignCommand, XtCResignCommand }, 6939 { XtNrestartCommand, XtCRestartCommand }, 6940 { XtNrestartStyle, XtCRestartStyle }, 6941 { XtNsaveCallback, XtNsaveCompleteCallback }, 6942 { XtNsessionID, XtCSessionID }, 6943 { XtNshutdownCommand, XtCShutdownCommand }, 6944 }; 6945 /* *INDENT-ON* */ 6946 Cardinal n; 6947 TRACE(("Session properties:\n")); 6948 for (n = 0; n < XtNumber(table); ++n) { 6949 trace_1_SM("app", table[n].app); 6950 trace_1_SM("cls", table[n].cls); 6951 } 6952} 6953#define TRACE_SM_PROPS() trace_SM_props() 6954#else 6955#define TRACE_SM_PROPS() /* nothing */ 6956#endif 6957 6958static void 6959die_callback(Widget w GCC_UNUSED, 6960 XtPointer client_data GCC_UNUSED, 6961 XtPointer call_data GCC_UNUSED) 6962{ 6963 TRACE(("die_callback client=%p, call=%p\n", 6964 (void *) client_data, 6965 (void *) call_data)); 6966 TRACE_SM_PROPS(); 6967 NormalExit(); 6968} 6969 6970static void 6971save_callback(Widget w GCC_UNUSED, 6972 XtPointer client_data GCC_UNUSED, 6973 XtPointer call_data) 6974{ 6975 XtCheckpointToken token = (XtCheckpointToken) call_data; 6976 TRACE(("save_callback:\n")); 6977 TRACE(("... save_type <-%d\n", token->save_type)); 6978 TRACE(("... interact_style <-%d\n", token->interact_style)); 6979 TRACE(("... shutdown <-%s\n", BtoS(token->shutdown))); 6980 TRACE(("... fast <-%s\n", BtoS(token->fast))); 6981 TRACE(("... cancel_shutdown <-%s\n", BtoS(token->cancel_shutdown))); 6982 TRACE(("... phase <-%d\n", token->phase)); 6983 TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type)); 6984 TRACE(("... request_cancel ->%s\n", BtoS(token->request_cancel))); 6985 TRACE(("... request_next_phase ->%s\n", BtoS(token->request_next_phase))); 6986 TRACE(("... save_success ->%s\n", BtoS(token->save_success))); 6987 xtermUpdateRestartCommand(term); 6988 /* we have nothing more to save */ 6989 token->save_success = True; 6990} 6991 6992static void 6993icewatch(IceConn iceConn, 6994 IcePointer clientData GCC_UNUSED, 6995 Bool opening, 6996 IcePointer * watchData GCC_UNUSED) 6997{ 6998 if (opening) { 6999 ice_fd = IceConnectionNumber(iceConn); 7000 TRACE(("got IceConnectionNumber %d\n", ice_fd)); 7001 } else { 7002 ice_fd = -1; 7003 TRACE(("reset IceConnectionNumber\n")); 7004 } 7005} 7006 7007void 7008xtermOpenSession(void) 7009{ 7010 if (resource.sessionMgt) { 7011 TRACE(("Enabling session-management callbacks\n")); 7012 XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL); 7013 XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL); 7014 7015 TRACE_SM_PROPS(); 7016 } 7017} 7018 7019void 7020xtermCloseSession(void) 7021{ 7022 IceRemoveConnectionWatch(icewatch, NULL); 7023} 7024 7025typedef enum { 7026 B_ARG = 0, 7027 I_ARG, 7028 D_ARG, 7029 S_ARG 7030} ParamType; 7031 7032#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) } 7033#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) } 7034#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) } 7035#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) } 7036 7037typedef struct { 7038 const char name[30]; 7039 ParamType type; 7040 Cardinal offset; 7041} FontParams; 7042 7043/* *INDENT-OFF* */ 7044static const FontParams fontParams[] = { 7045 Iarg(XtNinitialFont, screen.menu_font_number), /* "-fc" */ 7046 Barg(XtNallowBoldFonts, screen.allowBoldFonts), /* menu */ 7047#if OPT_BOX_CHARS 7048 Barg(XtNforceBoxChars, screen.force_box_chars), /* "-fbx" */ 7049 Barg(XtNforcePackedFont, screen.force_packed), /* menu */ 7050#endif 7051#if OPT_DEC_CHRSET 7052 Barg(XtNfontDoublesize, screen.font_doublesize), /* menu */ 7053#endif 7054#if OPT_WIDE_CHARS 7055 Barg(XtNutf8Fonts, screen.utf8_fonts), /* menu */ 7056#endif 7057#if OPT_RENDERFONT 7058 Darg(XtNfaceSize, misc.face_size[0]), /* "-fs" */ 7059 Sarg(XtNfaceName, misc.default_xft.f_n), /* "-fa" */ 7060 Sarg(XtNrenderFont, misc.render_font_s), /* (resource) */ 7061#endif 7062}; 7063/* *INDENT-ON* */ 7064 7065#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2) 7066#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset) 7067 7068/* 7069 * If no widget is given, no value is used. 7070 */ 7071static char * 7072formatFontParam(char *result, XtermWidget xw, const FontParams * parameter) 7073{ 7074 sprintf(result, "%s*%s:", ProgramName, parameter->name); 7075 if (xw != None) { 7076 char *next = result + strlen(result); 7077 switch (parameter->type) { 7078 case B_ARG: 7079 sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset) 7080 ? "true" 7081 : "false"); 7082 break; 7083 case I_ARG: 7084 sprintf(next, "%d", TypedPtr(int)); 7085 break; 7086 case D_ARG: 7087 sprintf(next, "%.1f", TypedPtr(float)); 7088 break; 7089 case S_ARG: 7090 strcpy(next, TypedPtr(char *)); 7091#if OPT_RENDERFONT 7092 if (!strcmp(parameter->name, XtNfaceName)) { 7093 if (IsEmpty(next) 7094 && xw->work.render_font) { 7095 strcpy(next, DEFFACENAME_AUTO); 7096 } 7097 } else if (!strcmp(parameter->name, XtNrenderFont)) { 7098 if (xw->work.render_font == erDefault 7099 && IsEmpty(xw->misc.default_xft.f_n)) { 7100 strcpy(next, "DefaultOff"); 7101 } 7102 } 7103#endif 7104 break; 7105 } 7106 } 7107 return result; 7108} 7109 7110#if OPT_TRACE 7111static void 7112dumpFontParams(XtermWidget xw) 7113{ 7114 char buffer[1024]; 7115 Cardinal n; 7116 7117 TRACE(("FontParams:\n")); 7118 for (n = 0; n < XtNumber(fontParams); ++n) { 7119 TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n))); 7120 } 7121} 7122#else 7123#define dumpFontParams(xw) /* nothing */ 7124#endif 7125 7126static Boolean 7127findFontParams(int argc, char **argv) 7128{ 7129 Boolean result = False; 7130 7131 if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) { 7132 int n; 7133 7134 for (n = 0; n < RESTART_PARAMS; ++n) { 7135 int my_index = argc - restart_params - n - 1; 7136 int my_param = (RESTART_PARAMS - n - 1) / 2; 7137 char *actual = argv[my_index]; 7138 char expect[1024]; 7139 Boolean value = (Boolean) ((n % 2) == 0); 7140 7141 result = False; 7142 TRACE(("...index: %d\n", my_index)); 7143 TRACE(("...param: %d\n", my_param)); 7144 TRACE(("...actual %s\n", actual)); 7145 if (IsEmpty(actual)) 7146 break; 7147 7148 if (value) { 7149 formatFontParam(expect, None, fontParams + my_param); 7150 } else { 7151 strcpy(expect, "-xrm"); 7152 } 7153 7154 TRACE(("...expect %s\n", expect)); 7155 7156 if (value) { 7157 if (strlen(expect) >= strlen(actual)) 7158 break; 7159 if (strncmp(expect, actual, strlen(expect))) 7160 break; 7161 } else { 7162 if (strcmp(actual, expect)) 7163 break; 7164 } 7165 TRACE(("fixme/ok:%d\n", n)); 7166 result = True; 7167 } 7168 TRACE(("findFontParams: %s (tested %d of %d parameters)\n", 7169 BtoS(result), n + 1, RESTART_PARAMS)); 7170 } 7171 return result; 7172} 7173 7174static int 7175insertFontParams(XtermWidget xw, int *targetp, Bool first) 7176{ 7177 int changed = 0; 7178 int n; 7179 int target = *targetp; 7180 char buffer[1024]; 7181 const char *option = "-xrm"; 7182 7183 for (n = 0; n < (int) XtNumber(fontParams); ++n) { 7184 formatFontParam(buffer, xw, fontParams + n); 7185 TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer)); 7186 if (restart_command[target] == NULL) 7187 restart_command[target] = x_strdup(option); 7188 ++target; 7189 if (first) { 7190 restart_command[target] = x_strdup(buffer); 7191 ++changed; 7192 } else if (restart_command[target] == NULL 7193 || strcmp(restart_command[target], buffer)) { 7194 free(restart_command[target]); 7195 restart_command[target] = x_strdup(buffer); 7196 ++changed; 7197 } 7198 ++target; 7199 } 7200 *targetp = target; 7201 return changed; 7202} 7203 7204void 7205xtermUpdateRestartCommand(XtermWidget xw) 7206{ 7207 if (resource.sessionMgt) { 7208 Arg args[1]; 7209 char **argv = 0; 7210 7211 XtSetArg(args[0], XtNrestartCommand, &argv); 7212 XtGetValues(toplevel, args, 1); 7213 if (argv != NULL) { 7214 static int my_params = 0; 7215 7216 int changes = 0; 7217 Boolean first = False; 7218 int argc; 7219 int want; 7220 int source, target; 7221 7222 TRACE(("xtermUpdateRestartCommand\n")); 7223 dumpFontParams(xw); 7224 for (argc = 0; argv[argc] != NULL; ++argc) { 7225 TRACE((" arg[%d] = %s\n", argc, argv[argc])); 7226 ; 7227 } 7228 want = argc - (restart_params + RESTART_PARAMS); 7229 7230 TRACE((" argc: %d\n", argc)); 7231 TRACE((" restart_params: %d\n", restart_params)); 7232 TRACE((" want to insert: %d\n", want)); 7233 7234 /* 7235 * If we already have the font-choice option, do not add it again. 7236 */ 7237 if (findFontParams(argc, argv)) { 7238 my_params = (want); 7239 } else { 7240 first = True; 7241 my_params = (argc - restart_params); 7242 } 7243 TRACE((" my_params: %d\n", my_params)); 7244 7245 if (my_params > argc) { 7246 TRACE((" re-allocate restartCommand\n")); 7247 FreeAndNull(restart_command); 7248 } 7249 7250 if (restart_command == NULL) { 7251 int need = argc + RESTART_PARAMS + 1; 7252 7253 restart_command = TypeCallocN(char *, need); 7254 7255 TRACE(("..inserting font-parameters\n")); 7256 for (source = target = 0; source < argc; ++source) { 7257 if (source == my_params) { 7258 changes += insertFontParams(xw, &target, first); 7259 if (!first) { 7260 source += (RESTART_PARAMS - 1); 7261 continue; 7262 } 7263 } 7264 if (argv[source] == NULL) 7265 break; 7266 restart_command[target++] = x_strdup(argv[source]); 7267 } 7268 restart_command[target] = NULL; 7269 } else { 7270 TRACE(("..replacing font-parameters\n")); 7271 target = my_params; 7272 changes += insertFontParams(xw, &target, first); 7273 } 7274 if (changes) { 7275 TRACE(("..%d parameters changed\n", changes)); 7276 XtSetArg(args[0], XtNrestartCommand, restart_command); 7277 XtSetValues(toplevel, args, 1); 7278 } else { 7279 TRACE(("..NO parameters changed\n")); 7280 } 7281 } 7282 TRACE_SM_PROPS(); 7283 } 7284} 7285#endif /* OPT_SESSION_MGT */ 7286 7287Widget 7288xtermOpenApplication(XtAppContext * app_context_return, 7289 String my_class, 7290 XrmOptionDescRec * options, 7291 Cardinal num_options, 7292 int *argc_in_out, 7293 char **argv_in_out, 7294 String *fallback_resources, 7295 WidgetClass widget_class, 7296 ArgList args, 7297 Cardinal num_args) 7298{ 7299 Widget result; 7300 7301 XtSetErrorHandler(xt_error); 7302#if OPT_SESSION_MGT 7303 result = XtOpenApplication(app_context_return, 7304 my_class, 7305 options, 7306 num_options, 7307 argc_in_out, 7308 argv_in_out, 7309 fallback_resources, 7310 widget_class, 7311 args, 7312 num_args); 7313 IceAddConnectionWatch(icewatch, NULL); 7314#else 7315 (void) widget_class; 7316 (void) args; 7317 (void) num_args; 7318 result = XtAppInitialize(app_context_return, 7319 my_class, 7320 options, 7321 num_options, 7322 argc_in_out, 7323 argv_in_out, 7324 fallback_resources, 7325 NULL, 0); 7326#endif /* OPT_SESSION_MGT */ 7327 XtSetErrorHandler(NULL); 7328 7329 return result; 7330} 7331 7332/* 7333 * Some calls to XGetAtom() will fail, and we don't want to stop. So we use 7334 * our own error-handler. 7335 */ 7336/* ARGSUSED */ 7337int 7338ignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED) 7339{ 7340 return 1; 7341} 7342 7343static int x11_errors; 7344 7345static int 7346catch_x11_error(Display *display, XErrorEvent *error_event) 7347{ 7348 (void) display; 7349 (void) error_event; 7350 ++x11_errors; 7351 return 0; 7352} 7353 7354Boolean 7355xtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs) 7356{ 7357 Boolean result = False; 7358 Status code; 7359 7360 memset(attrs, 0, sizeof(*attrs)); 7361 if (win != None) { 7362 XErrorHandler save = XSetErrorHandler(catch_x11_error); 7363 x11_errors = 0; 7364 code = XGetWindowAttributes(dpy, win, attrs); 7365 XSetErrorHandler(save); 7366 result = (Boolean) ((code != 0) && !x11_errors); 7367 if (result) { 7368 TRACE_WIN_ATTRS(attrs); 7369 } else { 7370 xtermWarning("invalid window-id %ld\n", (long) win); 7371 } 7372 } 7373 return result; 7374} 7375 7376Boolean 7377xtermGetWinProp(Display *display, 7378 Window win, 7379 Atom property, 7380 long long_offset, 7381 long long_length, 7382 Atom req_type, 7383 Atom *actual_type_return, 7384 int *actual_format_return, 7385 unsigned long *nitems_return, 7386 unsigned long *bytes_after_return, 7387 unsigned char **prop_return) 7388{ 7389 Boolean result = False; 7390 7391 if (win != None) { 7392 XErrorHandler save = XSetErrorHandler(catch_x11_error); 7393 x11_errors = 0; 7394 if (XGetWindowProperty(display, 7395 win, 7396 property, 7397 long_offset, 7398 long_length, 7399 False, 7400 req_type, 7401 actual_type_return, 7402 actual_format_return, 7403 nitems_return, 7404 bytes_after_return, 7405 prop_return) == Success 7406 && x11_errors == 0) { 7407 result = True; 7408 } 7409 XSetErrorHandler(save); 7410 } 7411 return result; 7412} 7413 7414void 7415xtermEmbedWindow(Window winToEmbedInto) 7416{ 7417 Display *dpy = XtDisplay(toplevel); 7418 XWindowAttributes attrs; 7419 7420 TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto)); 7421 if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) { 7422 XtermWidget xw = term; 7423 TScreen *screen = TScreenOf(xw); 7424 7425 XtRealizeWidget(toplevel); 7426 7427 TRACE(("...reparenting toplevel %#lx into %#lx\n", 7428 XtWindow(toplevel), 7429 winToEmbedInto)); 7430 XReparentWindow(dpy, 7431 XtWindow(toplevel), 7432 winToEmbedInto, 0, 0); 7433 7434 screen->embed_high = (Dimension) attrs.height; 7435 screen->embed_wide = (Dimension) attrs.width; 7436 } 7437} 7438 7439void 7440free_string(String value) 7441{ 7442 free((void *) value); 7443} 7444 7445/* Set tty's idea of window size, using the given file descriptor 'fd'. */ 7446int 7447update_winsize(int fd, int rows, int cols, int height, int width) 7448{ 7449 int code = -1; 7450#ifdef TTYSIZE_STRUCT 7451 static int last_rows = -1; 7452 static int last_cols = -1; 7453 static int last_high = -1; 7454 static int last_wide = -1; 7455 7456 TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n", 7457 last_rows, last_cols, last_high, last_wide, 7458 rows, cols, height, width)); 7459 7460 if (rows != last_rows 7461 || cols != last_cols 7462 || last_high != height 7463 || last_wide != width) { 7464 TTYSIZE_STRUCT ts; 7465 7466 last_rows = rows; 7467 last_cols = cols; 7468 last_high = height; 7469 last_wide = width; 7470 setup_winsize(ts, rows, cols, height, width); 7471 TRACE_RC(code, SET_TTYSIZE(fd, ts)); 7472 trace_winsize(ts, "from SET_TTYSIZE"); 7473 } 7474#endif 7475 7476 (void) rows; 7477 (void) cols; 7478 (void) height; 7479 (void) width; 7480 7481 return code; 7482} 7483 7484/* 7485 * Update stty settings to match the values returned by dtterm window 7486 * manipulation 18 and 19. 7487 */ 7488void 7489xtermSetWinSize(XtermWidget xw) 7490{ 7491#if OPT_TEK4014 7492 if (!TEK4014_ACTIVE(xw)) 7493#endif 7494 if (XtIsRealized((Widget) xw)) { 7495 TScreen *screen = TScreenOf(xw); 7496 7497 TRACE(("xtermSetWinSize\n")); 7498 update_winsize(screen->respond, 7499 MaxRows(screen), 7500 MaxCols(screen), 7501 Height(screen), 7502 Width(screen)); 7503 } 7504} 7505 7506#if OPT_XTERM_SGR 7507 7508#if OPT_TRACE 7509static char * 7510traceIFlags(IFlags flags) 7511{ 7512 static char result[1000]; 7513 result[0] = '\0'; 7514#define DATA(name) if (flags & name) { strcat(result, " " #name); } 7515 DATA(INVERSE); 7516 DATA(UNDERLINE); 7517 DATA(BOLD); 7518 DATA(BLINK); 7519 DATA(INVISIBLE); 7520 DATA(BG_COLOR); 7521 DATA(FG_COLOR); 7522 7523#if OPT_WIDE_ATTRS 7524 DATA(ATR_FAINT); 7525 DATA(ATR_ITALIC); 7526 DATA(ATR_STRIKEOUT); 7527 DATA(ATR_DBL_UNDER); 7528 DATA(ATR_DIRECT_FG); 7529 DATA(ATR_DIRECT_BG); 7530#endif 7531#undef DATA 7532 return result; 7533} 7534 7535static char * 7536traceIStack(unsigned flags) 7537{ 7538 static char result[1000]; 7539 result[0] = '\0'; 7540#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); } 7541 DATA(INVERSE); 7542 DATA(UNDERLINE); 7543 DATA(BOLD); 7544 DATA(BLINK); 7545 DATA(INVISIBLE); 7546#if OPT_ISO_COLORS 7547 DATA(BG_COLOR); 7548 DATA(FG_COLOR); 7549#endif 7550 7551#if OPT_WIDE_ATTRS 7552 DATA(ATR_FAINT); 7553 DATA(ATR_ITALIC); 7554 DATA(ATR_STRIKEOUT); 7555 DATA(ATR_DBL_UNDER); 7556 /* direct-colors are a special case of ISO-colors (see above) */ 7557#endif 7558#undef DATA 7559 return result; 7560} 7561#endif 7562 7563void 7564xtermPushSGR(XtermWidget xw, int value) 7565{ 7566 SavedSGR *s = &(xw->saved_sgr); 7567 7568 TRACE(("xtermPushSGR %d mask %#x %s\n", 7569 s->used + 1, (unsigned) value, traceIStack((unsigned) value))); 7570 7571 if (s->used < MAX_SAVED_SGR) { 7572 s->stack[s->used].mask = (IFlags) value; 7573#define PUSH_FLAG(name) \ 7574 s->stack[s->used].name = xw->name;\ 7575 TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name))) 7576#define PUSH_DATA(name) \ 7577 s->stack[s->used].name = xw->name;\ 7578 TRACE(("...may pop %s %d\n", #name, xw->name)) 7579 PUSH_FLAG(flags); 7580#if OPT_ISO_COLORS 7581 PUSH_DATA(sgr_foreground); 7582 PUSH_DATA(sgr_background); 7583 PUSH_DATA(sgr_38_xcolors); 7584#endif 7585 } 7586 s->used++; 7587} 7588 7589#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits) 7590 7591void 7592xtermReportSGR(XtermWidget xw, XTermRect *value) 7593{ 7594 TScreen *screen = TScreenOf(xw); 7595 char reply[BUFSIZ]; 7596 CellData working; 7597 int row, col; 7598 Boolean first = True; 7599 7600 TRACE(("xtermReportSGR %d,%d - %d,%d\n", 7601 value->top, value->left, 7602 value->bottom, value->right)); 7603 7604 memset(&working, 0, sizeof(working)); 7605 for (row = value->top - 1; row < value->bottom; ++row) { 7606 LineData *ld = getLineData(screen, row); 7607 if (ld == 0) 7608 continue; 7609 for (col = value->left - 1; col < value->right; ++col) { 7610 if (first) { 7611 first = False; 7612 saveCellData(screen, &working, 0, ld, NULL, col); 7613 } 7614 working.attribs &= ld->attribs[col]; 7615#if OPT_ISO_COLORS 7616 if (working.attribs & FG_COLOR 7617 && GetCellColorFG(working.color) 7618 != GetCellColorFG(ld->color[col])) { 7619 IAttrClr(working.attribs, FG_COLOR); 7620 } 7621 if (working.attribs & BG_COLOR 7622 && GetCellColorBG(working.color) 7623 != GetCellColorBG(ld->color[col])) { 7624 IAttrClr(working.attribs, BG_COLOR); 7625 } 7626#endif 7627 } 7628 } 7629 xtermFormatSGR(xw, reply, 7630 working.attribs, 7631 GetCellColorFG(working.color), 7632 GetCellColorBG(working.color)); 7633 unparseputc1(xw, ANSI_CSI); 7634 unparseputs(xw, reply); 7635 unparseputc(xw, 'm'); 7636 unparse_end(xw); 7637} 7638 7639void 7640xtermPopSGR(XtermWidget xw) 7641{ 7642 SavedSGR *s = &(xw->saved_sgr); 7643 7644 TRACE(("xtermPopSGR %d\n", s->used)); 7645 7646 if (s->used > 0) { 7647 if (s->used-- <= MAX_SAVED_SGR) { 7648 IFlags mask = s->stack[s->used].mask; 7649 Boolean changed = False; 7650 7651 TRACE(("...mask %#x %s\n", mask, traceIStack(mask))); 7652 TRACE(("...old: %s\n", traceIFlags(xw->flags))); 7653 TRACE(("...new: %s\n", traceIFlags(s->stack[s->used].flags))); 7654#define POP_FLAG(name) \ 7655 if (xBIT(ps##name - 1) & mask) { \ 7656 if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \ 7657 changed = True; \ 7658 UIntClr(xw->flags, name); \ 7659 UIntSet(xw->flags, (s->stack[s->used].flags & name)); \ 7660 TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \ 7661 } \ 7662 } 7663#define POP_FLAG2(name,part) \ 7664 if (xBIT(ps##name - 1) & mask) { \ 7665 if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \ 7666 changed = True; \ 7667 UIntClr(xw->flags, part); \ 7668 UIntSet(xw->flags, (s->stack[s->used].flags & part)); \ 7669 TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \ 7670 } \ 7671 } 7672#define POP_DATA(name,value) \ 7673 if (xBIT(ps##name - 1) & mask) { \ 7674 Bool always = False; \ 7675 if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \ 7676 always = changed = True; \ 7677 UIntClr(xw->flags, name); \ 7678 UIntSet(xw->flags, (s->stack[s->used].flags & name)); \ 7679 TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \ 7680 } \ 7681 if (always || (xw->value != s->stack[s->used].value)) { \ 7682 TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \ 7683 xw->value = s->stack[s->used].value; \ 7684 changed = True; \ 7685 } \ 7686 } 7687 POP_FLAG(BOLD); 7688 POP_FLAG(UNDERLINE); 7689 POP_FLAG(BLINK); 7690 POP_FLAG(INVERSE); 7691 POP_FLAG(INVISIBLE); 7692#if OPT_WIDE_ATTRS 7693 if (xBIT(psATR_ITALIC - 1) & mask) { 7694 xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags); 7695 } 7696 POP_FLAG(ATR_ITALIC); 7697 POP_FLAG(ATR_FAINT); 7698 POP_FLAG(ATR_STRIKEOUT); 7699 POP_FLAG(ATR_DBL_UNDER); 7700#endif 7701#if OPT_ISO_COLORS 7702 POP_DATA(FG_COLOR, sgr_foreground); 7703 POP_DATA(BG_COLOR, sgr_background); 7704 POP_DATA(BG_COLOR, sgr_38_xcolors); 7705#if OPT_DIRECT_COLOR 7706 POP_FLAG2(FG_COLOR, ATR_DIRECT_FG); 7707 POP_FLAG2(BG_COLOR, ATR_DIRECT_BG); 7708#endif 7709 if (changed) { 7710 setExtendedColors(xw); 7711 } 7712#else 7713 (void) changed; 7714#endif 7715 } 7716#if OPT_ISO_COLORS 7717 TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n", 7718 traceIFlags(xw->flags), 7719 xw->sgr_foreground, 7720 xw->sgr_background, 7721 xw->sgr_38_xcolors ? " (SGR 38)" : "")); 7722#else 7723 TRACE(("xtermP -> flags%s\n", 7724 traceIFlags(xw->flags))); 7725#endif 7726 } 7727} 7728 7729#if OPT_ISO_COLORS 7730static ColorSlot * 7731allocColorSlot(XtermWidget xw, int slot) 7732{ 7733 SavedColors *s = &(xw->saved_colors); 7734 ColorSlot *result = NULL; 7735 7736 if (slot >= 0 && slot < MAX_SAVED_SGR) { 7737 if (s->palettes[slot] == NULL) { 7738 s->palettes[slot] = (ColorSlot *) calloc((size_t) 1, 7739 sizeof(ColorSlot) 7740 + (sizeof(ColorRes) 7741 * MAXCOLORS)); 7742 } 7743 result = s->palettes[slot]; 7744 } 7745 return result; 7746} 7747 7748static void 7749popOldColors(XtermWidget xw, ScrnColors * source) 7750{ 7751 Boolean changed = False; 7752 ScrnColors *target = xw->work.oldColors; 7753 7754 if (source->which != target->which) { 7755 changed = True; 7756 } else { 7757 int n; 7758 for (n = 0; n < NCOLORS; ++n) { 7759 if (COLOR_DEFINED(source, n)) { 7760 if (COLOR_DEFINED(target, n)) { 7761 if (source->colors[n] != target->colors[n]) { 7762 changed = True; 7763 break; 7764 } 7765 } else { 7766 changed = True; 7767 break; 7768 } 7769 } else if (COLOR_DEFINED(target, n)) { 7770 changed = True; 7771 break; 7772 } 7773 } 7774 } 7775 if (changed) { 7776 ChangeColors(xw, source); 7777 UpdateOldColors(xw, source); 7778 } 7779} 7780#endif /* OPT_ISO_COLORS */ 7781 7782#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False) 7783#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes)) 7784 7785/* 7786 * By default, a "push" increments the stack after copying to the current 7787 * slot. But a specific target allows one to copy into a specific slot. 7788 */ 7789void 7790xtermPushColors(XtermWidget xw, int value) 7791{ 7792#if OPT_ISO_COLORS 7793 SavedColors *s = &(xw->saved_colors); 7794 int pushed = s->used; 7795 int actual = (value <= 0) ? pushed : (value - 1); 7796 7797 TRACE(("xtermPushColors %d:%d\n", actual, pushed)); 7798 if (actual < MAX_SAVED_SGR && actual >= 0) { 7799 TScreen *screen = TScreenOf(xw); 7800 ColorSlot *palette; 7801 7802 if ((palette = allocColorSlot(xw, actual)) != NULL) { 7803 GetColors(xw, &(palette->base)); 7804 CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS); 7805 if (value < 0) { 7806 s->used++; 7807 if (s->last < s->used) 7808 s->last = s->used; 7809 } else { 7810 s->used = value; 7811 } 7812 } 7813 } 7814#else 7815 (void) xw; 7816 (void) value; 7817#endif 7818} 7819 7820void 7821xtermPopColors(XtermWidget xw, int value) 7822{ 7823#if OPT_ISO_COLORS 7824 SavedColors *s = &(xw->saved_colors); 7825 int popped = (s->used - 1); 7826 int actual = (value <= 0) ? popped : (value - 1); 7827 7828 TRACE(("xtermPopColors %d:%d\n", actual, popped)); 7829 if (actual < MAX_SAVED_SGR && actual >= 0) { 7830 TScreen *screen = TScreenOf(xw); 7831 ColorSlot *palette; 7832 7833 if ((palette = s->palettes[actual]) != NULL) { 7834 Boolean changed = DiffColorSlot(screen->Acolors, 7835 palette->ansi, 7836 MAXCOLORS); 7837 7838 GetOldColors(xw); 7839 popOldColors(xw, &(palette->base)); 7840 CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS); 7841 s->used = actual; 7842 if (changed) 7843 xtermRepaint(xw); 7844 } 7845 } 7846#else 7847 (void) xw; 7848 (void) value; 7849#endif 7850} 7851 7852void 7853xtermReportColors(XtermWidget xw) 7854{ 7855 ANSI reply; 7856 SavedColors *s = &(xw->saved_colors); 7857 7858 memset(&reply, 0, sizeof(reply)); 7859 reply.a_type = ANSI_CSI; 7860 reply.a_pintro = '?'; 7861 reply.a_param[reply.a_nparam++] = (ParmType) s->used; 7862 reply.a_param[reply.a_nparam++] = (ParmType) s->last; 7863 reply.a_inters = '#'; 7864 reply.a_final = 'Q'; 7865 unparseseq(xw, &reply); 7866} 7867#endif /* OPT_XTERM_SGR */ 7868