misc.c revision 50027b5b
1/* $XTermId: misc.c,v 1.1015 2022/02/18 09:08:10 tom Exp $ */ 2 3/* 4 * Copyright 1999-2021,2022 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 if (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 XFree(prop_return); 1861 } 1862 } 1863 } 1864 TRACE(("...window %#lx is%s iconified\n", 1865 target, 1866 result ? "" : " not")); 1867 return result; 1868} 1869 1870#if OPT_MAXIMIZE 1871/*ARGSUSED*/ 1872void 1873HandleDeIconify(Widget w, 1874 XEvent *event GCC_UNUSED, 1875 String *params GCC_UNUSED, 1876 Cardinal *nparams GCC_UNUSED) 1877{ 1878 XtermWidget xw; 1879 1880 if ((xw = getXtermWidget(w)) != 0) { 1881 xtermDeiconify(xw); 1882 } 1883} 1884 1885/*ARGSUSED*/ 1886void 1887HandleIconify(Widget w, 1888 XEvent *event GCC_UNUSED, 1889 String *params GCC_UNUSED, 1890 Cardinal *nparams GCC_UNUSED) 1891{ 1892 XtermWidget xw; 1893 1894 if ((xw = getXtermWidget(w)) != 0) { 1895 xtermIconify(xw); 1896 } 1897} 1898 1899int 1900QueryMaximize(XtermWidget xw, unsigned *width, unsigned *height) 1901{ 1902 TScreen *screen = TScreenOf(xw); 1903 XSizeHints hints; 1904 long supp = 0; 1905 Window root_win; 1906 int root_x = -1; /* saved co-ordinates */ 1907 int root_y = -1; 1908 unsigned root_border; 1909 unsigned root_depth; 1910 int code; 1911 1912 if (XGetGeometry(screen->display, 1913 RootWindowOfScreen(XtScreen(xw)), 1914 &root_win, 1915 &root_x, 1916 &root_y, 1917 width, 1918 height, 1919 &root_border, 1920 &root_depth)) { 1921 TRACE(("QueryMaximize: XGetGeometry position %d,%d size %d,%d border %d\n", 1922 root_x, 1923 root_y, 1924 *width, 1925 *height, 1926 root_border)); 1927 1928 *width -= (root_border * 2); 1929 *height -= (root_border * 2); 1930 1931 hints.flags = PMaxSize; 1932 if (XGetWMNormalHints(screen->display, 1933 VShellWindow(xw), 1934 &hints, 1935 &supp) 1936 && (hints.flags & PMaxSize) != 0) { 1937 1938 TRACE(("QueryMaximize: WM hints max_w %#x max_h %#x\n", 1939 hints.max_width, 1940 hints.max_height)); 1941 1942 if ((unsigned) hints.max_width < *width) 1943 *width = (unsigned) hints.max_width; 1944 if ((unsigned) hints.max_height < *height) 1945 *height = (unsigned) hints.max_height; 1946 } 1947 code = 1; 1948 } else { 1949 *width = 0; 1950 *height = 0; 1951 code = 0; 1952 } 1953 return code; 1954} 1955 1956void 1957RequestMaximize(XtermWidget xw, int maximize) 1958{ 1959 TScreen *screen = TScreenOf(xw); 1960 XWindowAttributes wm_attrs, vshell_attrs; 1961 unsigned root_width = 0, root_height = 0; 1962 Boolean success = False; 1963 1964 TRACE(("RequestMaximize %d:%s\n", 1965 maximize, 1966 (maximize 1967 ? "maximize" 1968 : "restore"))); 1969 1970 /* 1971 * Before any maximize, ensure that we can capture the current screensize 1972 * as well as the estimated root-window size. 1973 */ 1974 if (maximize 1975 && QueryMaximize(xw, &root_width, &root_height) 1976 && xtermGetWinAttrs(screen->display, 1977 WMFrameWindow(xw), 1978 &wm_attrs) 1979 && xtermGetWinAttrs(screen->display, 1980 VShellWindow(xw), 1981 &vshell_attrs)) { 1982 1983 if (screen->restore_data != True 1984 || screen->restore_width != root_width 1985 || screen->restore_height != root_height) { 1986 screen->restore_data = True; 1987 screen->restore_x = wm_attrs.x; 1988 screen->restore_y = wm_attrs.y; 1989 screen->restore_width = (unsigned) vshell_attrs.width; 1990 screen->restore_height = (unsigned) vshell_attrs.height; 1991 TRACE(("RequestMaximize: save window position %d,%d size %d,%d\n", 1992 screen->restore_x, 1993 screen->restore_y, 1994 screen->restore_width, 1995 screen->restore_height)); 1996 } 1997 1998 /* subtract wm decoration dimensions */ 1999 root_width -= (unsigned) (wm_attrs.width - vshell_attrs.width); 2000 root_height -= (unsigned) (wm_attrs.height - vshell_attrs.height); 2001 success = True; 2002 } else if (screen->restore_data) { 2003 success = True; 2004 maximize = 0; 2005 } 2006 2007 if (success) { 2008 switch (maximize) { 2009 case 3: 2010 FullScreen(xw, 3); /* depends on EWMH */ 2011 break; 2012 case 2: 2013 FullScreen(xw, 2); /* depends on EWMH */ 2014 break; 2015 case 1: 2016 FullScreen(xw, 0); /* overrides any EWMH hint */ 2017 TRACE(("XMoveResizeWindow(Maximize): position %d,%d size %d,%d\n", 2018 0, 2019 0, 2020 root_width, 2021 root_height)); 2022 XMoveResizeWindow(screen->display, VShellWindow(xw), 2023 0, /* x */ 2024 0, /* y */ 2025 root_width, 2026 root_height); 2027 break; 2028 2029 default: 2030 FullScreen(xw, 0); /* reset any EWMH hint */ 2031 if (screen->restore_data) { 2032 screen->restore_data = False; 2033 2034 TRACE(("XMoveResizeWindow(Restore): position %d,%d size %d,%d\n", 2035 screen->restore_x, 2036 screen->restore_y, 2037 screen->restore_width, 2038 screen->restore_height)); 2039 2040 XMoveResizeWindow(screen->display, 2041 VShellWindow(xw), 2042 screen->restore_x, 2043 screen->restore_y, 2044 screen->restore_width, 2045 screen->restore_height); 2046 } 2047 break; 2048 } 2049 } 2050} 2051 2052/*ARGSUSED*/ 2053void 2054HandleMaximize(Widget w, 2055 XEvent *event GCC_UNUSED, 2056 String *params GCC_UNUSED, 2057 Cardinal *nparams GCC_UNUSED) 2058{ 2059 XtermWidget xw; 2060 2061 if ((xw = getXtermWidget(w)) != 0) { 2062 RequestMaximize(xw, 1); 2063 } 2064} 2065 2066/*ARGSUSED*/ 2067void 2068HandleRestoreSize(Widget w, 2069 XEvent *event GCC_UNUSED, 2070 String *params GCC_UNUSED, 2071 Cardinal *nparams GCC_UNUSED) 2072{ 2073 XtermWidget xw; 2074 2075 if ((xw = getXtermWidget(w)) != 0) { 2076 RequestMaximize(xw, 0); 2077 } 2078} 2079#endif /* OPT_MAXIMIZE */ 2080 2081void 2082Redraw(void) 2083{ 2084 XtermWidget xw = term; 2085 TScreen *screen = TScreenOf(xw); 2086 XExposeEvent event; 2087 2088 TRACE(("Redraw\n")); 2089 2090 event.type = Expose; 2091 event.display = screen->display; 2092 event.x = 0; 2093 event.y = 0; 2094 event.count = 0; 2095 2096 if (VWindow(screen)) { 2097 event.window = VWindow(screen); 2098 event.width = xw->core.width; 2099 event.height = xw->core.height; 2100 (*xw->core.widget_class->core_class.expose) ((Widget) xw, 2101 (XEvent *) &event, 2102 NULL); 2103 if (ScrollbarWidth(screen)) { 2104 (screen->scrollWidget->core.widget_class->core_class.expose) 2105 (screen->scrollWidget, (XEvent *) &event, NULL); 2106 } 2107 } 2108#if OPT_TEK4014 2109 if (TEK4014_SHOWN(xw)) { 2110 TekScreen *tekscr = TekScreenOf(tekWidget); 2111 event.window = TWindow(tekscr); 2112 event.width = tekWidget->core.width; 2113 event.height = tekWidget->core.height; 2114 TekExpose((Widget) tekWidget, (XEvent *) &event, NULL); 2115 } 2116#endif 2117} 2118 2119#ifdef VMS 2120#define TIMESTAMP_FMT "%s%d-%02d-%02d-%02d-%02d-%02d" 2121#else 2122#define TIMESTAMP_FMT "%s%d-%02d-%02d.%02d:%02d:%02d" 2123#endif 2124 2125void 2126timestamp_filename(char *dst, const char *src) 2127{ 2128 time_t tstamp; 2129 struct tm *tstruct; 2130 2131 tstamp = time((time_t *) 0); 2132 tstruct = localtime(&tstamp); 2133 sprintf(dst, TIMESTAMP_FMT, 2134 src, 2135 (int) tstruct->tm_year + 1900, 2136 tstruct->tm_mon + 1, 2137 tstruct->tm_mday, 2138 tstruct->tm_hour, 2139 tstruct->tm_min, 2140 tstruct->tm_sec); 2141} 2142 2143FILE * 2144create_printfile(XtermWidget xw, const char *suffix) 2145{ 2146 TScreen *screen = TScreenOf(xw); 2147 char fname[1024]; 2148 int fd; 2149 FILE *fp; 2150 2151#ifdef VMS 2152 sprintf(fname, "sys$scratch:xterm%s", suffix); 2153#elif defined(HAVE_STRFTIME) 2154 { 2155 char format[1024]; 2156 time_t now; 2157 struct tm *ltm; 2158 2159 now = time((time_t *) 0); 2160 ltm = localtime(&now); 2161 2162 sprintf(format, "xterm%s%s", FMT_TIMESTAMP, suffix); 2163 if (strftime(fname, sizeof fname, format, ltm) == 0) { 2164 sprintf(fname, "xterm%s", suffix); 2165 } 2166 } 2167#else 2168 sprintf(fname, "xterm%s", suffix); 2169#endif 2170 fd = open_userfile(screen->uid, screen->gid, fname, False); 2171 fp = (fd >= 0) ? fdopen(fd, "wb") : NULL; 2172 return fp; 2173} 2174 2175int 2176open_userfile(uid_t uid, gid_t gid, char *path, Bool append) 2177{ 2178 int fd; 2179 struct stat sb; 2180 2181#ifdef VMS 2182 if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { 2183 int the_error = errno; 2184 xtermWarning("cannot open %s: %d:%s\n", 2185 path, 2186 the_error, 2187 SysErrorMsg(the_error)); 2188 return -1; 2189 } 2190 chown(path, uid, gid); 2191#else 2192 if ((access(path, F_OK) != 0 && (errno != ENOENT)) 2193 || (creat_as(uid, gid, append, path, 0644) <= 0) 2194 || ((fd = open(path, O_WRONLY | O_APPEND)) < 0)) { 2195 int the_error = errno; 2196 xtermWarning("cannot open %s: %d:%s\n", 2197 path, 2198 the_error, 2199 SysErrorMsg(the_error)); 2200 return -1; 2201 } 2202#endif 2203 2204 /* 2205 * Doublecheck that the user really owns the file that we've opened before 2206 * we do any damage, and that it is not world-writable. 2207 */ 2208 if (fstat(fd, &sb) < 0 2209 || sb.st_uid != uid 2210 || (sb.st_mode & 022) != 0) { 2211 xtermWarning("you do not own %s\n", path); 2212 close(fd); 2213 return -1; 2214 } 2215 return fd; 2216} 2217 2218#ifndef VMS 2219/* 2220 * Create a file only if we could with the permissions of the real user id. 2221 * We could emulate this with careful use of access() and following 2222 * symbolic links, but that is messy and has race conditions. 2223 * Forking is messy, too, but we can't count on setreuid() or saved set-uids 2224 * being available. 2225 * 2226 * Note: When called for user logging, we have ensured that the real and 2227 * effective user ids are the same, so this remains as a convenience function 2228 * for the debug logs. 2229 * 2230 * Returns 2231 * 1 if we can proceed to open the file in relative safety, 2232 * -1 on error, e.g., cannot fork 2233 * 0 otherwise. 2234 */ 2235int 2236creat_as(uid_t uid, gid_t gid, Bool append, char *pathname, unsigned mode) 2237{ 2238 int fd; 2239 pid_t pid; 2240 int retval = 0; 2241 int childstat = 0; 2242#ifndef HAVE_WAITPID 2243 int waited; 2244 void (*chldfunc) (int); 2245 2246 chldfunc = signal(SIGCHLD, SIG_DFL); 2247#endif /* HAVE_WAITPID */ 2248 2249 TRACE(("creat_as(uid=%d/%d, gid=%d/%d, append=%d, pathname=%s, mode=%#o)\n", 2250 (int) uid, (int) geteuid(), 2251 (int) gid, (int) getegid(), 2252 append, 2253 pathname, 2254 mode)); 2255 2256 if (uid == geteuid() && gid == getegid()) { 2257 fd = open(pathname, 2258 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 2259 mode); 2260 if (fd >= 0) 2261 close(fd); 2262 return (fd >= 0); 2263 } 2264 2265 pid = fork(); 2266 switch (pid) { 2267 case 0: /* child */ 2268 if (setgid(gid) == -1 2269 || setuid(uid) == -1) { 2270 /* we cannot report an error here via stderr, just quit */ 2271 retval = 1; 2272 } else { 2273 fd = open(pathname, 2274 O_WRONLY | O_CREAT | (append ? O_APPEND : O_EXCL), 2275 mode); 2276 if (fd >= 0) { 2277 close(fd); 2278 retval = 0; 2279 } else { 2280 retval = 1; 2281 } 2282 } 2283 _exit(retval); 2284 /* NOTREACHED */ 2285 case -1: /* error */ 2286 return retval; 2287 default: /* parent */ 2288#ifdef HAVE_WAITPID 2289 while (waitpid(pid, &childstat, 0) < 0) { 2290#ifdef EINTR 2291 if (errno == EINTR) 2292 continue; 2293#endif /* EINTR */ 2294#ifdef ERESTARTSYS 2295 if (errno == ERESTARTSYS) 2296 continue; 2297#endif /* ERESTARTSYS */ 2298 break; 2299 } 2300#else /* HAVE_WAITPID */ 2301 waited = wait(&childstat); 2302 signal(SIGCHLD, chldfunc); 2303 /* 2304 Since we had the signal handler uninstalled for a while, 2305 we might have missed the termination of our screen child. 2306 If we can check for this possibility without hanging, do so. 2307 */ 2308 do 2309 if (waited == TScreenOf(term)->pid) 2310 NormalExit(); 2311 while ((waited = nonblocking_wait()) > 0) ; 2312#endif /* HAVE_WAITPID */ 2313#ifndef WIFEXITED 2314#define WIFEXITED(status) ((status & 0xff) != 0) 2315#endif 2316 if (WIFEXITED(childstat)) 2317 retval = 1; 2318 return retval; 2319 } 2320} 2321#endif /* !VMS */ 2322 2323int 2324xtermResetIds(TScreen *screen) 2325{ 2326 int result = 0; 2327 if (setgid(screen->gid) == -1) { 2328 xtermWarning("unable to reset group-id\n"); 2329 result = -1; 2330 } 2331 if (setuid(screen->uid) == -1) { 2332 xtermWarning("unable to reset user-id\n"); 2333 result = -1; 2334 } 2335 return result; 2336} 2337 2338#ifdef ALLOWLOGGING 2339 2340/* 2341 * Logging is a security hole, since it allows a setuid program to write 2342 * arbitrary data to an arbitrary file. So it is disabled by default. 2343 */ 2344 2345#ifdef ALLOWLOGFILEEXEC 2346static void 2347handle_SIGPIPE(int sig GCC_UNUSED) 2348{ 2349 XtermWidget xw = term; 2350 TScreen *screen = TScreenOf(xw); 2351 2352 DEBUG_MSG("handle:logpipe\n"); 2353#ifdef SYSV 2354 (void) signal(SIGPIPE, SIG_IGN); 2355#endif /* SYSV */ 2356 if (screen->logging) 2357 CloseLog(xw); 2358} 2359 2360/* 2361 * Open a command to pipe log data to it. 2362 * Warning, enabling this "feature" allows arbitrary programs 2363 * to be run. If ALLOWLOGFILECHANGES is enabled, this can be 2364 * done through escape sequences.... You have been warned. 2365 */ 2366static void 2367StartLogExec(TScreen *screen) 2368{ 2369 int pid; 2370 int p[2]; 2371 static char *shell; 2372 struct passwd pw; 2373 2374 if ((shell = x_getenv("SHELL")) == NULL) { 2375 2376 if (x_getpwuid(screen->uid, &pw)) { 2377 char *name = x_getlogin(screen->uid, &pw); 2378 if (*(pw.pw_shell)) { 2379 shell = pw.pw_shell; 2380 } 2381 free(name); 2382 } 2383 } 2384 2385 if (shell == 0) { 2386 static char dummy[] = "/bin/sh"; 2387 shell = dummy; 2388 } 2389 2390 if (access(shell, X_OK) != 0) { 2391 xtermPerror("Can't execute `%s'\n", shell); 2392 return; 2393 } 2394 2395 if (pipe(p) < 0) { 2396 xtermPerror("Can't make a pipe connection\n"); 2397 return; 2398 } else if ((pid = fork()) < 0) { 2399 xtermPerror("Can't fork...\n"); 2400 return; 2401 } 2402 if (pid == 0) { /* child */ 2403 /* 2404 * Close our output (we won't be talking back to the 2405 * parent), and redirect our child's output to the 2406 * original stderr. 2407 */ 2408 close(p[1]); 2409 dup2(p[0], 0); 2410 close(p[0]); 2411 dup2(fileno(stderr), 1); 2412 dup2(fileno(stderr), 2); 2413 2414 close(fileno(stderr)); 2415 close(ConnectionNumber(screen->display)); 2416 close(screen->respond); 2417 2418 signal(SIGHUP, SIG_DFL); 2419 signal(SIGCHLD, SIG_DFL); 2420 2421 /* (this is redundant) */ 2422 if (xtermResetIds(screen) < 0) 2423 exit(ERROR_SETUID); 2424 2425 execl(shell, shell, "-c", &screen->logfile[1], (void *) 0); 2426 xtermWarning("Can't exec `%s -c %s'\n", shell, &screen->logfile[1]); 2427 exit(ERROR_LOGEXEC); 2428 } 2429 close(p[0]); 2430 screen->logfd = p[1]; 2431 signal(SIGPIPE, handle_SIGPIPE); 2432} 2433#endif /* ALLOWLOGFILEEXEC */ 2434 2435/* 2436 * Generate a path for a logfile if no default path is given. 2437 */ 2438static char * 2439GenerateLogPath(void) 2440{ 2441 static char *log_default = NULL; 2442 2443 /* once opened we just reuse the same log name */ 2444 if (log_default) 2445 return (log_default); 2446 2447#if defined(HAVE_GETHOSTNAME) && defined(HAVE_STRFTIME) 2448 { 2449#define LEN_HOSTNAME 255 2450 /* Internet standard limit (RFC 1035): ``To simplify implementations, 2451 * the total length of a domain name (i.e., label octets and label 2452 * length octets) is restricted to 255 octets or less.'' 2453 */ 2454#define LEN_GETPID 9 2455 /* 2456 * This is arbitrary... 2457 */ 2458 const char form[] = "Xterm.log.%s%s.%lu"; 2459 char where[LEN_HOSTNAME + 1]; 2460 char when[LEN_TIMESTAMP]; 2461 time_t now = time((time_t *) 0); 2462 struct tm *ltm = (struct tm *) localtime(&now); 2463 2464 if ((gethostname(where, sizeof(where)) == 0) && 2465 (strftime(when, sizeof(when), FMT_TIMESTAMP, ltm) > 0) && 2466 ((log_default = (char *) malloc((sizeof(form) 2467 + strlen(where) 2468 + strlen(when) 2469 + LEN_GETPID))) != NULL)) { 2470 (void) sprintf(log_default, 2471 form, 2472 where, when, 2473 ((unsigned long) getpid()) % ((unsigned long) 1e10)); 2474 } 2475 } 2476#else 2477 static const char log_def_name[] = "XtermLog.XXXXXX"; 2478 if ((log_default = x_strdup(log_def_name)) != NULL) { 2479 mktemp(log_default); 2480 } 2481#endif 2482 2483 return (log_default); 2484} 2485 2486void 2487StartLog(XtermWidget xw) 2488{ 2489 TScreen *screen = TScreenOf(xw); 2490 2491 if (screen->logging || (screen->inhibit & I_LOG)) 2492 return; 2493#ifdef VMS /* file name is fixed in VMS variant */ 2494 screen->logfd = open(XTERM_VMS_LOGFILE, 2495 O_CREAT | O_TRUNC | O_APPEND | O_RDWR, 2496 0640); 2497 if (screen->logfd < 0) 2498 return; /* open failed */ 2499#else /*VMS */ 2500 2501 /* if we weren't supplied with a logfile path, generate one */ 2502 if (IsEmpty(screen->logfile)) 2503 screen->logfile = GenerateLogPath(); 2504 2505 /* give up if we were unable to allocate the filename */ 2506 if (!screen->logfile) 2507 return; 2508 2509 if (*screen->logfile == '|') { /* exec command */ 2510#ifdef ALLOWLOGFILEEXEC 2511 StartLogExec(screen); 2512#else 2513 Bell(xw, XkbBI_Info, 0); 2514 Bell(xw, XkbBI_Info, 0); 2515 return; 2516#endif 2517 } else if (strcmp(screen->logfile, "-") == 0) { 2518 screen->logfd = STDOUT_FILENO; 2519 } else { 2520 if ((screen->logfd = open_userfile(screen->uid, 2521 screen->gid, 2522 screen->logfile, 2523 True)) < 0) 2524 return; 2525 } 2526#endif /*VMS */ 2527 screen->logstart = VTbuffer->next; 2528 screen->logging = True; 2529 update_logging(); 2530} 2531 2532void 2533CloseLog(XtermWidget xw) 2534{ 2535 TScreen *screen = TScreenOf(xw); 2536 2537 if (!screen->logging || (screen->inhibit & I_LOG)) 2538 return; 2539 FlushLog(xw); 2540 close(screen->logfd); 2541 screen->logging = False; 2542 update_logging(); 2543} 2544 2545void 2546FlushLog(XtermWidget xw) 2547{ 2548 TScreen *screen = TScreenOf(xw); 2549 2550 if (screen->logging && !(screen->inhibit & I_LOG)) { 2551 Char *cp; 2552 int i; 2553 2554#ifdef VMS /* avoid logging output loops which otherwise occur sometimes 2555 when there is no output and cp/screen->logstart are 1 apart */ 2556 if (!tt_new_output) 2557 return; 2558 tt_new_output = False; 2559#endif /* VMS */ 2560 cp = VTbuffer->next; 2561 if (screen->logstart != 0 2562 && (i = (int) (cp - screen->logstart)) > 0) { 2563 IGNORE_RC(write(screen->logfd, screen->logstart, (size_t) i)); 2564 } 2565 screen->logstart = VTbuffer->next; 2566 } 2567} 2568 2569#endif /* ALLOWLOGGING */ 2570 2571/***====================================================================***/ 2572 2573static unsigned 2574maskToShift(unsigned long mask) 2575{ 2576 unsigned result = 0; 2577 if (mask != 0) { 2578 while ((mask & 1) == 0) { 2579 mask >>= 1; 2580 ++result; 2581 } 2582 } 2583 return result; 2584} 2585 2586static unsigned 2587maskToWidth(unsigned long mask) 2588{ 2589 unsigned result = 0; 2590 while (mask != 0) { 2591 if ((mask & 1) != 0) 2592 ++result; 2593 mask >>= 1; 2594 } 2595 return result; 2596} 2597 2598XVisualInfo * 2599getVisualInfo(XtermWidget xw) 2600{ 2601#define MYFMT "getVisualInfo \ 2602depth %d, \ 2603type %d (%s), \ 2604size %d \ 2605rgb masks (%04lx/%04lx/%04lx)\n" 2606#define MYARG \ 2607 vi->depth,\ 2608 vi->class,\ 2609 ((vi->class & 1) ? "dynamic" : "static"),\ 2610 vi->colormap_size,\ 2611 vi->red_mask,\ 2612 vi->green_mask,\ 2613 vi->blue_mask 2614 2615 TScreen *screen = TScreenOf(xw); 2616 Display *dpy = screen->display; 2617 XVisualInfo myTemplate; 2618 2619 if (xw->visInfo == 0 && xw->numVisuals == 0) { 2620 myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy, 2621 XDefaultScreen(dpy))); 2622 xw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask, 2623 &myTemplate, &xw->numVisuals); 2624 2625 if ((xw->visInfo != 0) && (xw->numVisuals > 0)) { 2626 XVisualInfo *vi = xw->visInfo; 2627 xw->rgb_widths[0] = maskToWidth(vi->red_mask); 2628 xw->rgb_widths[1] = maskToWidth(vi->green_mask); 2629 xw->rgb_widths[2] = maskToWidth(vi->blue_mask); 2630 xw->rgb_shifts[0] = maskToShift(vi->red_mask); 2631 xw->rgb_shifts[1] = maskToShift(vi->green_mask); 2632 xw->rgb_shifts[2] = maskToShift(vi->blue_mask); 2633 2634 xw->has_rgb = ((vi->red_mask != 0) && 2635 (vi->green_mask != 0) && 2636 (vi->blue_mask != 0) && 2637 ((vi->red_mask & vi->green_mask) == 0) && 2638 ((vi->green_mask & vi->blue_mask) == 0) && 2639 ((vi->blue_mask & vi->red_mask) == 0) && 2640 (vi->class == TrueColor 2641 || vi->class == DirectColor)); 2642 2643 if (resource.reportColors) { 2644 printf(MYFMT, MYARG); 2645 } 2646 TRACE((MYFMT, MYARG)); 2647 TRACE(("...shifts %u/%u/%u\n", 2648 xw->rgb_shifts[0], 2649 xw->rgb_shifts[1], 2650 xw->rgb_shifts[2])); 2651 TRACE(("...widths %u/%u/%u\n", 2652 xw->rgb_widths[0], 2653 xw->rgb_widths[1], 2654 xw->rgb_widths[2])); 2655 } 2656 } 2657 return (xw->visInfo != 0) && (xw->numVisuals > 0) ? xw->visInfo : NULL; 2658#undef MYFMT 2659#undef MYARG 2660} 2661 2662#if OPT_ISO_COLORS 2663static Bool 2664ReportAnsiColorRequest(XtermWidget xw, int opcode, int colornum, int final) 2665{ 2666 Bool result = False; 2667 2668 if (AllowColorOps(xw, ecGetAnsiColor)) { 2669 XColor color; 2670 char buffer[80]; 2671 2672 TRACE(("ReportAnsiColorRequest %d\n", colornum)); 2673 color.pixel = GET_COLOR_RES(xw, TScreenOf(xw)->Acolors[colornum]); 2674 (void) QueryOneColor(xw, &color); 2675 sprintf(buffer, "%d;%d;rgb:%04x/%04x/%04x", 2676 opcode, 2677 (opcode == 5) ? (colornum - NUM_ANSI_COLORS) : colornum, 2678 color.red, 2679 color.green, 2680 color.blue); 2681 unparseputc1(xw, ANSI_OSC); 2682 unparseputs(xw, buffer); 2683 unparseputc1(xw, final); 2684 result = True; 2685 } 2686 return result; 2687} 2688 2689static void 2690getColormapInfo(XtermWidget xw, unsigned *typep, unsigned *sizep) 2691{ 2692 if (getVisualInfo(xw)) { 2693 *typep = (unsigned) xw->visInfo->class; 2694 *sizep = (unsigned) xw->visInfo->colormap_size; 2695 } else { 2696 *typep = 0; 2697 *sizep = 0; 2698 } 2699} 2700 2701#define MAX_COLORTABLE 4096 2702 2703/* 2704 * Make only one call to XQueryColors(), since it can be slow. 2705 */ 2706static Boolean 2707loadColorTable(XtermWidget xw, unsigned length) 2708{ 2709 Colormap cmap = xw->core.colormap; 2710 TScreen *screen = TScreenOf(xw); 2711 Boolean result = (screen->cmap_data != 0); 2712 2713 if (!result 2714 && length != 0 2715 && length < MAX_COLORTABLE) { 2716 screen->cmap_data = TypeMallocN(XColor, (size_t) length); 2717 2718 if (screen->cmap_data != 0) { 2719 unsigned i; 2720 unsigned shift; 2721 2722 if (getVisualInfo(xw)) 2723 shift = xw->rgb_shifts[2]; 2724 else 2725 shift = 0; 2726 2727 screen->cmap_size = length; 2728 2729 for (i = 0; i < screen->cmap_size; i++) { 2730 screen->cmap_data[i].pixel = (unsigned long) i << shift; 2731 } 2732 result = (Boolean) (XQueryColors(screen->display, 2733 cmap, 2734 screen->cmap_data, 2735 (int) screen->cmap_size) != 0); 2736 } 2737 } 2738 return result; 2739} 2740 2741/***====================================================================***/ 2742 2743/* 2744 * Call this function with def->{red,green,blue} initialized, to obtain a pixel 2745 * value. 2746 */ 2747Boolean 2748AllocOneColor(XtermWidget xw, XColor *def) 2749{ 2750 TScreen *screen = TScreenOf(xw); 2751 Boolean result = True; 2752 2753#define MaskIt(name,nn) \ 2754 ((unsigned long) ((def->name >> (16 - xw->rgb_widths[nn])) \ 2755 << xw->rgb_shifts[nn]) \ 2756 & xw->visInfo->name ##_mask) 2757 2758 if (getVisualInfo(xw) != NULL && xw->has_rgb) { 2759 def->pixel = MaskIt(red, 0) | MaskIt(green, 1) | MaskIt(blue, 2); 2760 } else { 2761 Display *dpy = screen->display; 2762 if (!XAllocColor(dpy, xw->core.colormap, def)) { 2763 /* 2764 * Decide between foreground and background by a grayscale 2765 * approximation. 2766 */ 2767 int bright = def->red * 3 + def->green * 10 + def->blue; 2768 int levels = 14 * 0x8000; 2769 def->pixel = ((bright >= levels) 2770 ? xw->dft_background 2771 : xw->dft_foreground); 2772 result = False; 2773 } 2774 } 2775 return result; 2776} 2777 2778/***====================================================================***/ 2779 2780/* 2781 * Call this function with def->pixel set to the color that we want to convert 2782 * to separate red/green/blue. 2783 */ 2784Boolean 2785QueryOneColor(XtermWidget xw, XColor *def) 2786{ 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 (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[20]; 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 switch (mode) { 4105 case 0: 4106 case 1: 4107 case 2: 4108 buf = strcpy(temp, "xterm"); 4109 break; 4110 default: 4111 TRACE(("do_osc found no data\n")); 4112 return; 4113 } 4114 } else { 4115 temp[0] = '\0'; 4116 buf = temp; 4117 } 4118 } else if (!need_data && !optional_data) { 4119 TRACE(("do_osc found unwanted data\n")); 4120 return; 4121 } 4122 4123 switch (mode) { 4124 case 0: /* new icon name and title */ 4125 ChangeIconName(xw, buf); 4126 ChangeTitle(xw, buf); 4127 break; 4128 4129 case 1: /* new icon name only */ 4130 ChangeIconName(xw, buf); 4131 break; 4132 4133 case 2: /* new title only */ 4134 ChangeTitle(xw, buf); 4135 break; 4136 4137#ifdef notdef 4138 case 3: /* change X property */ 4139 if (AllowWindowOps(xw, ewSetXprop)) 4140 ChangeXprop(buf); 4141 break; 4142#endif 4143#if OPT_ISO_COLORS 4144 case 5: 4145 ansi_colors = NUM_ANSI_COLORS; 4146 /* FALLTHRU */ 4147 case 4: 4148 if (ChangeAnsiColorRequest(xw, mode, buf, ansi_colors, final)) 4149 xw->work.palette_changed = True; 4150 break; 4151 case 6: 4152 /* FALLTHRU */ 4153 case OSC_Reset(6): 4154 TRACE(("parse colorXXMode:%s\n", buf)); 4155 while (*buf != '\0') { 4156 long which = 0; 4157 long value = 0; 4158 char *next; 4159 if (*buf == ';') { 4160 ++buf; 4161 } else { 4162 which = strtol(buf, &next, 10); 4163 if (!PartS2L(buf, next) || (which < 0)) 4164 break; 4165 buf = next; 4166 if (*buf == ';') 4167 ++buf; 4168 } 4169 if (*buf == ';') { 4170 ++buf; 4171 } else { 4172 value = strtol(buf, &next, 10); 4173 if (!PartS2L(buf, next) || (value < 0)) 4174 break; 4175 buf = next; 4176 if (*buf == ';') 4177 ++buf; 4178 } 4179 TRACE(("updating colorXXMode which=%ld, value=%ld\n", which, value)); 4180 switch (which) { 4181 case 0: 4182 screen->colorBDMode = (value != 0); 4183 break; 4184 case 1: 4185 screen->colorULMode = (value != 0); 4186 break; 4187 case 2: 4188 screen->colorBLMode = (value != 0); 4189 break; 4190 case 3: 4191 screen->colorRVMode = (value != 0); 4192 break; 4193#if OPT_WIDE_ATTRS 4194 case 4: 4195 screen->colorITMode = (value != 0); 4196 break; 4197#endif 4198 default: 4199 TRACE(("...unknown colorXXMode\n")); 4200 break; 4201 } 4202 } 4203 break; 4204 case OSC_Reset(5): 4205 ansi_colors = NUM_ANSI_COLORS; 4206 /* FALLTHRU */ 4207 case OSC_Reset(4): 4208 if (ResetAnsiColorRequest(xw, buf, ansi_colors)) 4209 xw->work.palette_changed = True; 4210 break; 4211#endif 4212 case OSC_TEXT_FG: 4213 case OSC_TEXT_BG: 4214 case OSC_TEXT_CURSOR: 4215 case OSC_MOUSE_FG: 4216 case OSC_MOUSE_BG: 4217#if OPT_HIGHLIGHT_COLOR 4218 case OSC_HIGHLIGHT_BG: 4219 case OSC_HIGHLIGHT_FG: 4220#endif 4221#if OPT_TEK4014 4222 case OSC_TEK_FG: 4223 case OSC_TEK_BG: 4224 case OSC_TEK_CURSOR: 4225#endif 4226 if (xw->misc.dynamicColors) { 4227 ChangeColorsRequest(xw, mode, buf, final); 4228 } 4229 break; 4230 case OSC_Reset(OSC_TEXT_FG): 4231 case OSC_Reset(OSC_TEXT_BG): 4232 case OSC_Reset(OSC_TEXT_CURSOR): 4233 case OSC_Reset(OSC_MOUSE_FG): 4234 case OSC_Reset(OSC_MOUSE_BG): 4235#if OPT_HIGHLIGHT_COLOR 4236 case OSC_Reset(OSC_HIGHLIGHT_BG): 4237 case OSC_Reset(OSC_HIGHLIGHT_FG): 4238#endif 4239#if OPT_TEK4014 4240 case OSC_Reset(OSC_TEK_FG): 4241 case OSC_Reset(OSC_TEK_BG): 4242 case OSC_Reset(OSC_TEK_CURSOR): 4243#endif 4244 if (xw->misc.dynamicColors) { 4245 ResetColorsRequest(xw, mode); 4246 } 4247 break; 4248 4249 case 22: 4250 xtermSetupPointer(xw, buf); 4251 break; 4252 4253 case 30: 4254 case 31: 4255 /* reserved for Konsole (Stephan Binner <Stephan.Binner@gmx.de>) */ 4256 break; 4257 4258#ifdef ALLOWLOGGING 4259 case 46: /* new log file */ 4260#ifdef ALLOWLOGFILECHANGES 4261 /* 4262 * Warning, enabling this feature allows people to overwrite 4263 * arbitrary files accessible to the person running xterm. 4264 */ 4265 if (strcmp(buf, "?")) { 4266 char *bp; 4267 if ((bp = x_strdup(buf)) != NULL) { 4268 free(screen->logfile); 4269 screen->logfile = bp; 4270 break; 4271 } 4272 } 4273#endif 4274 Bell(xw, XkbBI_Info, 0); 4275 Bell(xw, XkbBI_Info, 0); 4276 break; 4277#endif /* ALLOWLOGGING */ 4278 4279 case 50: 4280#if OPT_SHIFT_FONTS 4281 if (*buf == '?') { 4282 QueryFontRequest(xw, buf, final); 4283 } else if (xw->misc.shift_fonts) { 4284 ChangeFontRequest(xw, buf); 4285 } 4286#endif /* OPT_SHIFT_FONTS */ 4287 break; 4288 case 51: 4289 /* reserved for Emacs shell (Rob Mayoff <mayoff@dqd.com>) */ 4290 break; 4291 4292#if OPT_PASTE64 4293 case 52: 4294 ManipulateSelectionData(xw, screen, buf, final); 4295 break; 4296#endif 4297 /* 4298 * One could write code to send back the display and host names, 4299 * but that could potentially open a fairly nasty security hole. 4300 */ 4301 default: 4302 TRACE(("do_osc - unrecognized code\n")); 4303 break; 4304 } 4305 unparse_end(xw); 4306} 4307 4308/* 4309 * Parse one nibble of a hex byte from the OSC string. We have removed the 4310 * string-terminator (replacing it with a null), so the only other delimiter 4311 * that is expected is semicolon. Ignore other characters (Ray Neuman says 4312 * "real" terminals accept commas in the string definitions). 4313 */ 4314static int 4315udk_value(const char **cp) 4316{ 4317 int result = -1; 4318 4319 for (;;) { 4320 int c; 4321 4322 if ((c = **cp) != '\0') 4323 *cp = *cp + 1; 4324 if (c == ';' || c == '\0') 4325 break; 4326 if ((result = x_hex2int(c)) >= 0) 4327 break; 4328 } 4329 4330 return result; 4331} 4332 4333void 4334reset_decudk(XtermWidget xw) 4335{ 4336 int n; 4337 for (n = 0; n < MAX_UDK; n++) { 4338 FreeAndNull(xw->work.user_keys[n].str); 4339 xw->work.user_keys[n].len = 0; 4340 } 4341} 4342 4343/* 4344 * Parse the data for DECUDK (user-defined keys). 4345 */ 4346static void 4347parse_decudk(XtermWidget xw, const char *cp) 4348{ 4349 while (*cp) { 4350 const char *base = cp; 4351 char *str = malloc(strlen(cp) + 3); 4352 unsigned key = 0; 4353 int len = 0; 4354 4355 if (str == NULL) 4356 break; 4357 4358 while (isdigit(CharOf(*cp))) 4359 key = (key * 10) + (unsigned) (*cp++ - '0'); 4360 4361 if (*cp == '/') { 4362 int lo, hi; 4363 4364 cp++; 4365 while ((hi = udk_value(&cp)) >= 0 4366 && (lo = udk_value(&cp)) >= 0) { 4367 str[len++] = (char) ((hi << 4) | lo); 4368 } 4369 } 4370 if (len > 0 && key < MAX_UDK) { 4371 str[len] = '\0'; 4372 free(xw->work.user_keys[key].str); 4373 xw->work.user_keys[key].str = str; 4374 xw->work.user_keys[key].len = len; 4375 TRACE(("parse_decudk %d:%.*s\n", key, len, str)); 4376 } else { 4377 free(str); 4378 } 4379 if (*cp == ';') 4380 cp++; 4381 if (cp == base) /* badly-formed sequence - bail out */ 4382 break; 4383 } 4384} 4385 4386/* 4387 * Parse numeric parameters. Normally we use a state machine to simplify 4388 * interspersing with control characters, but have the string already. 4389 */ 4390static void 4391parse_ansi_params(ANSI *params, const char **string) 4392{ 4393 const char *cp = *string; 4394 ParmType nparam = 0; 4395 int last_empty = 1; 4396 4397 memset(params, 0, sizeof(*params)); 4398 while (*cp != '\0') { 4399 Char ch = CharOf(*cp++); 4400 4401 if (isdigit(ch)) { 4402 last_empty = 0; 4403 if (nparam < NPARAM) { 4404 params->a_param[nparam] = 4405 (ParmType) ((params->a_param[nparam] * 10) 4406 + (ch - '0')); 4407 } 4408 } else if (ch == ';') { 4409 last_empty = 1; 4410 nparam++; 4411 } else if (ch < 32) { 4412 /* EMPTY */ ; 4413 } else { 4414 /* should be 0x30 to 0x7e */ 4415 params->a_final = ch; 4416 break; 4417 } 4418 } 4419 4420 *string = cp; 4421 if (!last_empty) 4422 nparam++; 4423 if (nparam > NPARAM) 4424 params->a_nparam = NPARAM; 4425 else 4426 params->a_nparam = nparam; 4427} 4428 4429#if OPT_TRACE 4430#define SOFT_WIDE 10 4431#define SOFT_HIGH 20 4432 4433static void 4434parse_decdld(ANSI *params, const char *string) 4435{ 4436 char DscsName[8]; 4437 int len; 4438 int Pfn = params->a_param[0]; 4439 int Pcn = params->a_param[1]; 4440 int Pe = params->a_param[2]; 4441 int Pcmw = params->a_param[3]; 4442 int Pw = params->a_param[4]; 4443 int Pt = params->a_param[5]; 4444 int Pcmh = params->a_param[6]; 4445 int Pcss = params->a_param[7]; 4446 4447 int start_char = Pcn + 0x20; 4448 int char_wide = ((Pcmw == 0) 4449 ? (Pcss ? 6 : 10) 4450 : (Pcmw > 4 4451 ? Pcmw 4452 : (Pcmw + 3))); 4453 int char_high = ((Pcmh == 0) 4454 ? ((Pcmw >= 2 && Pcmw <= 4) 4455 ? 10 4456 : 20) 4457 : Pcmh); 4458 Char ch; 4459 Char bits[SOFT_HIGH][SOFT_WIDE]; 4460 Bool first = True; 4461 Bool prior = False; 4462 int row = 0, col = 0; 4463 4464 TRACE(("Parsing DECDLD\n")); 4465 TRACE((" font number %d\n", Pfn)); 4466 TRACE((" starting char %d\n", Pcn)); 4467 TRACE((" erase control %d\n", Pe)); 4468 TRACE((" char-width %d\n", Pcmw)); 4469 TRACE((" font-width %d\n", Pw)); 4470 TRACE((" text/full %d\n", Pt)); 4471 TRACE((" char-height %d\n", Pcmh)); 4472 TRACE((" charset-size %d\n", Pcss)); 4473 4474 if (Pfn > 1 4475 || Pcn > 95 4476 || Pe > 2 4477 || Pcmw > 10 4478 || Pcmw == 1 4479 || Pt > 2 4480 || Pcmh > 20 4481 || Pcss > 1 4482 || char_wide > SOFT_WIDE 4483 || char_high > SOFT_HIGH) { 4484 TRACE(("DECDLD illegal parameter\n")); 4485 return; 4486 } 4487 4488 len = 0; 4489 while (*string != '\0') { 4490 ch = CharOf(*string++); 4491 if (ch >= ANSI_SPA && ch <= 0x2f) { 4492 if (len < 2) 4493 DscsName[len++] = (char) ch; 4494 } else if (ch >= 0x30 && ch <= 0x7e) { 4495 DscsName[len++] = (char) ch; 4496 break; 4497 } 4498 } 4499 DscsName[len] = 0; 4500 TRACE((" Dscs name '%s'\n", DscsName)); 4501 4502 TRACE((" character matrix %dx%d\n", char_high, char_wide)); 4503 while (*string != '\0') { 4504 if (first) { 4505 TRACE(("Char %d:\n", start_char)); 4506 if (prior) { 4507 for (row = 0; row < char_high; ++row) { 4508 TRACE(("%.*s\n", char_wide, bits[row])); 4509 } 4510 } 4511 prior = False; 4512 first = False; 4513 for (row = 0; row < char_high; ++row) { 4514 for (col = 0; col < char_wide; ++col) { 4515 bits[row][col] = '.'; 4516 } 4517 } 4518 row = col = 0; 4519 } 4520 ch = CharOf(*string++); 4521 if (ch >= 0x3f && ch <= 0x7e) { 4522 int n; 4523 4524 ch = CharOf(ch - 0x3f); 4525 for (n = 0; n < 6; ++n) { 4526 bits[row + n][col] = CharOf((ch & (1 << n)) ? '*' : '.'); 4527 } 4528 col += 1; 4529 prior = True; 4530 } else if (ch == '/') { 4531 row += 6; 4532 col = 0; 4533 } else if (ch == ';') { 4534 first = True; 4535 ++start_char; 4536 } 4537 } 4538} 4539#else 4540#define parse_decdld(p,q) /* nothing */ 4541#endif 4542 4543#if OPT_DEC_RECTOPS 4544static const char * 4545skip_params(const char *cp) 4546{ 4547 while (*cp == ';' || (*cp >= '0' && *cp <= '9')) 4548 ++cp; 4549 return cp; 4550} 4551 4552static int 4553parse_int_param(const char **cp) 4554{ 4555 int result = 0; 4556 const char *s = *cp; 4557 while (*s != '\0') { 4558 if (*s == ';') { 4559 ++s; 4560 break; 4561 } else if (*s >= '0' && *s <= '9') { 4562 result = (result * 10) + (*s++ - '0'); 4563 } else { 4564 s += strlen(s); 4565 } 4566 } 4567 TRACE(("parse-int %s ->%d, %#x->%s\n", *cp, result, result, s)); 4568 *cp = s; 4569 return result; 4570} 4571 4572static int 4573parse_chr_param(const char **cp) 4574{ 4575 int result = 0; 4576 const char *s = *cp; 4577 if (*s != '\0') { 4578 if ((result = CharOf(*s++)) != 0) { 4579 if (*s == ';') { 4580 ++s; 4581 } else if (*s != '\0') { 4582 result = 0; 4583 } 4584 } 4585 } 4586 TRACE(("parse-chr %s ->%d, %#x->%s\n", *cp, result, result, s)); 4587 *cp = s; 4588 return result; 4589} 4590 4591static void 4592restore_DECCIR(XtermWidget xw, const char *cp) 4593{ 4594 TScreen *screen = TScreenOf(xw); 4595 int value; 4596 4597 /* row */ 4598 if ((value = parse_int_param(&cp)) <= 0 || value > MaxRows(screen)) 4599 return; 4600 screen->cur_row = (value - 1); 4601 4602 /* column */ 4603 if ((value = parse_int_param(&cp)) <= 0 || value > MaxCols(screen)) 4604 return; 4605 screen->cur_col = (value - 1); 4606 4607 /* page */ 4608 if (parse_int_param(&cp) != 1) 4609 return; 4610 4611 /* rendition */ 4612 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) 4613 return; 4614 UIntClr(xw->flags, (INVERSE | BLINK | UNDERLINE | BOLD)); 4615 xw->flags |= (value & 8) ? INVERSE : 0; 4616 xw->flags |= (value & 4) ? BLINK : 0; 4617 xw->flags |= (value & 2) ? UNDERLINE : 0; 4618 xw->flags |= (value & 1) ? BOLD : 0; 4619 4620 /* attributes */ 4621 if (((value = parse_chr_param(&cp)) & 0xfe) != 0x40) 4622 return; 4623 screen->protected_mode &= ~DEC_PROTECT; 4624 screen->protected_mode |= (value & 1) ? DEC_PROTECT : 0; 4625 4626 /* flags */ 4627 if (((value = parse_chr_param(&cp)) & 0xf0) != 0x40) 4628 return; 4629 screen->do_wrap = (value & 8) ? True : False; 4630 screen->curss = (Char) ((value & 4) ? 3 : ((value & 2) ? 2 : 0)); 4631 UIntClr(xw->flags, ORIGIN); 4632 xw->flags |= (value & 1) ? ORIGIN : 0; 4633 4634 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS) 4635 return; 4636 screen->curgl = (Char) value; 4637 4638 if ((value = (parse_chr_param(&cp) - '0')) < 0 || value >= NUM_GSETS) 4639 return; 4640 screen->curgr = (Char) value; 4641 4642 /* character-set size */ 4643 if (parse_chr_param(&cp) != 0x4f) /* works for xterm */ 4644 return; 4645 4646 /* SCS designators */ 4647 for (value = 0; value < NUM_GSETS; ++value) { 4648 if (*cp == '%') { 4649 xtermDecodeSCS(xw, value, 0, '%', *++cp); 4650 } else if (*cp != '\0') { 4651 xtermDecodeSCS(xw, value, 0, '\0', *cp); 4652 } else { 4653 return; 4654 } 4655 cp++; 4656 } 4657 4658 TRACE(("...done DECCIR\n")); 4659} 4660 4661static void 4662restore_DECTABSR(XtermWidget xw, const char *cp) 4663{ 4664 int stop = 0; 4665 Bool fail = False; 4666 4667 TabZonk(xw->tabs); 4668 while (*cp != '\0' && !fail) { 4669 if ((*cp) >= '0' && (*cp) <= '9') { 4670 stop = (stop * 10) + ((*cp) - '0'); 4671 } else if (*cp == '/') { 4672 --stop; 4673 if (OkTAB(stop)) { 4674 TabSet(xw->tabs, stop); 4675 stop = 0; 4676 } else { 4677 fail = True; 4678 } 4679 } else { 4680 fail = True; 4681 } 4682 ++cp; 4683 } 4684 --stop; 4685 if (OkTAB(stop)) 4686 TabSet(xw->tabs, stop); 4687 4688 TRACE(("...done DECTABSR\n")); 4689} 4690#endif 4691 4692void 4693do_dcs(XtermWidget xw, Char *dcsbuf, size_t dcslen) 4694{ 4695 TScreen *screen = TScreenOf(xw); 4696 char reply[BUFSIZ]; 4697 const char *cp = (const char *) dcsbuf; 4698 Bool okay; 4699 ANSI params; 4700#if OPT_DEC_RECTOPS 4701 char psarg = '0'; 4702#endif 4703 4704 TRACE(("do_dcs(%s:%lu)\n", (char *) dcsbuf, (unsigned long) dcslen)); 4705 4706 if (dcslen != strlen(cp)) 4707 /* shouldn't have nulls in the string */ 4708 return; 4709 4710 switch (*cp) { /* intermediate character, or parameter */ 4711 case '$': /* DECRQSS */ 4712 okay = True; 4713 4714 cp++; 4715 if (*cp == 'q') { 4716 *reply = '\0'; 4717 cp++; 4718 if (!strcmp(cp, "\"q")) { /* DECSCA */ 4719 TRACE(("DECRQSS -> DECSCA\n")); 4720 sprintf(reply, "%d%s", 4721 (screen->protected_mode == DEC_PROTECT) 4722 && (xw->flags & PROTECTED) ? 1 : 0, 4723 cp); 4724 } else if (!strcmp(cp, "\"p")) { /* DECSCL */ 4725 if (screen->vtXX_level < 2) { 4726 /* actually none of DECRQSS is valid for vt100's */ 4727 break; 4728 } 4729 TRACE(("DECRQSS -> DECSCL\n")); 4730 sprintf(reply, "%d%s%s", 4731 (screen->vtXX_level ? 4732 screen->vtXX_level : 1) + 60, 4733 (screen->vtXX_level >= 2) 4734 ? (screen->control_eight_bits 4735 ? ";0" : ";1") 4736 : "", 4737 cp); 4738 } else if (!strcmp(cp, "r")) { /* DECSTBM */ 4739 TRACE(("DECRQSS -> DECSTBM\n")); 4740 sprintf(reply, "%d;%dr", 4741 screen->top_marg + 1, 4742 screen->bot_marg + 1); 4743 } else if (!strcmp(cp, "s")) { /* DECSLRM */ 4744 if (screen->vtXX_level >= 4) { /* VT420 */ 4745 TRACE(("DECRQSS -> DECSLRM\n")); 4746 sprintf(reply, "%d;%ds", 4747 screen->lft_marg + 1, 4748 screen->rgt_marg + 1); 4749 } else { 4750 okay = False; 4751 } 4752 } else if (!strcmp(cp, "m")) { /* SGR */ 4753 TRACE(("DECRQSS -> SGR\n")); 4754 xtermFormatSGR(xw, reply, xw->flags, xw->cur_foreground, xw->cur_background); 4755 strcat(reply, "m"); 4756 } else if (!strcmp(cp, " q")) { /* DECSCUSR */ 4757 int code = STEADY_BLOCK; 4758 if (isCursorUnderline(screen)) 4759 code = STEADY_UNDERLINE; 4760 else if (isCursorBar(screen)) 4761 code = STEADY_BAR; 4762#if OPT_BLINK_CURS 4763 if (screen->cursor_blink_esc != 0) 4764 code -= 1; 4765#endif 4766 TRACE(("reply DECSCUSR\n")); 4767 sprintf(reply, "%d%s", code, cp); 4768 } else if (!strcmp(cp, "t")) { /* DECSLPP */ 4769 sprintf(reply, "%d%s", 4770 ((screen->max_row > 24) ? screen->max_row : 24), 4771 cp); 4772 TRACE(("reply DECSLPP\n")); 4773 } else if (!strcmp(cp, "$|")) { /* DECSCPP */ 4774 TRACE(("reply DECSCPP\n")); 4775 sprintf(reply, "%d%s", 4776 ((xw->flags & IN132COLUMNS) ? 132 : 80), 4777 cp); 4778 } else 4779#if OPT_STATUS_LINE 4780 if (!strcmp(cp, "$}")) { /* DECSASD */ 4781 TRACE(("reply DECSASD\n")); 4782 sprintf(reply, "%d%s", 4783 screen->status_active, 4784 cp); 4785 } else if (!strcmp(cp, "$~")) { /* DECSSDT */ 4786 TRACE(("reply DECSASD\n")); 4787 sprintf(reply, "%d%s", 4788 screen->status_type, 4789 cp); 4790 } else 4791#endif 4792 if (!strcmp(cp, "*|")) { /* DECSNLS */ 4793 TRACE(("reply DECSNLS\n")); 4794 sprintf(reply, "%d%s", 4795 screen->max_row + 1, 4796 cp); 4797 } else { 4798 okay = False; 4799 } 4800 4801 unparseputc1(xw, ANSI_DCS); 4802 unparseputc(xw, okay ? '1' : '0'); 4803 unparseputc(xw, '$'); 4804 unparseputc(xw, 'r'); 4805 cp = reply; 4806 unparseputs(xw, cp); 4807 unparseputc1(xw, ANSI_ST); 4808 } else { 4809 unparseputc(xw, ANSI_CAN); 4810 } 4811 break; 4812 case '+': 4813 cp++; 4814 switch (*cp) { 4815#if OPT_TCAP_QUERY 4816 case 'p': 4817 if (AllowTcapOps(xw, etSetTcap)) { 4818 set_termcap(xw, cp + 1); 4819 } 4820 break; 4821 case 'q': 4822 if (AllowTcapOps(xw, etGetTcap)) { 4823 Bool fkey; 4824 unsigned state; 4825 int code; 4826 const char *tmp; 4827 const char *parsed = ++cp; 4828 4829 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4830 4831 unparseputc1(xw, ANSI_DCS); 4832 4833 unparseputc(xw, code >= 0 ? '1' : '0'); 4834 4835 unparseputc(xw, '+'); 4836 unparseputc(xw, 'r'); 4837 4838 while (*cp != 0 && (code >= -1)) { 4839 if (cp == parsed) 4840 break; /* no data found, error */ 4841 4842 for (tmp = cp; tmp != parsed; ++tmp) 4843 unparseputc(xw, *tmp); 4844 4845 if (code >= 0) { 4846 unparseputc(xw, '='); 4847 screen->tc_query_code = code; 4848 screen->tc_query_fkey = fkey; 4849#if OPT_ISO_COLORS 4850 /* XK_COLORS is a fake code for the "Co" entry (maximum 4851 * number of colors) */ 4852 if (code == XK_COLORS) { 4853 unparseputn(xw, (unsigned) NUM_ANSI_COLORS); 4854 } else 4855#if OPT_DIRECT_COLOR 4856 if (code == XK_RGB) { 4857 if (TScreenOf(xw)->direct_color && xw->has_rgb) { 4858 if (xw->rgb_widths[0] == xw->rgb_widths[1] && 4859 xw->rgb_widths[1] == xw->rgb_widths[2]) { 4860 unparseputn(xw, xw->rgb_widths[0]); 4861 } else { 4862 char temp[1024]; 4863 sprintf(temp, "%d/%d/%d", 4864 xw->rgb_widths[0], 4865 xw->rgb_widths[1], 4866 xw->rgb_widths[2]); 4867 unparseputs(xw, temp); 4868 } 4869 } else { 4870 unparseputs(xw, "-1"); 4871 } 4872 } else 4873#endif 4874#endif 4875 if (code == XK_TCAPNAME) { 4876 unparseputs(xw, resource.term_name); 4877 } else { 4878 XKeyEvent event; 4879 memset(&event, 0, sizeof(event)); 4880 event.state = state; 4881 Input(xw, &event, False); 4882 } 4883 screen->tc_query_code = -1; 4884 } else { 4885 break; /* no match found, error */ 4886 } 4887 4888 cp = parsed; 4889 if (*parsed == ';') { 4890 unparseputc(xw, *parsed++); 4891 cp = parsed; 4892 code = xtermcapKeycode(xw, &parsed, &state, &fkey); 4893 } 4894 } 4895 unparseputc1(xw, ANSI_ST); 4896 } 4897 break; 4898#endif 4899#if OPT_XRES_QUERY 4900 case 'Q': 4901 ++cp; 4902 if (AllowXResOps(xw)) { 4903 Boolean first = True; 4904 while (*cp != '\0') { 4905 const char *parsed = 0; 4906 const char *tmp; 4907 char *name = x_decode_hex(cp, &parsed); 4908 char *value; 4909 char *result; 4910 if (cp == parsed || name == NULL) { 4911 free(name); 4912 break; /* no data found, error */ 4913 } 4914 TRACE(("query-feature '%s'\n", name)); 4915 if ((value = vt100ResourceToString(xw, name)) != 0) { 4916 okay = True; /* valid */ 4917 } else { 4918 okay = False; /* invalid */ 4919 } 4920 if (first) { 4921 unparseputc1(xw, ANSI_DCS); 4922 unparseputc(xw, okay ? '1' : '0'); 4923 unparseputc(xw, '+'); 4924 unparseputc(xw, 'R'); 4925 first = False; 4926 } 4927 4928 for (tmp = cp; tmp != parsed; ++tmp) 4929 unparseputc(xw, *tmp); 4930 4931 if (value != 0) { 4932 unparseputc1(xw, '='); 4933 result = x_encode_hex(value); 4934 unparseputs(xw, result); 4935 } else { 4936 result = NULL; 4937 } 4938 4939 free(name); 4940 free(value); 4941 free(result); 4942 4943 cp = parsed; 4944 if (*parsed == ';') { 4945 unparseputc(xw, *parsed++); 4946 cp = parsed; 4947 } 4948 } 4949 if (!first) 4950 unparseputc1(xw, ANSI_ST); 4951 } 4952 break; 4953#endif 4954 } 4955 break; 4956#if OPT_DEC_RECTOPS 4957 case '1': 4958 /* FALLTHRU */ 4959 case '2': 4960 if (*skip_params(cp) == '$') { 4961 psarg = *cp++; 4962 if ((*cp++ == '$') 4963 && (*cp++ == 't') 4964 && (screen->vtXX_level >= 3)) { 4965 switch (psarg) { 4966 case '1': 4967 TRACE(("DECRSPS (DECCIR)\n")); 4968 restore_DECCIR(xw, cp); 4969 break; 4970 case '2': 4971 TRACE(("DECRSPS (DECTABSR)\n")); 4972 restore_DECTABSR(xw, cp); 4973 break; 4974 } 4975 } 4976 break; 4977 } 4978#endif 4979 /* FALLTHRU */ 4980 default: 4981 if (optRegisGraphics(screen) || 4982 optSixelGraphics(screen) || 4983 screen->vtXX_level >= 2) { /* VT220 */ 4984 parse_ansi_params(¶ms, &cp); 4985 switch (params.a_final) { 4986 case 'p': /* ReGIS */ 4987#if OPT_REGIS_GRAPHICS 4988 if (optRegisGraphics(screen)) { 4989 parse_regis(xw, ¶ms, cp); 4990 } 4991#else 4992 TRACE(("ignoring ReGIS graphic (compilation flag not enabled)\n")); 4993#endif 4994 break; 4995 case 'q': /* sixel */ 4996#if OPT_SIXEL_GRAPHICS 4997 if (optSixelGraphics(screen)) { 4998 (void) parse_sixel(xw, ¶ms, cp); 4999 } 5000#else 5001 TRACE(("ignoring sixel graphic (compilation flag not enabled)\n")); 5002#endif 5003 break; 5004 case '|': /* DECUDK */ 5005 if (screen->vtXX_level >= 2) { /* VT220 */ 5006 if (params.a_param[0] == 0) 5007 reset_decudk(xw); 5008 parse_decudk(xw, cp); 5009 } 5010 break; 5011 case L_CURL: /* DECDLD */ 5012 if (screen->vtXX_level >= 2) { /* VT220 */ 5013 parse_decdld(¶ms, cp); 5014 } 5015 break; 5016 } 5017 } 5018 break; 5019 } 5020 unparse_end(xw); 5021} 5022 5023#if OPT_DEC_RECTOPS 5024enum { 5025 mdUnknown = 0, 5026 mdMaybeSet = 1, 5027 mdMaybeReset = 2, 5028 mdAlwaysSet = 3, 5029 mdAlwaysReset = 4 5030}; 5031 5032#define MdBool(bool) ((bool) ? mdMaybeSet : mdMaybeReset) 5033#define MdFlag(mode,flag) MdBool((mode) & (flag)) 5034 5035/* 5036 * Reply is the same format as the query, with pair of mode/value: 5037 * 0 - not recognized 5038 * 1 - set 5039 * 2 - reset 5040 * 3 - permanently set 5041 * 4 - permanently reset 5042 * Only one mode can be reported at a time. 5043 */ 5044void 5045do_ansi_rqm(XtermWidget xw, int nparams, int *params) 5046{ 5047 ANSI reply; 5048 int count = 0; 5049 5050 TRACE(("do_ansi_rqm %d:%d\n", nparams, params[0])); 5051 memset(&reply, 0, sizeof(reply)); 5052 5053 if (nparams >= 1) { 5054 int result = mdUnknown; 5055 5056 /* DECRQM can only ask about one mode at a time */ 5057 switch (params[0]) { 5058 case 1: /* GATM */ 5059 result = mdAlwaysReset; 5060 break; 5061 case 2: 5062 result = MdFlag(xw->keyboard.flags, MODE_KAM); 5063 break; 5064 case 3: /* CRM */ 5065 result = mdMaybeReset; 5066 break; 5067 case 4: 5068 result = MdFlag(xw->flags, INSERT); 5069 break; 5070 case 5: /* SRTM */ 5071 case 7: /* VEM */ 5072 case 10: /* HEM */ 5073 case 11: /* PUM */ 5074 result = mdAlwaysReset; 5075 break; 5076 case 12: 5077 result = MdFlag(xw->keyboard.flags, MODE_SRM); 5078 break; 5079 case 13: /* FEAM */ 5080 case 14: /* FETM */ 5081 case 15: /* MATM */ 5082 case 16: /* TTM */ 5083 case 17: /* SATM */ 5084 case 18: /* TSM */ 5085 case 19: /* EBM */ 5086 result = mdAlwaysReset; 5087 break; 5088 case 20: 5089 result = MdFlag(xw->flags, LINEFEED); 5090 break; 5091 } 5092 reply.a_param[count++] = (ParmType) params[0]; 5093 reply.a_param[count++] = (ParmType) result; 5094 } 5095 reply.a_type = ANSI_CSI; 5096 reply.a_nparam = (ParmType) count; 5097 reply.a_inters = '$'; 5098 reply.a_final = 'y'; 5099 unparseseq(xw, &reply); 5100} 5101 5102void 5103do_dec_rqm(XtermWidget xw, int nparams, int *params) 5104{ 5105 ANSI reply; 5106 int count = 0; 5107 5108 TRACE(("do_dec_rqm %d:%d\n", nparams, params[0])); 5109 memset(&reply, 0, sizeof(reply)); 5110 5111 if (nparams >= 1) { 5112 TScreen *screen = TScreenOf(xw); 5113 int result = mdUnknown; 5114 5115 /* DECRQM can only ask about one mode at a time */ 5116 switch ((DECSET_codes) params[0]) { 5117 case srm_DECCKM: 5118 result = MdFlag(xw->keyboard.flags, MODE_DECCKM); 5119 break; 5120 case srm_DECANM: /* ANSI/VT52 mode */ 5121#if OPT_VT52_MODE 5122 result = MdBool(screen->vtXX_level >= 1); 5123#else 5124 result = mdMaybeSet; 5125#endif 5126 break; 5127 case srm_DECCOLM: 5128 result = MdFlag(xw->flags, IN132COLUMNS); 5129 break; 5130 case srm_DECSCLM: /* (slow scroll) */ 5131 result = MdFlag(xw->flags, SMOOTHSCROLL); 5132 break; 5133 case srm_DECSCNM: 5134 result = MdFlag(xw->flags, REVERSE_VIDEO); 5135 break; 5136 case srm_DECOM: 5137 result = MdFlag(xw->flags, ORIGIN); 5138 break; 5139 case srm_DECAWM: 5140 result = MdFlag(xw->flags, WRAPAROUND); 5141 break; 5142 case srm_DECARM: 5143 result = mdAlwaysReset; 5144 break; 5145 case srm_X10_MOUSE: /* X10 mouse */ 5146 result = MdBool(screen->send_mouse_pos == X10_MOUSE); 5147 break; 5148#if OPT_TOOLBAR 5149 case srm_RXVT_TOOLBAR: 5150 result = MdBool(resource.toolBar); 5151 break; 5152#endif 5153#if OPT_BLINK_CURS 5154 case srm_ATT610_BLINK: /* AT&T 610: Start/stop blinking cursor */ 5155 result = MdBool(screen->cursor_blink_esc); 5156 break; 5157 case srm_CURSOR_BLINK_OPS: 5158 switch (screen->cursor_blink) { 5159 case cbTrue: 5160 result = mdMaybeSet; 5161 break; 5162 case cbFalse: 5163 result = mdMaybeReset; 5164 break; 5165 case cbAlways: 5166 result = mdAlwaysSet; 5167 break; 5168 case cbLAST: 5169 /* FALLTHRU */ 5170 case cbNever: 5171 result = mdAlwaysReset; 5172 break; 5173 } 5174 break; 5175 case srm_XOR_CURSOR_BLINKS: 5176 result = (screen->cursor_blink_xor 5177 ? mdAlwaysSet 5178 : mdAlwaysReset); 5179 break; 5180#endif 5181 case srm_DECPFF: /* print form feed */ 5182 result = MdBool(PrinterOf(screen).printer_formfeed); 5183 break; 5184 case srm_DECPEX: /* print extent */ 5185 result = MdBool(PrinterOf(screen).printer_extent); 5186 break; 5187 case srm_DECTCEM: /* Show/hide cursor (VT200) */ 5188 result = MdBool(screen->cursor_set); 5189 break; 5190 case srm_RXVT_SCROLLBAR: 5191 result = MdBool(screen->fullVwin.sb_info.width != OFF); 5192 break; 5193#if OPT_SHIFT_FONTS 5194 case srm_RXVT_FONTSIZE: 5195 result = MdBool(xw->misc.shift_fonts); 5196 break; 5197#endif 5198#if OPT_TEK4014 5199 case srm_DECTEK: 5200 result = MdBool(TEK4014_ACTIVE(xw)); 5201 break; 5202#endif 5203 case srm_132COLS: 5204 result = MdBool(screen->c132); 5205 break; 5206 case srm_CURSES_HACK: 5207 result = MdBool(screen->curses); 5208 break; 5209 case srm_DECNRCM: /* national charset (VT220) */ 5210 if (screen->vtXX_level >= 2) { 5211 result = MdFlag(xw->flags, NATIONAL); 5212 } else { 5213 result = 0; 5214 } 5215 break; 5216 case srm_MARGIN_BELL: /* margin bell */ 5217 result = MdBool(screen->marginbell); 5218 break; 5219#if OPT_PRINT_GRAPHICS 5220 case srm_DECGEPM: /* Graphics Expanded Print Mode */ 5221 result = MdBool(screen->graphics_expanded_print_mode); 5222 break; 5223#endif 5224 case srm_REVERSEWRAP: /* reverse wraparound */ 5225 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_color_syntax)) 5226 result = MdFlag(xw->flags, REVERSEWRAP); 5227 break; 5228#if defined(ALLOWLOGGING) 5229 case srm_ALLOWLOGGING: /* logging */ 5230 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode)) 5231#if defined(ALLOWLOGFILEONOFF) 5232 result = MdBool(screen->logging); 5233#else 5234 result = ((MdBool(screen->logging) == mdMaybeSet) 5235 ? mdAlwaysSet 5236 : mdAlwaysReset); 5237#endif 5238 break; 5239#elif OPT_PRINT_GRAPHICS 5240 case srm_DECGPBM: /* Graphics Print Background Mode */ 5241 result = MdBool(screen->graphics_print_background_mode); 5242 break; 5243#endif 5244 case srm_OPT_ALTBUF_CURSOR: /* alternate buffer & cursor */ 5245 /* FALLTHRU */ 5246 case srm_OPT_ALTBUF: 5247 result = MdBool(screen->whichBuf); 5248 break; 5249 case srm_ALTBUF: 5250 if_PRINT_GRAPHICS2(result = MdBool(screen->graphics_print_background_mode)) 5251 result = MdBool(screen->whichBuf); 5252 break; 5253 case srm_DECNKM: 5254 result = MdFlag(xw->keyboard.flags, MODE_DECKPAM); 5255 break; 5256 case srm_DECBKM: 5257 result = MdFlag(xw->keyboard.flags, MODE_DECBKM); 5258 break; 5259 case srm_DECLRMM: 5260 if (screen->vtXX_level >= 4) { /* VT420 */ 5261 result = MdFlag(xw->flags, LEFT_RIGHT); 5262 } else { 5263 result = 0; 5264 } 5265 break; 5266#if OPT_SIXEL_GRAPHICS 5267 case srm_DECSDM: 5268 result = MdFlag(xw->keyboard.flags, MODE_DECSDM); 5269 break; 5270#endif 5271 case srm_DECNCSM: 5272 if (screen->vtXX_level >= 5) { /* VT510 */ 5273 result = MdFlag(xw->flags, NOCLEAR_COLM); 5274 } else { 5275 result = 0; 5276 } 5277 break; 5278 case srm_VT200_MOUSE: /* xterm bogus sequence */ 5279 result = MdBool(screen->send_mouse_pos == VT200_MOUSE); 5280 break; 5281 case srm_VT200_HIGHLIGHT_MOUSE: /* xterm sequence w/hilite tracking */ 5282 result = MdBool(screen->send_mouse_pos == VT200_HIGHLIGHT_MOUSE); 5283 break; 5284 case srm_BTN_EVENT_MOUSE: 5285 result = MdBool(screen->send_mouse_pos == BTN_EVENT_MOUSE); 5286 break; 5287 case srm_ANY_EVENT_MOUSE: 5288 result = MdBool(screen->send_mouse_pos == ANY_EVENT_MOUSE); 5289 break; 5290#if OPT_FOCUS_EVENT 5291 case srm_FOCUS_EVENT_MOUSE: 5292 result = MdBool(screen->send_focus_pos); 5293 break; 5294#endif 5295 case srm_EXT_MODE_MOUSE: 5296 /* FALLTHRU */ 5297 case srm_SGR_EXT_MODE_MOUSE: 5298 /* FALLTHRU */ 5299 case srm_URXVT_EXT_MODE_MOUSE: 5300 /* FALLTHRU */ 5301 case srm_PIXEL_POSITION_MOUSE: 5302 result = MdBool(screen->extend_coords == params[0]); 5303 break; 5304 case srm_ALTERNATE_SCROLL: 5305 result = MdBool(screen->alternateScroll); 5306 break; 5307 case srm_RXVT_SCROLL_TTY_OUTPUT: 5308 result = MdBool(screen->scrollttyoutput); 5309 break; 5310 case srm_RXVT_SCROLL_TTY_KEYPRESS: 5311 result = MdBool(screen->scrollkey); 5312 break; 5313 case srm_EIGHT_BIT_META: 5314 result = MdBool(screen->eight_bit_meta); 5315 break; 5316#if OPT_NUM_LOCK 5317 case srm_REAL_NUMLOCK: 5318 result = MdBool(xw->misc.real_NumLock); 5319 break; 5320 case srm_META_SENDS_ESC: 5321 result = MdBool(screen->meta_sends_esc); 5322 break; 5323#endif 5324 case srm_DELETE_IS_DEL: 5325 result = MdBool(xtermDeleteIsDEL(xw)); 5326 break; 5327#if OPT_NUM_LOCK 5328 case srm_ALT_SENDS_ESC: 5329 result = MdBool(screen->alt_sends_esc); 5330 break; 5331#endif 5332 case srm_KEEP_SELECTION: 5333 result = MdBool(screen->keepSelection); 5334 break; 5335 case srm_SELECT_TO_CLIPBOARD: 5336 result = MdBool(screen->selectToClipboard); 5337 break; 5338 case srm_BELL_IS_URGENT: 5339 result = MdBool(screen->bellIsUrgent); 5340 break; 5341 case srm_POP_ON_BELL: 5342 result = MdBool(screen->poponbell); 5343 break; 5344 case srm_KEEP_CLIPBOARD: 5345 result = MdBool(screen->keepClipboard); 5346 break; 5347 case srm_ALLOW_ALTBUF: 5348 result = MdBool(xw->misc.titeInhibit); 5349 break; 5350 case srm_SAVE_CURSOR: 5351 result = MdBool(screen->sc[screen->whichBuf].saved); 5352 break; 5353#if OPT_TCAP_FKEYS 5354 case srm_TCAP_FKEYS: 5355 result = MdBool(xw->keyboard.type == keyboardIsTermcap); 5356 break; 5357#endif 5358#if OPT_SUN_FUNC_KEYS 5359 case srm_SUN_FKEYS: 5360 result = MdBool(xw->keyboard.type == keyboardIsSun); 5361 break; 5362#endif 5363#if OPT_HP_FUNC_KEYS 5364 case srm_HP_FKEYS: 5365 result = MdBool(xw->keyboard.type == keyboardIsHP); 5366 break; 5367#endif 5368#if OPT_SCO_FUNC_KEYS 5369 case srm_SCO_FKEYS: 5370 result = MdBool(xw->keyboard.type == keyboardIsSCO); 5371 break; 5372#endif 5373 case srm_LEGACY_FKEYS: 5374 result = MdBool(xw->keyboard.type == keyboardIsLegacy); 5375 break; 5376#if OPT_SUNPC_KBD 5377 case srm_VT220_FKEYS: 5378 result = MdBool(xw->keyboard.type == keyboardIsVT220); 5379 break; 5380#endif 5381#if OPT_PASTE64 || OPT_READLINE 5382 case srm_PASTE_IN_BRACKET: 5383 result = MdBool(SCREEN_FLAG(screen, paste_brackets)); 5384 break; 5385#endif 5386#if OPT_READLINE 5387 case srm_BUTTON1_MOVE_POINT: 5388 result = MdBool(SCREEN_FLAG(screen, click1_moves)); 5389 break; 5390 case srm_BUTTON2_MOVE_POINT: 5391 result = MdBool(SCREEN_FLAG(screen, paste_moves)); 5392 break; 5393 case srm_DBUTTON3_DELETE: 5394 result = MdBool(SCREEN_FLAG(screen, dclick3_deletes)); 5395 break; 5396 case srm_PASTE_QUOTE: 5397 result = MdBool(SCREEN_FLAG(screen, paste_quotes)); 5398 break; 5399 case srm_PASTE_LITERAL_NL: 5400 result = MdBool(SCREEN_FLAG(screen, paste_literal_nl)); 5401 break; 5402#endif /* OPT_READLINE */ 5403#if OPT_GRAPHICS 5404 case srm_PRIVATE_COLOR_REGISTERS: 5405 result = MdBool(screen->privatecolorregisters); 5406 break; 5407#endif 5408#if OPT_SIXEL_GRAPHICS 5409 case srm_SIXEL_SCROLLS_RIGHT: 5410 result = MdBool(screen->sixel_scrolls_right); 5411 break; 5412#endif 5413 default: 5414 TRACE(("DATA_ERROR: requested report for unknown private mode %d\n", 5415 params[0])); 5416 } 5417 reply.a_param[count++] = (ParmType) params[0]; 5418 reply.a_param[count++] = (ParmType) result; 5419 TRACE(("DECRPM(%d) = %d\n", params[0], result)); 5420 } 5421 reply.a_type = ANSI_CSI; 5422 reply.a_pintro = '?'; 5423 reply.a_nparam = (ParmType) count; 5424 reply.a_inters = '$'; 5425 reply.a_final = 'y'; 5426 unparseseq(xw, &reply); 5427} 5428#endif /* OPT_DEC_RECTOPS */ 5429 5430char * 5431udk_lookup(XtermWidget xw, int keycode, int *len) 5432{ 5433 char *result = NULL; 5434 if (keycode >= 0 && keycode < MAX_UDK) { 5435 *len = xw->work.user_keys[keycode].len; 5436 result = xw->work.user_keys[keycode].str; 5437 TRACE(("udk_lookup(%d) = %.*s\n", keycode, *len, result)); 5438 } else { 5439 TRACE(("udk_lookup(%d) = <null>\n", keycode)); 5440 } 5441 return result; 5442} 5443 5444#if OPT_REPORT_ICONS 5445void 5446report_icons(const char *fmt, ...) 5447{ 5448 if (resource.reportIcons) { 5449 va_list ap; 5450 va_start(ap, fmt); 5451 vfprintf(stdout, fmt, ap); 5452 va_end(ap); 5453#if OPT_TRACE 5454 va_start(ap, fmt); 5455 TraceVA(fmt, ap); 5456 va_end(ap); 5457#endif 5458 } 5459} 5460#endif 5461 5462#ifdef HAVE_LIBXPM 5463 5464#ifndef PIXMAP_ROOTDIR 5465#define PIXMAP_ROOTDIR "/usr/share/pixmaps/" 5466#endif 5467 5468typedef struct { 5469 const char *name; 5470 const char *const *data; 5471} XPM_DATA; 5472 5473static char * 5474x_find_icon(char **work, int *state, const char *filename, const char *suffix) 5475{ 5476 const char *prefix = PIXMAP_ROOTDIR; 5477 const char *larger = "_48x48"; 5478 char *result = 0; 5479 5480 if (*state >= 0) { 5481 if ((*state & 1) == 0) 5482 suffix = ""; 5483 if ((*state & 2) == 0) 5484 larger = ""; 5485 if ((*state & 4) == 0) { 5486 prefix = ""; 5487 } else if (!strncmp(filename, "/", (size_t) 1) || 5488 !strncmp(filename, "./", (size_t) 2) || 5489 !strncmp(filename, "../", (size_t) 3)) { 5490 *state = -1; 5491 } else if (*state >= 8) { 5492 *state = -1; 5493 } 5494 } 5495 5496 if (*state >= 0) { 5497 size_t length; 5498 5499 FreeAndNull(*work); 5500 length = 3 + strlen(prefix) + strlen(filename) + strlen(larger) + 5501 strlen(suffix); 5502 if ((result = malloc(length)) != 0) { 5503 sprintf(result, "%s%s%s%s", prefix, filename, larger, suffix); 5504 *work = result; 5505 } 5506 *state += 1; 5507 } 5508 TRACE(("x_find_icon %d:%s ->%s\n", *state, filename, NonNull(result))); 5509 return result; 5510} 5511 5512#if OPT_BUILTIN_XPMS 5513 5514static const XPM_DATA * 5515built_in_xpm(const XPM_DATA * table, Cardinal length, const char *find) 5516{ 5517 const XPM_DATA *result = 0; 5518 if (!IsEmpty(find)) { 5519 Cardinal n; 5520 for (n = 0; n < length; ++n) { 5521 if (!x_strcasecmp(find, table[n].name)) { 5522 result = table + n; 5523 ReportIcons(("use builtin-icon %s\n", table[n].name)); 5524 break; 5525 } 5526 } 5527 5528 /* 5529 * As a fallback, check if the icon name matches without the lengths, 5530 * which are all _HHxWW format. 5531 */ 5532 if (result == 0) { 5533 const char *base = table[0].name; 5534 const char *last = strchr(base, '_'); 5535 if (last != 0 5536 && !x_strncasecmp(find, base, (unsigned) (last - base))) { 5537 result = table + length - 1; 5538 ReportIcons(("use builtin-icon %s\n", table[0].name)); 5539 } 5540 } 5541 } 5542 return result; 5543} 5544#define BuiltInXPM(name) built_in_xpm(name, XtNumber(name), icon_hint) 5545#endif /* OPT_BUILTIN_XPMS */ 5546 5547typedef enum { 5548 eHintDefault = 0 /* use the largest builtin-icon */ 5549 ,eHintNone 5550 ,eHintSearch 5551} ICON_HINT; 5552#endif /* HAVE_LIBXPM */ 5553 5554int 5555getVisualDepth(XtermWidget xw) 5556{ 5557 int result = 0; 5558 5559 if (getVisualInfo(xw)) { 5560 result = xw->visInfo->depth; 5561 } 5562 return result; 5563} 5564 5565/* 5566 * WM_ICON_SIZE should be honored if possible. 5567 */ 5568void 5569xtermLoadIcon(XtermWidget xw, const char *icon_hint) 5570{ 5571#ifdef HAVE_LIBXPM 5572 Display *dpy = XtDisplay(xw); 5573 Pixmap myIcon = 0; 5574 Pixmap myMask = 0; 5575 char *workname = 0; 5576 ICON_HINT hint = eHintDefault; 5577#include <builtin_icons.h> 5578 5579 ReportIcons(("load icon (hint: %s)\n", NonNull(icon_hint))); 5580 if (!IsEmpty(icon_hint)) { 5581 if (!x_strcasecmp(icon_hint, "none")) { 5582 hint = eHintNone; 5583 } else { 5584 hint = eHintSearch; 5585 } 5586 } 5587 5588 if (hint == eHintSearch) { 5589 int state = 0; 5590 while (x_find_icon(&workname, &state, icon_hint, ".xpm") != 0) { 5591 Pixmap resIcon = 0; 5592 Pixmap shapemask = 0; 5593 XpmAttributes attributes; 5594 struct stat sb; 5595 5596 attributes.depth = (unsigned) getVisualDepth(xw); 5597 attributes.valuemask = XpmDepth; 5598 5599 if (IsEmpty(workname) 5600 || lstat(workname, &sb) != 0 5601 || !S_ISREG(sb.st_mode)) { 5602 TRACE(("...failure (no such file)\n")); 5603 } else { 5604 int rc = XpmReadFileToPixmap(dpy, 5605 DefaultRootWindow(dpy), 5606 workname, 5607 &resIcon, 5608 &shapemask, 5609 &attributes); 5610 if (rc == XpmSuccess) { 5611 myIcon = resIcon; 5612 myMask = shapemask; 5613 TRACE(("...success\n")); 5614 ReportIcons(("found/loaded icon-file %s\n", workname)); 5615 break; 5616 } else { 5617 TRACE(("...failure (%s)\n", XpmGetErrorString(rc))); 5618 } 5619 } 5620 } 5621 } 5622 5623 /* 5624 * If no external file was found, look for the name in the built-in table. 5625 * If that fails, just use the biggest mini-icon. 5626 */ 5627 if (myIcon == 0 && hint != eHintNone) { 5628 char **data; 5629#if OPT_BUILTIN_XPMS 5630 const XPM_DATA *myData = 0; 5631 myData = BuiltInXPM(mini_xterm_xpms); 5632 if (myData == 0) 5633 myData = BuiltInXPM(filled_xterm_xpms); 5634 if (myData == 0) 5635 myData = BuiltInXPM(xterm_color_xpms); 5636 if (myData == 0) 5637 myData = BuiltInXPM(xterm_xpms); 5638 if (myData == 0) 5639 myData = &mini_xterm_xpms[XtNumber(mini_xterm_xpms) - 1]; 5640 data = (char **) myData->data; 5641#else 5642 data = (char **) &mini_xterm_48x48_xpm; 5643#endif 5644 if (XpmCreatePixmapFromData(dpy, 5645 DefaultRootWindow(dpy), 5646 data, 5647 &myIcon, &myMask, 0) == 0) { 5648 ReportIcons(("loaded built-in pixmap icon\n")); 5649 } else { 5650 myIcon = 0; 5651 myMask = 0; 5652 } 5653 } 5654 5655 if (myIcon != 0) { 5656 XWMHints *hints = XGetWMHints(dpy, VShellWindow(xw)); 5657 if (!hints) 5658 hints = XAllocWMHints(); 5659 5660 if (hints) { 5661 hints->flags |= IconPixmapHint; 5662 hints->icon_pixmap = myIcon; 5663 if (myMask) { 5664 hints->flags |= IconMaskHint; 5665 hints->icon_mask = myMask; 5666 } 5667 5668 XSetWMHints(dpy, VShellWindow(xw), hints); 5669 XFree(hints); 5670 ReportIcons(("updated window-manager hints\n")); 5671 } 5672 } 5673 5674 free(workname); 5675 5676#else 5677 (void) xw; 5678 (void) icon_hint; 5679#endif 5680} 5681 5682void 5683ChangeGroup(XtermWidget xw, const char *attribute, char *value) 5684{ 5685 Arg args[1]; 5686 Boolean changed = True; 5687 Widget w = CURRENT_EMU(); 5688 Widget top = SHELL_OF(w); 5689 5690 char *my_attr = NULL; 5691 char *old_value = value; 5692#if OPT_WIDE_CHARS 5693 Boolean titleIsUTF8; 5694#endif 5695 5696 if (!AllowTitleOps(xw)) 5697 return; 5698 5699 /* 5700 * Ignore empty or too-long requests. 5701 */ 5702 if (value == 0 || strlen(value) > 1000) 5703 return; 5704 5705 if (IsTitleMode(xw, tmSetBase16)) { 5706 const char *temp; 5707 char *test; 5708 5709 /* this allocates a new string, if no error is detected */ 5710 value = x_decode_hex(value, &temp); 5711 if (value == 0 || *temp != '\0') { 5712 free(value); 5713 return; 5714 } 5715 for (test = value; *test != '\0'; ++test) { 5716 if (CharOf(*test) < 32) { 5717 *test = '\0'; 5718 break; 5719 } 5720 } 5721 } 5722#if OPT_WIDE_CHARS 5723 /* 5724 * By design, xterm uses the XtNtitle resource of the X Toolkit for setting 5725 * the WM_NAME property, rather than doing this directly. That relies on 5726 * the application to tell it if the format should be something other than 5727 * STRING, i.e., by setting the XtNtitleEncoding resource. 5728 * 5729 * The ICCCM says that WM_NAME is TEXT (i.e., uninterpreted). In X11R6, 5730 * the ICCCM listed STRING and COMPOUND_TEXT as possibilities; XFree86 5731 * added UTF8_STRING (the documentation for that was discarded by an Xorg 5732 * developer, although the source-code provides this feature). 5733 * 5734 * Since X11R5, if the X11 library fails to store a text property as 5735 * STRING, it falls back to COMPOUND_TEXT. For best interoperability, we 5736 * prefer to use STRING if the data fits, or COMPOUND_TEXT. In either 5737 * case, limit the resulting characters to the printable ISO-8859-1 set. 5738 */ 5739 titleIsUTF8 = isValidUTF8((Char *) value); 5740 if (IsSetUtf8Title(xw) && titleIsUTF8) { 5741 char *testc = malloc(strlen(value) + 1); 5742 Char *nextc = (Char *) value; 5743 Boolean ok8bit = True; 5744 5745 if (testc != NULL) { 5746 /* 5747 * Check if the data fits in STRING. Along the way, replace 5748 * control characters. 5749 */ 5750 Char *lastc = (Char *) testc; 5751 while (*nextc != '\0') { 5752 unsigned ch; 5753 nextc = convertFromUTF8(nextc, &ch); 5754 if (ch > 255) { 5755 ok8bit = False; 5756 } else if (!IsLatin1(ch)) { 5757 ch = OnlyLatin1(ch); 5758 } 5759 *lastc++ = (Char) ch; 5760 } 5761 *lastc = '\0'; 5762 if (ok8bit) { 5763 TRACE(("ChangeGroup: UTF-8 converted to ISO-8859-1\n")); 5764 if (value != old_value) 5765 free(value); 5766 value = testc; 5767 titleIsUTF8 = False; 5768 } else { 5769 TRACE(("ChangeGroup: UTF-8 NOT converted to ISO-8859-1:\n" 5770 "\t%s\n", value)); 5771 free(testc); 5772 nextc = (Char *) value; 5773 while (*nextc != '\0') { 5774 unsigned ch; 5775 Char *skip = convertFromUTF8(nextc, &ch); 5776 if (iswcntrl((wint_t) ch)) { 5777 memset(nextc, BAD_ASCII, (size_t) (skip - nextc)); 5778 } 5779 nextc = skip; 5780 } 5781 } 5782 } 5783 } else 5784#endif 5785 { 5786 Char *c1 = (Char *) value; 5787 5788 TRACE(("ChangeGroup: assume ISO-8859-1\n")); 5789 for (c1 = (Char *) value; *c1 != '\0'; ++c1) { 5790 *c1 = (Char) OnlyLatin1(*c1); 5791 } 5792 } 5793 5794 my_attr = x_strdup(attribute); 5795 5796 ReportIcons(("ChangeGroup(attribute=%s, value=%s)\n", my_attr, value)); 5797 5798#if OPT_WIDE_CHARS 5799 /* 5800 * If we're running in UTF-8 mode, and have not been told that the 5801 * title string is in UTF-8, it is likely that non-ASCII text in the 5802 * string will be rejected because it is not printable in the current 5803 * locale. So we convert it to UTF-8, allowing the X library to 5804 * convert it back. 5805 */ 5806 TRACE(("ChangeGroup: value is %sUTF-8\n", titleIsUTF8 ? "" : "NOT ")); 5807 if (xtermEnvUTF8() && !titleIsUTF8) { 5808 size_t limit = strlen(value); 5809 Char *c1 = (Char *) value; 5810 int n; 5811 5812 for (n = 0; c1[n] != '\0'; ++n) { 5813 if (c1[n] > 127) { 5814 Char *converted; 5815 if ((converted = TypeMallocN(Char, 1 + (6 * limit))) != 0) { 5816 Char *temp = converted; 5817 while (*c1 != 0) { 5818 temp = convertToUTF8(temp, *c1++); 5819 } 5820 *temp = 0; 5821 if (value != old_value) 5822 free(value); 5823 value = (char *) converted; 5824 ReportIcons(("...converted{%s}\n", value)); 5825 } 5826 break; 5827 } 5828 } 5829 } 5830#endif 5831 5832#if OPT_SAME_NAME 5833 /* If the attribute isn't going to change, then don't bother... */ 5834 if (resource.sameName) { 5835 char *buf = 0; 5836 XtSetArg(args[0], my_attr, &buf); 5837 XtGetValues(top, args, 1); 5838 TRACE(("...comparing{%s}\n", NonNull(buf))); 5839 if (buf != 0 && strcmp(value, buf) == 0) 5840 changed = False; 5841 } 5842#endif /* OPT_SAME_NAME */ 5843 5844 if (changed) { 5845 ReportIcons(("...updating %s\n", my_attr)); 5846 ReportIcons(("...value is %s\n", value)); 5847 XtSetArg(args[0], my_attr, value); 5848 XtSetValues(top, args, 1); 5849 } 5850#if OPT_WIDE_CHARS 5851 if (xtermEnvUTF8()) { 5852 Display *dpy = XtDisplay(xw); 5853 const char *propname = (!strcmp(my_attr, XtNtitle) 5854 ? "_NET_WM_NAME" 5855 : "_NET_WM_ICON_NAME"); 5856 Atom my_atom = XInternAtom(dpy, propname, False); 5857 5858 if (my_atom != None) { 5859 changed = True; 5860 5861 if (IsSetUtf8Title(xw)) { 5862#if OPT_SAME_NAME 5863 if (resource.sameName) { 5864 Atom actual_type; 5865 Atom requested_type = XA_UTF8_STRING(dpy); 5866 int actual_format = 0; 5867 long long_length = 1024; 5868 unsigned long nitems = 0; 5869 unsigned long bytes_after = 0; 5870 unsigned char *prop = 0; 5871 5872 if (xtermGetWinProp(dpy, 5873 VShellWindow(xw), 5874 my_atom, 5875 0L, 5876 long_length, 5877 requested_type, 5878 &actual_type, 5879 &actual_format, 5880 &nitems, 5881 &bytes_after, 5882 &prop)) { 5883 if (actual_type == requested_type 5884 && actual_format == 8 5885 && prop != 0 5886 && nitems == strlen(value) 5887 && memcmp(value, prop, nitems) == 0) { 5888 changed = False; 5889 } 5890 XFree(prop); 5891 } 5892 } 5893#endif /* OPT_SAME_NAME */ 5894 if (changed) { 5895 ReportIcons(("...updating %s\n", propname)); 5896 ReportIcons(("...value is %s\n", value)); 5897 XChangeProperty(dpy, VShellWindow(xw), my_atom, 5898 XA_UTF8_STRING(dpy), 8, 5899 PropModeReplace, 5900 (Char *) value, 5901 (int) strlen(value)); 5902 } 5903 } else { 5904 ReportIcons(("...deleting %s\n", propname)); 5905 XDeleteProperty(dpy, VShellWindow(xw), my_atom); 5906 } 5907 } 5908 } 5909#endif 5910 if (value != old_value) { 5911 free(value); 5912 } 5913 free(my_attr); 5914 5915 return; 5916} 5917 5918void 5919ChangeIconName(XtermWidget xw, char *name) 5920{ 5921 if (name == 0) { 5922 name = emptyString; 5923 } 5924 if (!showZIconBeep(xw, name)) 5925 ChangeGroup(xw, XtNiconName, name); 5926} 5927 5928void 5929ChangeTitle(XtermWidget xw, char *name) 5930{ 5931 ChangeGroup(xw, XtNtitle, name); 5932} 5933 5934#define Strlen(s) strlen((const char *)(s)) 5935 5936void 5937ChangeXprop(char *buf) 5938{ 5939 Display *dpy = XtDisplay(toplevel); 5940 Window w = XtWindow(toplevel); 5941 XTextProperty text_prop; 5942 Atom aprop; 5943 Char *pchEndPropName = (Char *) strchr(buf, '='); 5944 5945 if (pchEndPropName) 5946 *pchEndPropName = '\0'; 5947 aprop = XInternAtom(dpy, buf, False); 5948 if (pchEndPropName == NULL) { 5949 /* no "=value" given, so delete the property */ 5950 XDeleteProperty(dpy, w, aprop); 5951 } else { 5952 text_prop.value = pchEndPropName + 1; 5953 text_prop.encoding = XA_STRING; 5954 text_prop.format = 8; 5955 text_prop.nitems = Strlen(text_prop.value); 5956 XSetTextProperty(dpy, w, &text_prop, aprop); 5957 } 5958} 5959 5960/***====================================================================***/ 5961 5962/* 5963 * This is part of ReverseVideo(). It reverses the data stored for the old 5964 * "dynamic" colors that might have been retrieved using OSC 10-18. 5965 */ 5966void 5967ReverseOldColors(XtermWidget xw) 5968{ 5969 ScrnColors *pOld = xw->work.oldColors; 5970 Pixel tmpPix; 5971 char *tmpName; 5972 5973 if (pOld) { 5974 /* change text cursor, if necessary */ 5975 if (pOld->colors[TEXT_CURSOR] == pOld->colors[TEXT_FG]) { 5976 pOld->colors[TEXT_CURSOR] = pOld->colors[TEXT_BG]; 5977 if (pOld->names[TEXT_CURSOR]) { 5978 XtFree(xw->work.oldColors->names[TEXT_CURSOR]); 5979 pOld->names[TEXT_CURSOR] = NULL; 5980 } 5981 if (pOld->names[TEXT_BG]) { 5982 if ((tmpName = x_strdup(pOld->names[TEXT_BG])) != 0) { 5983 pOld->names[TEXT_CURSOR] = tmpName; 5984 } 5985 } 5986 } 5987 5988 EXCHANGE(pOld->colors[TEXT_FG], pOld->colors[TEXT_BG], tmpPix); 5989 EXCHANGE(pOld->names[TEXT_FG], pOld->names[TEXT_BG], tmpName); 5990 5991 EXCHANGE(pOld->colors[MOUSE_FG], pOld->colors[MOUSE_BG], tmpPix); 5992 EXCHANGE(pOld->names[MOUSE_FG], pOld->names[MOUSE_BG], tmpName); 5993 5994#if OPT_TEK4014 5995 EXCHANGE(pOld->colors[TEK_FG], pOld->colors[TEK_BG], tmpPix); 5996 EXCHANGE(pOld->names[TEK_FG], pOld->names[TEK_BG], tmpName); 5997#endif 5998 FreeMarkGCs(xw); 5999 } 6000 return; 6001} 6002 6003Bool 6004AllocateTermColor(XtermWidget xw, 6005 ScrnColors * pNew, 6006 int ndx, 6007 const char *name, 6008 Bool always) 6009{ 6010 Bool result = False; 6011 6012 if (always || AllowColorOps(xw, ecSetColor)) { 6013 XColor def; 6014 char *newName; 6015 6016 result = True; 6017 if (!x_strcasecmp(name, XtDefaultForeground)) { 6018 def.pixel = xw->old_foreground; 6019 } else if (!x_strcasecmp(name, XtDefaultBackground)) { 6020 def.pixel = xw->old_background; 6021 } else if (!xtermAllocColor(xw, &def, name)) { 6022 result = False; 6023 } 6024 6025 if (result 6026 && (newName = x_strdup(name)) != 0) { 6027 if (COLOR_DEFINED(pNew, ndx)) { 6028 free(pNew->names[ndx]); 6029 } 6030 SET_COLOR_VALUE(pNew, ndx, def.pixel); 6031 SET_COLOR_NAME(pNew, ndx, newName); 6032 TRACE(("AllocateTermColor #%d: %s (pixel 0x%06lx)\n", 6033 ndx, newName, def.pixel)); 6034 } else { 6035 TRACE(("AllocateTermColor #%d: %s (failed)\n", ndx, name)); 6036 result = False; 6037 } 6038 } 6039 return result; 6040} 6041/***====================================================================***/ 6042 6043/* ARGSUSED */ 6044void 6045Panic(const char *s GCC_UNUSED, int a GCC_UNUSED) 6046{ 6047 if_DEBUG({ 6048 xtermWarning(s, a); 6049 }); 6050} 6051 6052const char * 6053SysErrorMsg(int code) 6054{ 6055 static const char unknown[] = "unknown error"; 6056 const char *s = strerror(code); 6057 return s ? s : unknown; 6058} 6059 6060const char * 6061SysReasonMsg(int code) 6062{ 6063 /* *INDENT-OFF* */ 6064 static const struct { 6065 int code; 6066 const char *name; 6067 } table[] = { 6068 { ERROR_FIONBIO, "main: ioctl() failed on FIONBIO" }, 6069 { ERROR_F_GETFL, "main: ioctl() failed on F_GETFL" }, 6070 { ERROR_F_SETFL, "main: ioctl() failed on F_SETFL", }, 6071 { ERROR_OPDEVTTY, "spawn: open() failed on /dev/tty", }, 6072 { ERROR_TIOCGETP, "spawn: ioctl() failed on TIOCGETP", }, 6073 { ERROR_PTSNAME, "spawn: ptsname() failed", }, 6074 { ERROR_OPPTSNAME, "spawn: open() failed on ptsname", }, 6075 { ERROR_PTEM, "spawn: ioctl() failed on I_PUSH/\"ptem\"" }, 6076 { ERROR_CONSEM, "spawn: ioctl() failed on I_PUSH/\"consem\"" }, 6077 { ERROR_LDTERM, "spawn: ioctl() failed on I_PUSH/\"ldterm\"" }, 6078 { ERROR_TTCOMPAT, "spawn: ioctl() failed on I_PUSH/\"ttcompat\"" }, 6079 { ERROR_TIOCSETP, "spawn: ioctl() failed on TIOCSETP" }, 6080 { ERROR_TIOCSETC, "spawn: ioctl() failed on TIOCSETC" }, 6081 { ERROR_TIOCSETD, "spawn: ioctl() failed on TIOCSETD" }, 6082 { ERROR_TIOCSLTC, "spawn: ioctl() failed on TIOCSLTC" }, 6083 { ERROR_TIOCLSET, "spawn: ioctl() failed on TIOCLSET" }, 6084 { ERROR_INIGROUPS, "spawn: initgroups() failed" }, 6085 { ERROR_FORK, "spawn: fork() failed" }, 6086 { ERROR_EXEC, "spawn: exec() failed" }, 6087 { ERROR_PTYS, "get_pty: not enough ptys" }, 6088 { ERROR_PTY_EXEC, "waiting for initial map" }, 6089 { ERROR_SETUID, "spawn: setuid() failed" }, 6090 { ERROR_INIT, "spawn: can't initialize window" }, 6091 { ERROR_TIOCKSET, "spawn: ioctl() failed on TIOCKSET" }, 6092 { ERROR_TIOCKSETC, "spawn: ioctl() failed on TIOCKSETC" }, 6093 { ERROR_LUMALLOC, "luit: command-line malloc failed" }, 6094 { ERROR_SELECT, "in_put: select() failed" }, 6095 { ERROR_VINIT, "VTInit: can't initialize window" }, 6096 { ERROR_KMMALLOC1, "HandleKeymapChange: malloc failed" }, 6097 { ERROR_TSELECT, "Tinput: select() failed" }, 6098 { ERROR_TINIT, "TekInit: can't initialize window" }, 6099 { ERROR_BMALLOC2, "SaltTextAway: malloc() failed" }, 6100 { ERROR_LOGEXEC, "StartLog: exec() failed" }, 6101 { ERROR_XERROR, "xerror: XError event" }, 6102 { ERROR_XIOERROR, "xioerror: X I/O error" }, 6103 { ERROR_SCALLOC, "Alloc: calloc() failed on base" }, 6104 { ERROR_SCALLOC2, "Alloc: calloc() failed on rows" }, 6105 { ERROR_SAVE_PTR, "ScrnPointers: malloc/realloc() failed" }, 6106 }; 6107 /* *INDENT-ON* */ 6108 6109 Cardinal n; 6110 const char *result = "?"; 6111 6112 for (n = 0; n < XtNumber(table); ++n) { 6113 if (code == table[n].code) { 6114 result = table[n].name; 6115 break; 6116 } 6117 } 6118 return result; 6119} 6120 6121void 6122SysError(int code) 6123{ 6124 int oerrno = errno; 6125 6126 fprintf(stderr, "%s: Error %d, errno %d: ", ProgramName, code, oerrno); 6127 fprintf(stderr, "%s\n", SysErrorMsg(oerrno)); 6128 fprintf(stderr, "Reason: %s\n", SysReasonMsg(code)); 6129 6130 Cleanup(code); 6131} 6132 6133void 6134NormalExit(void) 6135{ 6136 static Bool cleaning; 6137 6138 /* 6139 * Process "-hold" and session cleanup only for a normal exit. 6140 */ 6141 if (cleaning) { 6142 hold_screen = 0; 6143 return; 6144 } 6145 6146 cleaning = True; 6147 need_cleanup = False; 6148 6149 if (hold_screen) { 6150 hold_screen = 2; 6151 while (hold_screen) { 6152 xtermFlushDbe(term); 6153 xevents(term); 6154 Sleep(EVENT_DELAY); 6155 } 6156 } 6157#if OPT_SESSION_MGT 6158 if (resource.sessionMgt) { 6159 XtVaSetValues(toplevel, 6160 XtNjoinSession, False, 6161 (void *) 0); 6162 } 6163#endif 6164 Cleanup(0); 6165} 6166 6167#if USE_DOUBLE_BUFFER 6168void 6169xtermFlushDbe(XtermWidget xw) 6170{ 6171 TScreen *screen = TScreenOf(xw); 6172 if (resource.buffered && screen->needSwap) { 6173 XdbeSwapInfo swap; 6174 swap.swap_window = VWindow(screen); 6175 swap.swap_action = XdbeCopied; 6176 XdbeSwapBuffers(XtDisplay(xw), &swap, 1); 6177 XFlush(XtDisplay(xw)); 6178 screen->needSwap = 0; 6179 ScrollBarDrawThumb(xw, 2); 6180 X_GETTIMEOFDAY(&screen->buffered_at); 6181 } 6182} 6183 6184void 6185xtermTimedDbe(XtermWidget xw) 6186{ 6187 if (resource.buffered) { 6188 TScreen *screen = TScreenOf(xw); 6189 struct timeval now; 6190 long elapsed; 6191 long limit = DbeMsecs(xw); 6192 6193 X_GETTIMEOFDAY(&now); 6194 if (screen->buffered_at.tv_sec) { 6195 elapsed = (1000L * (now.tv_sec - screen->buffered_at.tv_sec) 6196 + (now.tv_usec - screen->buffered_at.tv_usec) / 1000L); 6197 } else { 6198 elapsed = limit; 6199 } 6200 if (elapsed >= limit) { 6201 xtermNeedSwap(xw, 1); 6202 xtermFlushDbe(xw); 6203 } 6204 } 6205} 6206#endif 6207 6208/* 6209 * cleanup by sending SIGHUP to client processes 6210 */ 6211void 6212Cleanup(int code) 6213{ 6214 TScreen *screen = TScreenOf(term); 6215 6216 TRACE(("Cleanup %d\n", code)); 6217 6218 if (screen->pid > 1) { 6219 (void) kill_process_group(screen->pid, SIGHUP); 6220 } 6221 Exit(code); 6222} 6223 6224#ifndef S_IXOTH 6225#define S_IXOTH 1 6226#endif 6227 6228Boolean 6229validProgram(const char *pathname) 6230{ 6231 Boolean result = False; 6232 struct stat sb; 6233 6234 if (!IsEmpty(pathname) 6235 && *pathname == '/' 6236 && strstr(pathname, "/..") == 0 6237 && stat(pathname, &sb) == 0 6238 && (sb.st_mode & S_IFMT) == S_IFREG 6239 && (sb.st_mode & S_IXOTH) != 0) { 6240 result = True; 6241 } 6242 return result; 6243} 6244 6245#ifndef VMS 6246#ifndef PATH_MAX 6247#define PATH_MAX 512 /* ... is not defined consistently in Xos.h */ 6248#endif 6249char * 6250xtermFindShell(char *leaf, Bool warning) 6251{ 6252 char *s0; 6253 char *s; 6254 char *d; 6255 char *tmp; 6256 char *result = leaf; 6257 Bool allocated = False; 6258 6259 TRACE(("xtermFindShell(%s)\n", leaf)); 6260 6261 if (!strncmp("./", result, (size_t) 2) 6262 || !strncmp("../", result, (size_t) 3)) { 6263 size_t need = PATH_MAX; 6264 size_t used = strlen(result) + 2; 6265 char *buffer = malloc(used + need); 6266 if (buffer != 0) { 6267 if (getcwd(buffer, need) != 0) { 6268 sprintf(buffer + strlen(buffer), "/%s", result); 6269 result = buffer; 6270 allocated = True; 6271 } else { 6272 free(buffer); 6273 } 6274 } 6275 } else if (*result != '\0' && strchr("+/-", *result) == 0) { 6276 /* find it in $PATH */ 6277 if ((s = s0 = x_getenv("PATH")) != 0) { 6278 if ((tmp = TypeMallocN(char, strlen(leaf) + strlen(s) + 2)) != 0) { 6279 Bool found = False; 6280 while (*s != '\0') { 6281 strcpy(tmp, s); 6282 for (d = tmp;; ++d) { 6283 if (*d == ':' || *d == '\0') { 6284 int skip = (*d != '\0'); 6285 *d = '/'; 6286 strcpy(d + 1, leaf); 6287 if (skip) 6288 ++d; 6289 s += (d - tmp); 6290 if (validProgram(tmp)) { 6291 result = x_strdup(tmp); 6292 found = True; 6293 allocated = True; 6294 } 6295 break; 6296 } 6297 } 6298 if (found) 6299 break; 6300 } 6301 free(tmp); 6302 } 6303 free(s0); 6304 } 6305 } 6306 TRACE(("...xtermFindShell(%s)\n", result)); 6307 if (!validProgram(result)) { 6308 if (warning) 6309 xtermWarning("No absolute path found for shell: %s\n", result); 6310 if (allocated) 6311 free(result); 6312 result = 0; 6313 } 6314 /* be consistent, so that caller can always free the result */ 6315 if (result != 0 && !allocated) 6316 result = x_strdup(result); 6317 return result; 6318} 6319#endif /* VMS */ 6320 6321#define ENV_HUNK(n) (unsigned) ((((n) + 1) | 31) + 1) 6322 6323/* 6324 * If we do not have unsetenv(), make consistent updates for environ[]. 6325 * This could happen on some older machines due to the uneven standardization 6326 * process for the two functions. 6327 * 6328 * That is, putenv() makes a copy of environ, and some implementations do not 6329 * update the environ pointer, so the fallback when unsetenv() is missing would 6330 * not work as intended. Likewise, the reverse could be true, i.e., unsetenv 6331 * could copy environ. 6332 */ 6333#if defined(HAVE_PUTENV) && !defined(HAVE_UNSETENV) 6334#undef HAVE_PUTENV 6335#elif !defined(HAVE_PUTENV) && defined(HAVE_UNSETENV) 6336#undef HAVE_UNSETENV 6337#endif 6338 6339/* 6340 * copy the environment before Setenv'ing. 6341 */ 6342void 6343xtermCopyEnv(char **oldenv) 6344{ 6345#ifdef HAVE_PUTENV 6346 (void) oldenv; 6347#else 6348 unsigned size; 6349 char **newenv; 6350 6351 for (size = 0; oldenv[size] != NULL; size++) { 6352 ; 6353 } 6354 6355 newenv = TypeCallocN(char *, ENV_HUNK(size)); 6356 memmove(newenv, oldenv, size * sizeof(char *)); 6357 environ = newenv; 6358#endif 6359} 6360 6361#if !defined(HAVE_PUTENV) || !defined(HAVE_UNSETENV) 6362static int 6363findEnv(const char *var, int *lengthp) 6364{ 6365 char *test; 6366 int envindex = 0; 6367 size_t len = strlen(var); 6368 int found = -1; 6369 6370 TRACE(("findEnv(%s=..)\n", var)); 6371 6372 while ((test = environ[envindex]) != NULL) { 6373 if (strncmp(test, var, len) == 0 && test[len] == '=') { 6374 found = envindex; 6375 break; 6376 } 6377 envindex++; 6378 } 6379 *lengthp = envindex; 6380 return found; 6381} 6382#endif 6383 6384/* 6385 * sets the value of var to be arg in the Unix 4.2 BSD environment env. 6386 * Var should end with '=' (bindings are of the form "var=value"). 6387 * This procedure assumes the memory for the first level of environ 6388 * was allocated using calloc, with enough extra room at the end so not 6389 * to have to do a realloc(). 6390 */ 6391void 6392xtermSetenv(const char *var, const char *value) 6393{ 6394 if (value != 0) { 6395#ifdef HAVE_PUTENV 6396 char *both = malloc(2 + strlen(var) + strlen(value)); 6397 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 6398 if (both) { 6399 sprintf(both, "%s=%s", var, value); 6400 putenv(both); 6401 } 6402#else 6403 size_t len = strlen(var); 6404 int envindex; 6405 int found = findEnv(var, &envindex); 6406 6407 TRACE(("xtermSetenv(%s=%s)\n", var, value)); 6408 6409 if (found < 0) { 6410 unsigned need = ENV_HUNK(envindex + 1); 6411 unsigned have = ENV_HUNK(envindex); 6412 6413 if (need > have) { 6414 char **newenv; 6415 newenv = TypeMallocN(char *, need); 6416 if (newenv == 0) { 6417 xtermWarning("Cannot increase environment\n"); 6418 return; 6419 } 6420 memmove(newenv, environ, have * sizeof(*newenv)); 6421 free(environ); 6422 environ = newenv; 6423 } 6424 6425 found = envindex; 6426 environ[found + 1] = NULL; 6427 environ = environ; 6428 } 6429 6430 environ[found] = malloc(2 + len + strlen(value)); 6431 if (environ[found] == 0) { 6432 xtermWarning("Cannot allocate environment %s\n", var); 6433 return; 6434 } 6435 sprintf(environ[found], "%s=%s", var, value); 6436#endif 6437 } 6438} 6439 6440void 6441xtermUnsetenv(const char *var) 6442{ 6443 TRACE(("xtermUnsetenv(%s)\n", var)); 6444#ifdef HAVE_UNSETENV 6445 unsetenv(var); 6446#else 6447 { 6448 int ignore; 6449 int item = findEnv(var, &ignore); 6450 if (item >= 0) { 6451 while ((environ[item] = environ[item + 1]) != 0) { 6452 ++item; 6453 } 6454 } 6455 } 6456#endif 6457} 6458 6459/*ARGSUSED*/ 6460int 6461xerror(Display *d, XErrorEvent *ev) 6462{ 6463 xtermWarning("warning, error event received:\n"); 6464 TRACE_X_ERR(d, ev); 6465 (void) XmuPrintDefaultErrorMessage(d, ev, stderr); 6466 Exit(ERROR_XERROR); 6467 return 0; /* appease the compiler */ 6468} 6469 6470void 6471ice_error(IceConn iceConn) 6472{ 6473 (void) iceConn; 6474 6475 xtermWarning("ICE IO error handler doing an exit(), pid = %ld, errno = %d\n", 6476 (long) getpid(), errno); 6477 6478 Exit(ERROR_ICEERROR); 6479} 6480 6481/*ARGSUSED*/ 6482int 6483xioerror(Display *dpy) 6484{ 6485 int the_error = errno; 6486 6487 xtermWarning("fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n", 6488 the_error, SysErrorMsg(the_error), 6489 DisplayString(dpy)); 6490 6491 Exit(ERROR_XIOERROR); 6492 return 0; /* appease the compiler */ 6493} 6494 6495void 6496xt_error(String message) 6497{ 6498 xtermWarning("Xt error: %s\n", message); 6499 6500 /* 6501 * Check for the obvious - Xt does a poor job of reporting this. 6502 */ 6503 if (x_getenv("DISPLAY") == 0) { 6504 xtermWarning("DISPLAY is not set\n"); 6505 } 6506 exit(1); 6507} 6508 6509int 6510XStrCmp(char *s1, char *s2) 6511{ 6512 if (s1 && s2) 6513 return (strcmp(s1, s2)); 6514 if (s1 && *s1) 6515 return (1); 6516 if (s2 && *s2) 6517 return (-1); 6518 return (0); 6519} 6520 6521#if OPT_TEK4014 6522static void 6523withdraw_window(Display *dpy, Window w, int scr) 6524{ 6525 TRACE(("withdraw_window %#lx\n", (long) w)); 6526 (void) XmuUpdateMapHints(dpy, w, NULL); 6527 XWithdrawWindow(dpy, w, scr); 6528 return; 6529} 6530#endif 6531 6532void 6533set_vt_visibility(Bool on) 6534{ 6535 XtermWidget xw = term; 6536 TScreen *screen = TScreenOf(xw); 6537 6538 TRACE(("set_vt_visibility(%d)\n", on)); 6539 if (on) { 6540 if (!screen->Vshow && xw) { 6541 VTInit(xw); 6542 XtMapWidget(XtParent(xw)); 6543#if OPT_TOOLBAR 6544 /* we need both of these during initialization */ 6545 XtMapWidget(SHELL_OF(xw)); 6546 ShowToolbar(resource.toolBar); 6547#endif 6548 screen->Vshow = True; 6549 } 6550 } 6551#if OPT_TEK4014 6552 else { 6553 if (screen->Vshow && xw) { 6554 withdraw_window(XtDisplay(xw), 6555 VShellWindow(xw), 6556 XScreenNumberOfScreen(XtScreen(xw))); 6557 screen->Vshow = False; 6558 } 6559 } 6560 set_vthide_sensitivity(); 6561 set_tekhide_sensitivity(); 6562 update_vttekmode(); 6563 update_tekshow(); 6564 update_vtshow(); 6565#endif 6566 return; 6567} 6568 6569#if OPT_TEK4014 6570void 6571set_tek_visibility(Bool on) 6572{ 6573 XtermWidget xw = term; 6574 6575 TRACE(("set_tek_visibility(%d)\n", on)); 6576 6577 if (on) { 6578 if (!TEK4014_SHOWN(xw)) { 6579 if (tekWidget == 0) { 6580 TekInit(); /* will exit on failure */ 6581 } 6582 if (tekWidget != 0) { 6583 Widget tekParent = SHELL_OF(tekWidget); 6584 XtRealizeWidget(tekParent); 6585 XtMapWidget(XtParent(tekWidget)); 6586#if OPT_TOOLBAR 6587 /* we need both of these during initialization */ 6588 XtMapWidget(tekParent); 6589 XtMapWidget(tekWidget); 6590#endif 6591 XtOverrideTranslations(tekParent, 6592 XtParseTranslationTable 6593 ("<Message>WM_PROTOCOLS: DeleteWindow()")); 6594 (void) XSetWMProtocols(XtDisplay(tekParent), 6595 XtWindow(tekParent), 6596 &wm_delete_window, 1); 6597 TEK4014_SHOWN(xw) = True; 6598 } 6599 } 6600 } else { 6601 if (TEK4014_SHOWN(xw) && tekWidget) { 6602 withdraw_window(XtDisplay(tekWidget), 6603 TShellWindow, 6604 XScreenNumberOfScreen(XtScreen(tekWidget))); 6605 TEK4014_SHOWN(xw) = False; 6606 } 6607 } 6608 set_tekhide_sensitivity(); 6609 set_vthide_sensitivity(); 6610 update_vtshow(); 6611 update_tekshow(); 6612 update_vttekmode(); 6613 return; 6614} 6615 6616void 6617end_tek_mode(void) 6618{ 6619 XtermWidget xw = term; 6620 6621 if (TEK4014_ACTIVE(xw)) { 6622 FlushLog(xw); 6623 TEK4014_ACTIVE(xw) = False; 6624 xtermSetWinSize(xw); 6625 longjmp(Tekend, 1); 6626 } 6627 return; 6628} 6629 6630void 6631end_vt_mode(void) 6632{ 6633 XtermWidget xw = term; 6634 6635 if (!TEK4014_ACTIVE(xw)) { 6636 FlushLog(xw); 6637 set_tek_visibility(True); 6638 TEK4014_ACTIVE(xw) = True; 6639 TekSetWinSize(tekWidget); 6640 longjmp(VTend, 1); 6641 } 6642 return; 6643} 6644 6645void 6646switch_modes(Bool tovt) /* if true, then become vt mode */ 6647{ 6648 if (tovt) { 6649 if (tekRefreshList) 6650 TekRefresh(tekWidget); 6651 end_tek_mode(); /* WARNING: this does a longjmp... */ 6652 } else { 6653 end_vt_mode(); /* WARNING: this does a longjmp... */ 6654 } 6655} 6656 6657void 6658hide_vt_window(void) 6659{ 6660 set_vt_visibility(False); 6661 if (!TEK4014_ACTIVE(term)) 6662 switch_modes(False); /* switch to tek mode */ 6663} 6664 6665void 6666hide_tek_window(void) 6667{ 6668 set_tek_visibility(False); 6669 tekRefreshList = (TekLink *) 0; 6670 if (TEK4014_ACTIVE(term)) 6671 switch_modes(True); /* does longjmp to vt mode */ 6672} 6673#endif /* OPT_TEK4014 */ 6674 6675static const char * 6676skip_punct(const char *s) 6677{ 6678 while (*s == '-' || *s == '/' || *s == '+' || *s == '#' || *s == '%') { 6679 ++s; 6680 } 6681 return s; 6682} 6683 6684static int 6685cmp_options(const void *a, const void *b) 6686{ 6687 const char *s1 = skip_punct(((const OptionHelp *) a)->opt); 6688 const char *s2 = skip_punct(((const OptionHelp *) b)->opt); 6689 return strcmp(s1, s2); 6690} 6691 6692static int 6693cmp_resources(const void *a, const void *b) 6694{ 6695 return strcmp(((const XrmOptionDescRec *) a)->option, 6696 ((const XrmOptionDescRec *) b)->option); 6697} 6698 6699XrmOptionDescRec * 6700sortedOptDescs(XrmOptionDescRec * descs, Cardinal res_count) 6701{ 6702 static XrmOptionDescRec *res_array = 0; 6703 6704#ifdef NO_LEAKS 6705 if (descs == 0) { 6706 FreeAndNull(res_array); 6707 } else 6708#endif 6709 if (res_array == 0) { 6710 Cardinal j; 6711 6712 /* make a sorted index to 'resources' */ 6713 res_array = TypeCallocN(XrmOptionDescRec, res_count); 6714 if (res_array != 0) { 6715 for (j = 0; j < res_count; j++) 6716 res_array[j] = descs[j]; 6717 qsort(res_array, (size_t) res_count, sizeof(*res_array), cmp_resources); 6718 } 6719 } 6720 return res_array; 6721} 6722 6723/* 6724 * The first time this is called, construct sorted index to the main program's 6725 * list of options, taking into account the on/off options which will be 6726 * compressed into one token. It's a lot simpler to do it this way than 6727 * maintain the list in sorted form with lots of ifdef's. 6728 */ 6729OptionHelp * 6730sortedOpts(OptionHelp * options, XrmOptionDescRec * descs, Cardinal numDescs) 6731{ 6732 static OptionHelp *opt_array = 0; 6733 6734#ifdef NO_LEAKS 6735 if (descs == 0 && opt_array != 0) { 6736 sortedOptDescs(descs, numDescs); 6737 FreeAndNull(opt_array); 6738 return 0; 6739 } else if (options == 0 || descs == 0) { 6740 return 0; 6741 } 6742#endif 6743 6744 if (opt_array == 0) { 6745 size_t opt_count, j; 6746#if OPT_TRACE 6747 Cardinal k; 6748 XrmOptionDescRec *res_array = sortedOptDescs(descs, numDescs); 6749 int code; 6750 const char *mesg; 6751#else 6752 (void) descs; 6753 (void) numDescs; 6754#endif 6755 6756 /* count 'options' and make a sorted index to it */ 6757 for (opt_count = 0; options[opt_count].opt != 0; ++opt_count) { 6758 ; 6759 } 6760 opt_array = TypeCallocN(OptionHelp, opt_count + 1); 6761 for (j = 0; j < opt_count; j++) 6762 opt_array[j] = options[j]; 6763 qsort(opt_array, opt_count, sizeof(OptionHelp), cmp_options); 6764 6765 /* supply the "turn on/off" strings if needed */ 6766#if OPT_TRACE 6767 for (j = 0; j < opt_count; j++) { 6768 if (!strncmp(opt_array[j].opt, "-/+", (size_t) 3)) { 6769 char temp[80]; 6770 const char *name = opt_array[j].opt + 3; 6771 for (k = 0; k < numDescs; ++k) { 6772 const char *value = res_array[k].value; 6773 if (res_array[k].option[0] == '-') { 6774 code = -1; 6775 } else if (res_array[k].option[0] == '+') { 6776 code = 1; 6777 } else { 6778 code = 0; 6779 } 6780 sprintf(temp, "%.*s", 6781 (int) sizeof(temp) - 2, 6782 opt_array[j].desc); 6783 if (x_strindex(temp, "inhibit") != 0) 6784 code = -code; 6785 if (code != 0 6786 && res_array[k].value != 0 6787 && !strcmp(name, res_array[k].option + 1)) { 6788 if (((code < 0) && !strcmp(value, "on")) 6789 || ((code > 0) && !strcmp(value, "off")) 6790 || ((code > 0) && !strcmp(value, "0"))) { 6791 mesg = "turn on/off"; 6792 } else { 6793 mesg = "turn off/on"; 6794 } 6795 TRACE(("%s: %s %s: %s (%s)\n", 6796 mesg, 6797 res_array[k].option, 6798 res_array[k].value, 6799 opt_array[j].opt, 6800 opt_array[j].desc)); 6801 break; 6802 } 6803 } 6804 } 6805 } 6806#endif 6807 } 6808 return opt_array; 6809} 6810 6811/* 6812 * Report the character-type locale that xterm was started in. 6813 */ 6814String 6815xtermEnvLocale(void) 6816{ 6817 static String result; 6818 6819 if (result == 0) { 6820 if ((result = x_nonempty(setlocale(LC_CTYPE, 0))) == 0) { 6821 result = x_strdup("C"); 6822 } else { 6823 result = x_strdup(result); 6824 } 6825 TRACE(("xtermEnvLocale ->%s\n", result)); 6826 } 6827 return result; 6828} 6829 6830char * 6831xtermEnvEncoding(void) 6832{ 6833 static char *result; 6834 6835 if (result == 0) { 6836#ifdef HAVE_LANGINFO_CODESET 6837 result = nl_langinfo(CODESET); 6838#else 6839 const char *locale = xtermEnvLocale(); 6840 if (!strcmp(locale, "C") || !strcmp(locale, "POSIX")) { 6841 result = x_strdup("ASCII"); 6842 } else { 6843 result = x_strdup("ISO-8859-1"); 6844 } 6845#endif 6846 TRACE(("xtermEnvEncoding ->%s\n", result)); 6847 } 6848 return result; 6849} 6850 6851#if OPT_WIDE_CHARS 6852/* 6853 * Tell whether xterm was started in a locale that uses UTF-8 encoding for 6854 * characters. That environment is inherited by subprocesses and used in 6855 * various library calls. 6856 */ 6857Bool 6858xtermEnvUTF8(void) 6859{ 6860 static Bool init = False; 6861 static Bool result = False; 6862 6863 if (!init) { 6864 init = True; 6865#ifdef HAVE_LANGINFO_CODESET 6866 result = (strcmp(xtermEnvEncoding(), "UTF-8") == 0); 6867#else 6868 { 6869 char *locale = x_strdup(xtermEnvLocale()); 6870 int n; 6871 for (n = 0; locale[n] != 0; ++n) { 6872 locale[n] = x_toupper(locale[n]); 6873 } 6874 if (strstr(locale, "UTF-8") != 0) 6875 result = True; 6876 else if (strstr(locale, "UTF8") != 0) 6877 result = True; 6878 free(locale); 6879 } 6880#endif 6881 TRACE(("xtermEnvUTF8 ->%s\n", BtoS(result))); 6882 } 6883 return result; 6884} 6885#endif /* OPT_WIDE_CHARS */ 6886 6887/* 6888 * Check if the current widget, or any parent, is the VT100 "xterm" widget. 6889 */ 6890XtermWidget 6891getXtermWidget(Widget w) 6892{ 6893 XtermWidget xw; 6894 6895 if (w == 0) { 6896 xw = (XtermWidget) CURRENT_EMU(); 6897 if (!IsXtermWidget(xw)) { 6898 xw = 0; 6899 } 6900 } else if (IsXtermWidget(w)) { 6901 xw = (XtermWidget) w; 6902 } else { 6903 xw = getXtermWidget(XtParent(w)); 6904 } 6905 TRACE2(("getXtermWidget %p -> %p\n", w, xw)); 6906 return xw; 6907} 6908 6909#if OPT_SESSION_MGT 6910 6911#if OPT_TRACE 6912static void 6913trace_1_SM(const char *tag, String name) 6914{ 6915 Arg args[1]; 6916 char *buf = 0; 6917 6918 XtSetArg(args[0], name, &buf); 6919 XtGetValues(toplevel, args, 1); 6920 6921 if (strstr(name, "Path") || strstr(name, "Directory")) { 6922 TRACE(("%s %s: %s\n", tag, name, NonNull(buf))); 6923 } else if (strstr(name, "Command")) { 6924 if (buf != NULL) { 6925 char **vec = (char **) (void *) buf; 6926 int n; 6927 TRACE(("%s %s:\n", tag, name)); 6928 for (n = 0; vec[n] != NULL; ++n) { 6929 TRACE((" arg[%d] = %s\n", n, vec[n])); 6930 } 6931 } else { 6932 TRACE(("%s %s: %p\n", tag, name, buf)); 6933 } 6934 } else { 6935 TRACE(("%s %s: %p\n", tag, name, buf)); 6936 } 6937} 6938 6939static void 6940trace_SM_props(void) 6941{ 6942 /* *INDENT-OFF* */ 6943 static struct { String app, cls; } table[] = { 6944 { XtNcurrentDirectory, XtCCurrentDirectory }, 6945 { XtNdieCallback, XtNdiscardCommand }, 6946 { XtCDiscardCommand, XtNenvironment }, 6947 { XtCEnvironment, XtNinteractCallback }, 6948 { XtNjoinSession, XtCJoinSession }, 6949 { XtNprogramPath, XtCProgramPath }, 6950 { XtNresignCommand, XtCResignCommand }, 6951 { XtNrestartCommand, XtCRestartCommand }, 6952 { XtNrestartStyle, XtCRestartStyle }, 6953 { XtNsaveCallback, XtNsaveCompleteCallback }, 6954 { XtNsessionID, XtCSessionID }, 6955 { XtNshutdownCommand, XtCShutdownCommand }, 6956 }; 6957 /* *INDENT-ON* */ 6958 Cardinal n; 6959 TRACE(("Session properties:\n")); 6960 for (n = 0; n < XtNumber(table); ++n) { 6961 trace_1_SM("app", table[n].app); 6962 trace_1_SM("cls", table[n].cls); 6963 } 6964} 6965#define TRACE_SM_PROPS() trace_SM_props() 6966#else 6967#define TRACE_SM_PROPS() /* nothing */ 6968#endif 6969 6970static void 6971die_callback(Widget w GCC_UNUSED, 6972 XtPointer client_data GCC_UNUSED, 6973 XtPointer call_data GCC_UNUSED) 6974{ 6975 TRACE(("die_callback client=%p, call=%p\n", 6976 (void *) client_data, 6977 (void *) call_data)); 6978 TRACE_SM_PROPS(); 6979 NormalExit(); 6980} 6981 6982static void 6983save_callback(Widget w GCC_UNUSED, 6984 XtPointer client_data GCC_UNUSED, 6985 XtPointer call_data) 6986{ 6987 XtCheckpointToken token = (XtCheckpointToken) call_data; 6988 TRACE(("save_callback:\n")); 6989 TRACE(("... save_type <-%d\n", token->save_type)); 6990 TRACE(("... interact_style <-%d\n", token->interact_style)); 6991 TRACE(("... shutdown <-%s\n", BtoS(token->shutdown))); 6992 TRACE(("... fast <-%s\n", BtoS(token->fast))); 6993 TRACE(("... cancel_shutdown <-%s\n", BtoS(token->cancel_shutdown))); 6994 TRACE(("... phase <-%d\n", token->phase)); 6995 TRACE(("... interact_dialog_type ->%d\n", token->interact_dialog_type)); 6996 TRACE(("... request_cancel ->%s\n", BtoS(token->request_cancel))); 6997 TRACE(("... request_next_phase ->%s\n", BtoS(token->request_next_phase))); 6998 TRACE(("... save_success ->%s\n", BtoS(token->save_success))); 6999 xtermUpdateRestartCommand(term); 7000 /* we have nothing more to save */ 7001 token->save_success = True; 7002} 7003 7004static void 7005icewatch(IceConn iceConn, 7006 IcePointer clientData GCC_UNUSED, 7007 Bool opening, 7008 IcePointer * watchData GCC_UNUSED) 7009{ 7010 if (opening) { 7011 ice_fd = IceConnectionNumber(iceConn); 7012 TRACE(("got IceConnectionNumber %d\n", ice_fd)); 7013 } else { 7014 ice_fd = -1; 7015 TRACE(("reset IceConnectionNumber\n")); 7016 } 7017} 7018 7019void 7020xtermOpenSession(void) 7021{ 7022 if (resource.sessionMgt) { 7023 TRACE(("Enabling session-management callbacks\n")); 7024 XtAddCallback(toplevel, XtNdieCallback, die_callback, NULL); 7025 XtAddCallback(toplevel, XtNsaveCallback, save_callback, NULL); 7026 7027 TRACE_SM_PROPS(); 7028 } 7029} 7030 7031void 7032xtermCloseSession(void) 7033{ 7034 IceRemoveConnectionWatch(icewatch, NULL); 7035} 7036 7037typedef enum { 7038 B_ARG = 0, 7039 I_ARG, 7040 D_ARG, 7041 S_ARG 7042} ParamType; 7043 7044#define Barg(name, field) { name, B_ARG, XtOffsetOf(XtermWidgetRec, field) } 7045#define Iarg(name, field) { name, I_ARG, XtOffsetOf(XtermWidgetRec, field) } 7046#define Darg(name, field) { name, D_ARG, XtOffsetOf(XtermWidgetRec, field) } 7047#define Sarg(name, field) { name, S_ARG, XtOffsetOf(XtermWidgetRec, field) } 7048 7049typedef struct { 7050 const char name[30]; 7051 ParamType type; 7052 Cardinal offset; 7053} FontParams; 7054 7055/* *INDENT-OFF* */ 7056static const FontParams fontParams[] = { 7057 Iarg(XtNinitialFont, screen.menu_font_number), /* "-fc" */ 7058 Barg(XtNallowBoldFonts, screen.allowBoldFonts), /* menu */ 7059#if OPT_BOX_CHARS 7060 Barg(XtNforceBoxChars, screen.force_box_chars), /* "-fbx" */ 7061 Barg(XtNforcePackedFont, screen.force_packed), /* menu */ 7062#endif 7063#if OPT_DEC_CHRSET 7064 Barg(XtNfontDoublesize, screen.font_doublesize), /* menu */ 7065#endif 7066#if OPT_WIDE_CHARS 7067 Barg(XtNutf8Fonts, screen.utf8_fonts), /* menu */ 7068#endif 7069#if OPT_RENDERFONT 7070 Darg(XtNfaceSize, misc.face_size[0]), /* "-fs" */ 7071 Sarg(XtNfaceName, misc.default_xft.f_n), /* "-fa" */ 7072 Sarg(XtNrenderFont, misc.render_font_s), /* (resource) */ 7073#endif 7074}; 7075/* *INDENT-ON* */ 7076 7077#define RESTART_PARAMS (int)(XtNumber(fontParams) * 2) 7078#define TypedPtr(type) *(type *)(void *)((char *) xw + parameter->offset) 7079 7080/* 7081 * If no widget is given, no value is used. 7082 */ 7083static char * 7084formatFontParam(char *result, XtermWidget xw, const FontParams * parameter) 7085{ 7086 sprintf(result, "%s*%s:", ProgramName, parameter->name); 7087 if (xw != None) { 7088 char *next = result + strlen(result); 7089 switch (parameter->type) { 7090 case B_ARG: 7091 sprintf(next, "%s", *(Boolean *) ((char *) xw + parameter->offset) 7092 ? "true" 7093 : "false"); 7094 break; 7095 case I_ARG: 7096 sprintf(next, "%d", TypedPtr(int)); 7097 break; 7098 case D_ARG: 7099 sprintf(next, "%.1f", TypedPtr(float)); 7100 break; 7101 case S_ARG: 7102 strcpy(next, TypedPtr(char *)); 7103#if OPT_RENDERFONT 7104 if (!strcmp(parameter->name, XtNfaceName)) { 7105 if (IsEmpty(next) 7106 && xw->work.render_font) { 7107 strcpy(next, DEFFACENAME_AUTO); 7108 } 7109 } else if (!strcmp(parameter->name, XtNrenderFont)) { 7110 if (xw->work.render_font == erDefault 7111 && IsEmpty(xw->misc.default_xft.f_n)) { 7112 strcpy(next, "DefaultOff"); 7113 } 7114 } 7115#endif 7116 break; 7117 } 7118 } 7119 return result; 7120} 7121 7122#if OPT_TRACE 7123static void 7124dumpFontParams(XtermWidget xw) 7125{ 7126 char buffer[1024]; 7127 Cardinal n; 7128 7129 TRACE(("FontParams:\n")); 7130 for (n = 0; n < XtNumber(fontParams); ++n) { 7131 TRACE(("%3d:%s\n", n, formatFontParam(buffer, xw, fontParams + n))); 7132 } 7133} 7134#else 7135#define dumpFontParams(xw) /* nothing */ 7136#endif 7137 7138static Boolean 7139findFontParams(int argc, char **argv) 7140{ 7141 Boolean result = False; 7142 7143 if (argc > RESTART_PARAMS && (argc - restart_params) > RESTART_PARAMS) { 7144 int n; 7145 7146 for (n = 0; n < RESTART_PARAMS; ++n) { 7147 int my_index = argc - restart_params - n - 1; 7148 int my_param = (RESTART_PARAMS - n - 1) / 2; 7149 char *actual = argv[my_index]; 7150 char expect[1024]; 7151 Boolean value = (Boolean) ((n % 2) == 0); 7152 7153 result = False; 7154 TRACE(("...index: %d\n", my_index)); 7155 TRACE(("...param: %d\n", my_param)); 7156 TRACE(("...actual %s\n", actual)); 7157 if (IsEmpty(actual)) 7158 break; 7159 7160 if (value) { 7161 formatFontParam(expect, None, fontParams + my_param); 7162 } else { 7163 strcpy(expect, "-xrm"); 7164 } 7165 7166 TRACE(("...expect %s\n", expect)); 7167 7168 if (value) { 7169 if (strlen(expect) >= strlen(actual)) 7170 break; 7171 if (strncmp(expect, actual, strlen(expect))) 7172 break; 7173 } else { 7174 if (strcmp(actual, expect)) 7175 break; 7176 } 7177 TRACE(("fixme/ok:%d\n", n)); 7178 result = True; 7179 } 7180 TRACE(("findFontParams: %s (tested %d of %d parameters)\n", 7181 BtoS(result), n + 1, RESTART_PARAMS)); 7182 } 7183 return result; 7184} 7185 7186static int 7187insertFontParams(XtermWidget xw, int *targetp, Bool first) 7188{ 7189 int changed = 0; 7190 int n; 7191 int target = *targetp; 7192 char buffer[1024]; 7193 const char *option = "-xrm"; 7194 7195 for (n = 0; n < (int) XtNumber(fontParams); ++n) { 7196 formatFontParam(buffer, xw, fontParams + n); 7197 TRACE(("formatted %3d ->%3d:%s\n", n, target, buffer)); 7198 if (restart_command[target] == NULL) 7199 restart_command[target] = x_strdup(option); 7200 ++target; 7201 if (first) { 7202 restart_command[target] = x_strdup(buffer); 7203 ++changed; 7204 } else if (restart_command[target] == NULL 7205 || strcmp(restart_command[target], buffer)) { 7206 free(restart_command[target]); 7207 restart_command[target] = x_strdup(buffer); 7208 ++changed; 7209 } 7210 ++target; 7211 } 7212 *targetp = target; 7213 return changed; 7214} 7215 7216void 7217xtermUpdateRestartCommand(XtermWidget xw) 7218{ 7219 if (resource.sessionMgt) { 7220 Arg args[1]; 7221 char **argv = 0; 7222 7223 XtSetArg(args[0], XtNrestartCommand, &argv); 7224 XtGetValues(toplevel, args, 1); 7225 if (argv != NULL) { 7226 static int my_params = 0; 7227 7228 int changes = 0; 7229 Boolean first = False; 7230 int argc; 7231 int want; 7232 int source, target; 7233 7234 TRACE(("xtermUpdateRestartCommand\n")); 7235 dumpFontParams(xw); 7236 for (argc = 0; argv[argc] != NULL; ++argc) { 7237 TRACE((" arg[%d] = %s\n", argc, argv[argc])); 7238 ; 7239 } 7240 want = argc - (restart_params + RESTART_PARAMS); 7241 7242 TRACE((" argc: %d\n", argc)); 7243 TRACE((" restart_params: %d\n", restart_params)); 7244 TRACE((" want to insert: %d\n", want)); 7245 7246 /* 7247 * If we already have the font-choice option, do not add it again. 7248 */ 7249 if (findFontParams(argc, argv)) { 7250 my_params = (want); 7251 } else { 7252 first = True; 7253 my_params = (argc - restart_params); 7254 } 7255 TRACE((" my_params: %d\n", my_params)); 7256 7257 if (my_params > argc) { 7258 TRACE((" re-allocate restartCommand\n")); 7259 FreeAndNull(restart_command); 7260 } 7261 7262 if (restart_command == NULL) { 7263 int need = argc + RESTART_PARAMS + 1; 7264 7265 restart_command = TypeCallocN(char *, need); 7266 7267 TRACE(("..inserting font-parameters\n")); 7268 for (source = target = 0; source < argc; ++source) { 7269 if (source == my_params) { 7270 changes += insertFontParams(xw, &target, first); 7271 if (!first) { 7272 source += (RESTART_PARAMS - 1); 7273 continue; 7274 } 7275 } 7276 if (argv[source] == NULL) 7277 break; 7278 restart_command[target++] = x_strdup(argv[source]); 7279 } 7280 restart_command[target] = NULL; 7281 } else { 7282 TRACE(("..replacing font-parameters\n")); 7283 target = my_params; 7284 changes += insertFontParams(xw, &target, first); 7285 } 7286 if (changes) { 7287 TRACE(("..%d parameters changed\n", changes)); 7288 XtSetArg(args[0], XtNrestartCommand, restart_command); 7289 XtSetValues(toplevel, args, 1); 7290 } else { 7291 TRACE(("..NO parameters changed\n")); 7292 } 7293 } 7294 TRACE_SM_PROPS(); 7295 } 7296} 7297#endif /* OPT_SESSION_MGT */ 7298 7299Widget 7300xtermOpenApplication(XtAppContext * app_context_return, 7301 String my_class, 7302 XrmOptionDescRec * options, 7303 Cardinal num_options, 7304 int *argc_in_out, 7305 char **argv_in_out, 7306 String *fallback_resources, 7307 WidgetClass widget_class, 7308 ArgList args, 7309 Cardinal num_args) 7310{ 7311 Widget result; 7312 7313 XtSetErrorHandler(xt_error); 7314#if OPT_SESSION_MGT 7315 result = XtOpenApplication(app_context_return, 7316 my_class, 7317 options, 7318 num_options, 7319 argc_in_out, 7320 argv_in_out, 7321 fallback_resources, 7322 widget_class, 7323 args, 7324 num_args); 7325 IceAddConnectionWatch(icewatch, NULL); 7326#else 7327 (void) widget_class; 7328 (void) args; 7329 (void) num_args; 7330 result = XtAppInitialize(app_context_return, 7331 my_class, 7332 options, 7333 num_options, 7334 argc_in_out, 7335 argv_in_out, 7336 fallback_resources, 7337 NULL, 0); 7338#endif /* OPT_SESSION_MGT */ 7339 XtSetErrorHandler(NULL); 7340 7341 return result; 7342} 7343 7344/* 7345 * Some calls to XGetAtom() will fail, and we don't want to stop. So we use 7346 * our own error-handler. 7347 */ 7348/* ARGSUSED */ 7349int 7350ignore_x11_error(Display *dpy GCC_UNUSED, XErrorEvent *event GCC_UNUSED) 7351{ 7352 return 1; 7353} 7354 7355static int x11_errors; 7356 7357static int 7358catch_x11_error(Display *display, XErrorEvent *error_event) 7359{ 7360 (void) display; 7361 (void) error_event; 7362 ++x11_errors; 7363 return 0; 7364} 7365 7366Boolean 7367xtermGetWinAttrs(Display *dpy, Window win, XWindowAttributes * attrs) 7368{ 7369 Boolean result = False; 7370 Status code; 7371 7372 memset(attrs, 0, sizeof(*attrs)); 7373 if (win != None) { 7374 XErrorHandler save = XSetErrorHandler(catch_x11_error); 7375 x11_errors = 0; 7376 code = XGetWindowAttributes(dpy, win, attrs); 7377 XSetErrorHandler(save); 7378 result = (Boolean) ((code != 0) && !x11_errors); 7379 if (result) { 7380 TRACE_WIN_ATTRS(attrs); 7381 } else { 7382 xtermWarning("invalid window-id %ld\n", (long) win); 7383 } 7384 } 7385 return result; 7386} 7387 7388Boolean 7389xtermGetWinProp(Display *display, 7390 Window win, 7391 Atom property, 7392 long long_offset, 7393 long long_length, 7394 Atom req_type, 7395 Atom *actual_type_return, 7396 int *actual_format_return, 7397 unsigned long *nitems_return, 7398 unsigned long *bytes_after_return, 7399 unsigned char **prop_return) 7400{ 7401 Boolean result = False; 7402 7403 if (win != None) { 7404 XErrorHandler save = XSetErrorHandler(catch_x11_error); 7405 x11_errors = 0; 7406 if (XGetWindowProperty(display, 7407 win, 7408 property, 7409 long_offset, 7410 long_length, 7411 False, 7412 req_type, 7413 actual_type_return, 7414 actual_format_return, 7415 nitems_return, 7416 bytes_after_return, 7417 prop_return) == Success 7418 && x11_errors == 0) { 7419 result = True; 7420 } 7421 XSetErrorHandler(save); 7422 } 7423 return result; 7424} 7425 7426void 7427xtermEmbedWindow(Window winToEmbedInto) 7428{ 7429 Display *dpy = XtDisplay(toplevel); 7430 XWindowAttributes attrs; 7431 7432 TRACE(("checking winToEmbedInto %#lx\n", winToEmbedInto)); 7433 if (xtermGetWinAttrs(dpy, winToEmbedInto, &attrs)) { 7434 XtermWidget xw = term; 7435 TScreen *screen = TScreenOf(xw); 7436 7437 XtRealizeWidget(toplevel); 7438 7439 TRACE(("...reparenting toplevel %#lx into %#lx\n", 7440 XtWindow(toplevel), 7441 winToEmbedInto)); 7442 XReparentWindow(dpy, 7443 XtWindow(toplevel), 7444 winToEmbedInto, 0, 0); 7445 7446 screen->embed_high = (Dimension) attrs.height; 7447 screen->embed_wide = (Dimension) attrs.width; 7448 } 7449} 7450 7451void 7452free_string(String value) 7453{ 7454 free((void *) value); 7455} 7456 7457/* Set tty's idea of window size, using the given file descriptor 'fd'. */ 7458int 7459update_winsize(TScreen *screen, int rows, int cols, int height, int width) 7460{ 7461 int code = -1; 7462#ifdef TTYSIZE_STRUCT 7463 static int last_rows = -1; 7464 static int last_cols = -1; 7465 static int last_high = -1; 7466 static int last_wide = -1; 7467 7468 TRACE(("update_winsize %dx%d (%dx%d) -> %dx%d (%dx%d)\n", 7469 last_rows, last_cols, last_high, last_wide, 7470 rows, cols, height, width)); 7471 7472 if (rows != last_rows 7473 || cols != last_cols 7474 || last_high != height 7475 || last_wide != width) { 7476 TTYSIZE_STRUCT ts; 7477 7478 last_rows = rows; 7479 last_cols = cols; 7480 last_high = height; 7481 last_wide = width; 7482#if OPT_STATUS_LINE 7483 if (IsStatusShown(screen)) { 7484 ++rows; 7485 height += FontHeight(screen); 7486 TRACE(("... account for status-line -> %dx%d (%dx%d)\n", 7487 rows, cols, height, width)); 7488 } 7489#endif 7490 setup_winsize(ts, rows, cols, height, width); 7491 TRACE_RC(code, SET_TTYSIZE(screen->respond, ts)); 7492 trace_winsize(ts, "from SET_TTYSIZE"); 7493 } 7494#endif 7495 7496 (void) rows; 7497 (void) cols; 7498 (void) height; 7499 (void) width; 7500 7501 return code; 7502} 7503 7504/* 7505 * Update stty settings to match the values returned by dtterm window 7506 * manipulation 18 and 19. 7507 */ 7508void 7509xtermSetWinSize(XtermWidget xw) 7510{ 7511#if OPT_TEK4014 7512 if (!TEK4014_ACTIVE(xw)) 7513#endif 7514 if (XtIsRealized((Widget) xw)) { 7515 TScreen *screen = TScreenOf(xw); 7516 7517 TRACE(("xtermSetWinSize\n")); 7518 update_winsize(screen, 7519 MaxRows(screen), 7520 MaxCols(screen), 7521 Height(screen), 7522 Width(screen)); 7523 } 7524} 7525 7526#if OPT_XTERM_SGR 7527 7528#if OPT_TRACE 7529static char * 7530traceIFlags(IFlags flags) 7531{ 7532 static char result[1000]; 7533 result[0] = '\0'; 7534#define DATA(name) if (flags & name) { strcat(result, " " #name); } 7535 DATA(INVERSE); 7536 DATA(UNDERLINE); 7537 DATA(BOLD); 7538 DATA(BLINK); 7539 DATA(INVISIBLE); 7540 DATA(BG_COLOR); 7541 DATA(FG_COLOR); 7542 7543#if OPT_WIDE_ATTRS 7544 DATA(ATR_FAINT); 7545 DATA(ATR_ITALIC); 7546 DATA(ATR_STRIKEOUT); 7547 DATA(ATR_DBL_UNDER); 7548 DATA(ATR_DIRECT_FG); 7549 DATA(ATR_DIRECT_BG); 7550#endif 7551#undef DATA 7552 return result; 7553} 7554 7555static char * 7556traceIStack(unsigned flags) 7557{ 7558 static char result[1000]; 7559 result[0] = '\0'; 7560#define DATA(name) if (flags & xBIT(ps##name - 1)) { strcat(result, " " #name); } 7561 DATA(INVERSE); 7562 DATA(UNDERLINE); 7563 DATA(BOLD); 7564 DATA(BLINK); 7565 DATA(INVISIBLE); 7566#if OPT_ISO_COLORS 7567 DATA(BG_COLOR); 7568 DATA(FG_COLOR); 7569#endif 7570 7571#if OPT_WIDE_ATTRS 7572 DATA(ATR_FAINT); 7573 DATA(ATR_ITALIC); 7574 DATA(ATR_STRIKEOUT); 7575 DATA(ATR_DBL_UNDER); 7576 /* direct-colors are a special case of ISO-colors (see above) */ 7577#endif 7578#undef DATA 7579 return result; 7580} 7581#endif 7582 7583void 7584xtermPushSGR(XtermWidget xw, int value) 7585{ 7586 SavedSGR *s = &(xw->saved_sgr); 7587 7588 TRACE(("xtermPushSGR %d mask %#x %s\n", 7589 s->used + 1, (unsigned) value, traceIStack((unsigned) value))); 7590 7591 if (s->used < MAX_SAVED_SGR) { 7592 s->stack[s->used].mask = (IFlags) value; 7593#define PUSH_FLAG(name) \ 7594 s->stack[s->used].name = xw->name;\ 7595 TRACE(("...may pop %s 0x%04X %s\n", #name, xw->name, traceIFlags(xw->name))) 7596#define PUSH_DATA(name) \ 7597 s->stack[s->used].name = xw->name;\ 7598 TRACE(("...may pop %s %d\n", #name, xw->name)) 7599 PUSH_FLAG(flags); 7600#if OPT_ISO_COLORS 7601 PUSH_DATA(sgr_foreground); 7602 PUSH_DATA(sgr_background); 7603 PUSH_DATA(sgr_38_xcolors); 7604#endif 7605 } 7606 s->used++; 7607} 7608 7609#define IAttrClr(dst,bits) dst = dst & (IAttr) ~(bits) 7610 7611void 7612xtermReportSGR(XtermWidget xw, XTermRect *value) 7613{ 7614 TScreen *screen = TScreenOf(xw); 7615 char reply[BUFSIZ]; 7616 CellData working; 7617 int row, col; 7618 Boolean first = True; 7619 7620 TRACE(("xtermReportSGR %d,%d - %d,%d\n", 7621 value->top, value->left, 7622 value->bottom, value->right)); 7623 7624 memset(&working, 0, sizeof(working)); 7625 for (row = value->top - 1; row < value->bottom; ++row) { 7626 LineData *ld = getLineData(screen, row); 7627 if (ld == 0) 7628 continue; 7629 for (col = value->left - 1; col < value->right; ++col) { 7630 if (first) { 7631 first = False; 7632 saveCellData(screen, &working, 0, ld, NULL, col); 7633 } 7634 working.attribs &= ld->attribs[col]; 7635#if OPT_ISO_COLORS 7636 if (working.attribs & FG_COLOR 7637 && GetCellColorFG(working.color) 7638 != GetCellColorFG(ld->color[col])) { 7639 IAttrClr(working.attribs, FG_COLOR); 7640 } 7641 if (working.attribs & BG_COLOR 7642 && GetCellColorBG(working.color) 7643 != GetCellColorBG(ld->color[col])) { 7644 IAttrClr(working.attribs, BG_COLOR); 7645 } 7646#endif 7647 } 7648 } 7649 xtermFormatSGR(xw, reply, 7650 working.attribs, 7651 GetCellColorFG(working.color), 7652 GetCellColorBG(working.color)); 7653 unparseputc1(xw, ANSI_CSI); 7654 unparseputs(xw, reply); 7655 unparseputc(xw, 'm'); 7656 unparse_end(xw); 7657} 7658 7659void 7660xtermPopSGR(XtermWidget xw) 7661{ 7662 SavedSGR *s = &(xw->saved_sgr); 7663 7664 TRACE(("xtermPopSGR %d\n", s->used)); 7665 7666 if (s->used > 0) { 7667 if (s->used-- <= MAX_SAVED_SGR) { 7668 IFlags mask = s->stack[s->used].mask; 7669 Boolean changed = False; 7670 7671 TRACE(("...mask %#x %s\n", mask, traceIStack(mask))); 7672 TRACE(("...old: %s\n", traceIFlags(xw->flags))); 7673 TRACE(("...new: %s\n", traceIFlags(s->stack[s->used].flags))); 7674#define POP_FLAG(name) \ 7675 if (xBIT(ps##name - 1) & mask) { \ 7676 if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \ 7677 changed = True; \ 7678 UIntClr(xw->flags, name); \ 7679 UIntSet(xw->flags, (s->stack[s->used].flags & name)); \ 7680 TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \ 7681 } \ 7682 } 7683#define POP_FLAG2(name,part) \ 7684 if (xBIT(ps##name - 1) & mask) { \ 7685 if ((xw->flags & part) ^ (s->stack[s->used].flags & part)) { \ 7686 changed = True; \ 7687 UIntClr(xw->flags, part); \ 7688 UIntSet(xw->flags, (s->stack[s->used].flags & part)); \ 7689 TRACE(("...pop " #part " = %s\n", BtoS(xw->flags & part))); \ 7690 } \ 7691 } 7692#define POP_DATA(name,value) \ 7693 if (xBIT(ps##name - 1) & mask) { \ 7694 Bool always = False; \ 7695 if ((xw->flags & name) ^ (s->stack[s->used].flags & name)) { \ 7696 always = changed = True; \ 7697 UIntClr(xw->flags, name); \ 7698 UIntSet(xw->flags, (s->stack[s->used].flags & name)); \ 7699 TRACE(("...pop " #name " = %s\n", BtoS(xw->flags & name))); \ 7700 } \ 7701 if (always || (xw->value != s->stack[s->used].value)) { \ 7702 TRACE(("...pop " #name " %d => %d\n", xw->value, s->stack[s->used].value)); \ 7703 xw->value = s->stack[s->used].value; \ 7704 changed = True; \ 7705 } \ 7706 } 7707 POP_FLAG(BOLD); 7708 POP_FLAG(UNDERLINE); 7709 POP_FLAG(BLINK); 7710 POP_FLAG(INVERSE); 7711 POP_FLAG(INVISIBLE); 7712#if OPT_WIDE_ATTRS 7713 if (xBIT(psATR_ITALIC - 1) & mask) { 7714 xtermUpdateItalics(xw, s->stack[s->used].flags, xw->flags); 7715 } 7716 POP_FLAG(ATR_ITALIC); 7717 POP_FLAG(ATR_FAINT); 7718 POP_FLAG(ATR_STRIKEOUT); 7719 POP_FLAG(ATR_DBL_UNDER); 7720#endif 7721#if OPT_ISO_COLORS 7722 POP_DATA(FG_COLOR, sgr_foreground); 7723 POP_DATA(BG_COLOR, sgr_background); 7724 POP_DATA(BG_COLOR, sgr_38_xcolors); 7725#if OPT_DIRECT_COLOR 7726 POP_FLAG2(FG_COLOR, ATR_DIRECT_FG); 7727 POP_FLAG2(BG_COLOR, ATR_DIRECT_BG); 7728#endif 7729 if (changed) { 7730 setExtendedColors(xw); 7731 } 7732#else 7733 (void) changed; 7734#endif 7735 } 7736#if OPT_ISO_COLORS 7737 TRACE(("xtermP -> flags%s, fg=%d bg=%d%s\n", 7738 traceIFlags(xw->flags), 7739 xw->sgr_foreground, 7740 xw->sgr_background, 7741 xw->sgr_38_xcolors ? " (SGR 38)" : "")); 7742#else 7743 TRACE(("xtermP -> flags%s\n", 7744 traceIFlags(xw->flags))); 7745#endif 7746 } 7747} 7748 7749#if OPT_ISO_COLORS 7750static ColorSlot * 7751allocColorSlot(XtermWidget xw, int slot) 7752{ 7753 SavedColors *s = &(xw->saved_colors); 7754 ColorSlot *result = NULL; 7755 7756 if (slot >= 0 && slot < MAX_SAVED_SGR) { 7757 if (s->palettes[slot] == NULL) { 7758 s->palettes[slot] = (ColorSlot *) calloc((size_t) 1, 7759 sizeof(ColorSlot) 7760 + (sizeof(ColorRes) 7761 * MAXCOLORS)); 7762 } 7763 result = s->palettes[slot]; 7764 } 7765 return result; 7766} 7767 7768static void 7769popOldColors(XtermWidget xw, ScrnColors * source) 7770{ 7771 Boolean changed = False; 7772 ScrnColors *target = xw->work.oldColors; 7773 7774 if (source->which != target->which) { 7775 changed = True; 7776 } else { 7777 int n; 7778 for (n = 0; n < NCOLORS; ++n) { 7779 if (COLOR_DEFINED(source, n)) { 7780 if (COLOR_DEFINED(target, n)) { 7781 if (source->colors[n] != target->colors[n]) { 7782 changed = True; 7783 break; 7784 } 7785 } else { 7786 changed = True; 7787 break; 7788 } 7789 } else if (COLOR_DEFINED(target, n)) { 7790 changed = True; 7791 break; 7792 } 7793 } 7794 } 7795 if (changed) { 7796 ChangeColors(xw, source); 7797 UpdateOldColors(xw, source); 7798 } 7799} 7800#endif /* OPT_ISO_COLORS */ 7801 7802#define DiffColorSlot(d,s,n) (memcmp((d), (s), (n) * sizeof(ColorRes)) ? True : False) 7803#define CopyColorSlot(d,s,n) memcpy((d), (s), (n) * sizeof(ColorRes)) 7804 7805/* 7806 * By default, a "push" increments the stack after copying to the current 7807 * slot. But a specific target allows one to copy into a specific slot. 7808 */ 7809void 7810xtermPushColors(XtermWidget xw, int value) 7811{ 7812#if OPT_ISO_COLORS 7813 SavedColors *s = &(xw->saved_colors); 7814 int pushed = s->used; 7815 int actual = (value <= 0) ? pushed : (value - 1); 7816 7817 TRACE(("xtermPushColors %d:%d\n", actual, pushed)); 7818 if (actual < MAX_SAVED_SGR && actual >= 0) { 7819 TScreen *screen = TScreenOf(xw); 7820 ColorSlot *palette; 7821 7822 if ((palette = allocColorSlot(xw, actual)) != NULL) { 7823 GetColors(xw, &(palette->base)); 7824 CopyColorSlot(&(palette->ansi[0]), screen->Acolors, MAXCOLORS); 7825 if (value < 0) { 7826 s->used++; 7827 if (s->last < s->used) 7828 s->last = s->used; 7829 } else { 7830 s->used = value; 7831 } 7832 } 7833 } 7834#else 7835 (void) xw; 7836 (void) value; 7837#endif 7838} 7839 7840void 7841xtermPopColors(XtermWidget xw, int value) 7842{ 7843#if OPT_ISO_COLORS 7844 SavedColors *s = &(xw->saved_colors); 7845 int popped = (s->used - 1); 7846 int actual = (value <= 0) ? popped : (value - 1); 7847 7848 TRACE(("xtermPopColors %d:%d\n", actual, popped)); 7849 if (actual < MAX_SAVED_SGR && actual >= 0) { 7850 TScreen *screen = TScreenOf(xw); 7851 ColorSlot *palette; 7852 7853 if ((palette = s->palettes[actual]) != NULL) { 7854 Boolean changed = DiffColorSlot(screen->Acolors, 7855 palette->ansi, 7856 MAXCOLORS); 7857 7858 GetOldColors(xw); 7859 popOldColors(xw, &(palette->base)); 7860 CopyColorSlot(screen->Acolors, &(palette->ansi[0]), MAXCOLORS); 7861 s->used = actual; 7862 if (changed) 7863 xtermRepaint(xw); 7864 } 7865 } 7866#else 7867 (void) xw; 7868 (void) value; 7869#endif 7870} 7871 7872void 7873xtermReportColors(XtermWidget xw) 7874{ 7875 ANSI reply; 7876 SavedColors *s = &(xw->saved_colors); 7877 7878 memset(&reply, 0, sizeof(reply)); 7879 reply.a_type = ANSI_CSI; 7880 reply.a_pintro = '?'; 7881 reply.a_param[reply.a_nparam++] = (ParmType) s->used; 7882 reply.a_param[reply.a_nparam++] = (ParmType) s->last; 7883 reply.a_inters = '#'; 7884 reply.a_final = 'Q'; 7885 unparseseq(xw, &reply); 7886} 7887#endif /* OPT_XTERM_SGR */ 7888