button.c revision 5104ee6e
1/* $XTermId: button.c,v 1.669 2025/01/03 00:20:00 tom Exp $ */ 2 3/* 4 * Copyright 1999-2024,2025 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/* 56button.c Handles button events in the terminal emulator. 57 does cut/paste operations, change modes via menu, 58 passes button events through to some applications. 59 J. Gettys. 60*/ 61 62#include <xterm.h> 63 64#include <stdio.h> 65#include <ctype.h> 66#include <assert.h> 67 68#include <X11/Xatom.h> 69#include <X11/Xmu/Atoms.h> 70#include <X11/Xmu/StdSel.h> 71 72#include <xutf8.h> 73#include <fontutils.h> 74 75#include <data.h> 76#include <error.h> 77#include <menu.h> 78#include <charclass.h> 79#include <xstrings.h> 80#include <xterm_io.h> 81 82#if OPT_SELECT_REGEX 83#if defined(HAVE_PCRE2POSIX_H) 84#include <pcre2posix.h> 85 86/* pcre2 used to provide its "POSIX" entrypoints using the same names as the 87 * standard ones in the C runtime, but that never worked because the linker 88 * would use the C runtime. Debian patched the library to fix this symbol 89 * conflict, but overlooked the header file, and Debian's patch was made 90 * obsolete when pcre2 was changed early in 2019 to provide different names. 91 * 92 * Here is a workaround to make the older version of Debian's package work. 93 */ 94#if !defined(PCRE2regcomp) && defined(HAVE_PCRE2REGCOMP) 95 96#undef regcomp 97#undef regexec 98#undef regfree 99 100#ifdef __cplusplus 101extern "C" { 102#endif 103 PCRE2POSIX_EXP_DECL int PCRE2regcomp(regex_t *, const char *, int); 104 PCRE2POSIX_EXP_DECL int PCRE2regexec(const regex_t *, const char *, size_t, 105 regmatch_t *, int); 106 PCRE2POSIX_EXP_DECL void PCRE2regfree(regex_t *); 107#ifdef __cplusplus 108} /* extern "C" */ 109#endif 110#define regcomp(r,s,n) PCRE2regcomp(r,s,n) 111#define regexec(r,s,n,m,x) PCRE2regexec(r,s,n,m,x) 112#define regfree(r) PCRE2regfree(r) 113#endif 114/* end workaround... */ 115#elif defined(HAVE_PCREPOSIX_H) 116#include <pcreposix.h> 117#else /* POSIX regex.h */ 118#include <sys/types.h> 119#include <regex.h> 120#endif 121#endif /* OPT_SELECT_REGEX */ 122 123#ifdef HAVE_X11_TRANSLATEI_H 124#include <X11/ConvertI.h> 125#include <X11/TranslateI.h> 126#else 127extern String _XtPrintXlations(Widget w, 128 XtTranslations xlations, 129 Widget accelWidget, 130 _XtBoolean includeRHS); 131#endif 132 133#define PRIMARY_NAME "PRIMARY" 134#define CLIPBOARD_NAME "CLIPBOARD" 135#define SECONDARY_NAME "SECONDARY" 136 137#define AtomToSelection(d,n) \ 138 (((n) == XA_CLIPBOARD(d)) \ 139 ? CLIPBOARD_CODE \ 140 : (((n) == XA_SECONDARY) \ 141 ? SECONDARY_CODE \ 142 : PRIMARY_CODE)) 143 144#define isSelectionCode(n) ((n) >= PRIMARY_CODE) 145#define CutBufferToCode(n) ((n) + MAX_SELECTION_CODES) 146#define okSelectionCode(n) (isSelectionCode(n) ? (n) : PRIMARY_CODE) 147 148#if OPT_WIDE_CHARS 149#include <ctype.h> 150#include <wcwidth.h> 151#else 152#define CharacterClass(value) \ 153 charClass[(value) & (int)((sizeof(charClass)/sizeof(charClass[0]))-1)] 154#endif 155 156 /* 157 * We'll generally map rows to indices when doing selection. 158 * Simplify that with a macro. 159 * 160 * Note that ROW2INX() is safe to use with auto increment/decrement for 161 * the row expression since that is evaluated once. 162 */ 163#define GET_LINEDATA(screen, row) \ 164 getLineData(screen, ROW2INX(screen, row)) 165 166#define MaxMouseBtn 5 167 168#define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease) 169#define IsKeyEvent(event) ((event)->type == KeyPress || (event)->type == KeyRelease) 170 171#define Coordinate(s,c) ((c)->row * MaxCols(s) + (c)->col) 172 173static const CELL zeroCELL = 174{0, 0}; 175 176#if OPT_DEC_LOCATOR 177static Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event); 178static void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event); 179#endif /* OPT_DEC_LOCATOR */ 180 181/* Multi-click handling */ 182#if OPT_READLINE 183static Time lastButtonDownTime = 0; 184static int ExtendingSelection = 0; 185static Time lastButton3UpTime = 0; 186static Time lastButton3DoubleDownTime = 0; 187static CELL lastButton3; /* At the release time */ 188#endif /* OPT_READLINE */ 189 190static Char *SaveText(TScreen *screen, int row, int scol, int ecol, 191 Char *lp, int *eol); 192static int Length(TScreen *screen, int row, int scol, int ecol); 193static void ComputeSelect(XtermWidget xw, const CELL *startc, const CELL *endc, 194 Bool extend, Bool normal); 195static void EditorButton(XtermWidget xw, XButtonEvent *event); 196static void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal 197 num_params, Bool use_cursor_loc); 198static void ExtendExtend(XtermWidget xw, const CELL *cell); 199static void PointToCELL(TScreen *screen, int y, int x, CELL *cell); 200static void ReHiliteText(XtermWidget xw, const CELL *first, const CELL *last); 201static void SaltTextAway(XtermWidget xw, int which, const CELL *cellc, const CELL *cell); 202static void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params); 203static void SelectionReceived PROTO_XT_SEL_CB_ARGS; 204static void StartSelect(XtermWidget xw, const CELL *cell); 205static void TrackDown(XtermWidget xw, XButtonEvent *event); 206static void TrackText(XtermWidget xw, const CELL *first, const CELL *last); 207static void UnHiliteText(XtermWidget xw); 208static void _OwnSelection(XtermWidget xw, String *selections, Cardinal count); 209static void do_select_end(XtermWidget xw, XEvent *event, String *params, 210 Cardinal *num_params, Bool use_cursor_loc); 211 212#define MOUSE_LIMIT (255 - 32) 213 214/* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */ 215#define EXT_MOUSE_LIMIT (2047 - 32) 216#define EXT_MOUSE_START (127 - 32) 217 218static int 219MouseLimit(TScreen *screen) 220{ 221 int mouse_limit; 222 223 switch (screen->extend_coords) { 224 default: 225 mouse_limit = MOUSE_LIMIT; 226 break; 227 case SET_EXT_MODE_MOUSE: 228 mouse_limit = EXT_MOUSE_LIMIT; 229 break; 230 case SET_SGR_EXT_MODE_MOUSE: 231 case SET_URXVT_EXT_MODE_MOUSE: 232 case SET_PIXEL_POSITION_MOUSE: 233 mouse_limit = -1; 234 break; 235 } 236 return mouse_limit; 237} 238 239static unsigned 240EmitMousePosition(TScreen *screen, Char line[], unsigned count, int value) 241{ 242 int mouse_limit = MouseLimit(screen); 243 244 /* 245 * Add pointer position to key sequence 246 * 247 * In extended mode we encode large positions as two-byte UTF-8. 248 * 249 * NOTE: historically, it was possible to emit 256, which became 250 * zero by truncation to 8 bits. While this was arguably a bug, 251 * it's also somewhat useful as a past-end marker. We preserve 252 * this behavior for both normal and extended mouse modes. 253 */ 254 switch (screen->extend_coords) { 255 default: 256 if (value == mouse_limit) { 257 line[count++] = CharOf(0); 258 } else { 259 line[count++] = CharOf(' ' + value + 1); 260 } 261 break; 262 case SET_EXT_MODE_MOUSE: 263 if (value == mouse_limit) { 264 line[count++] = CharOf(0); 265 } else if (value < EXT_MOUSE_START) { 266 line[count++] = CharOf(' ' + value + 1); 267 } else { 268 value += ' ' + 1; 269 line[count++] = CharOf(0xC0 + (value >> 6)); 270 line[count++] = CharOf(0x80 + (value & 0x3F)); 271 } 272 break; 273 case SET_SGR_EXT_MODE_MOUSE: 274 case SET_URXVT_EXT_MODE_MOUSE: 275 case SET_PIXEL_POSITION_MOUSE: 276 count += (unsigned) sprintf((char *) line + count, "%d", value + 1); 277 break; 278 } 279 return count; 280} 281 282static unsigned 283EmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count) 284{ 285 switch (screen->extend_coords) { 286 case SET_SGR_EXT_MODE_MOUSE: 287 case SET_URXVT_EXT_MODE_MOUSE: 288 case SET_PIXEL_POSITION_MOUSE: 289 line[count++] = ';'; 290 break; 291 } 292 return count; 293} 294 295enum { 296 scanMods, 297 scanKey, 298 scanColon, 299 scanFunc, 300 scanArgs 301}; 302 303#if OPT_TRACE > 1 304static const char * 305visibleScan(int mode) 306{ 307 const char *result = "?"; 308#define DATA(name) case name: result = #name; break 309 switch (mode) { 310 DATA(scanMods); 311 DATA(scanKey); 312 DATA(scanColon); 313 DATA(scanFunc); 314 DATA(scanArgs); 315 } 316#undef DATA 317 return result; 318} 319#endif 320 321#define L_BRACK '<' 322#define R_BRACK '>' 323#define L_PAREN '(' 324#define R_PAREN ')' 325 326static char * 327scanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last) 328{ 329 char *target = source; 330 331 *first = *last = 0; 332 if (IsEmpty(target)) { 333 target = NULL; 334 } else { 335 do { 336 char ch; 337 while (IsSpace(*target)) 338 target++; 339 *first = (unsigned) (target - source); 340 switch (*this_is = *next_is) { 341 case scanMods: 342 while ((ch = *target)) { 343 if (IsSpace(ch)) { 344 break; 345 } else if (ch == L_BRACK) { 346 *next_is = scanKey; 347 break; 348 } else if (ch == ':') { 349 *next_is = scanColon; 350 break; 351 } else if (ch == '~' && target != source) { 352 break; 353 } 354 target++; 355 } 356 break; 357 case scanKey: 358 while ((ch = *target)) { 359 if (IsSpace(ch)) { 360 break; 361 } else if (ch == ':') { 362 *next_is = scanColon; 363 break; 364 } 365 target++; 366 if (ch == R_BRACK) 367 break; 368 } 369 break; 370 case scanColon: 371 *next_is = scanFunc; 372 target++; 373 break; 374 case scanFunc: 375 while ((ch = *target)) { 376 if (IsSpace(ch)) { 377 break; 378 } else if (ch == L_PAREN) { 379 *next_is = scanArgs; 380 break; 381 } 382 target++; 383 } 384 break; 385 case scanArgs: 386 while ((ch = *target)) { 387 if (ch == R_PAREN) { 388 target++; 389 *next_is = scanFunc; 390 break; 391 } 392 target++; 393 } 394 break; 395 } 396 *last = (unsigned) (target - source); 397 if (*target == '\n') { 398 *next_is = scanMods; 399 target++; 400 } 401 } while (*first == *last); 402 } 403 return target; 404} 405 406void 407xtermButtonInit(XtermWidget xw) 408{ 409 Widget w = (Widget) xw; 410 XErrorHandler save = XSetErrorHandler(ignore_x11_error); 411 XtTranslations xlations; 412 Widget xcelerat; 413 String result; 414 415 XtVaGetValues(w, 416 XtNtranslations, &xlations, 417 XtNaccelerators, &xcelerat, 418 (XtPointer) 0); 419 result = _XtPrintXlations(w, xlations, xcelerat, True); 420 if (result) { 421 static const char *table[] = 422 { 423 "insert-selection", 424 "select-end", 425 "select-extend", 426 "select-start", 427 "start-extend", 428 }; 429 char *data = x_strdup(result); 430 char *next; 431 int state = scanMods; 432 int state2 = scanMods; 433 unsigned first; 434 unsigned last; 435 int have_button = -1; 436 Bool want_button = False; 437 Bool have_shift = False; 438 unsigned allowed = 0; 439 unsigned disallow = 0; 440 441 TRACE(("xtermButtonInit length %ld\n", (long) strlen(result))); 442 xw->keyboard.print_translations = data; 443 while ((next = scanTrans(data, &state, &state2, &first, &last)) != NULL) { 444 unsigned len = (last - first); 445 TRACE2(("parse %s:%d..%d '%.*s'\n", 446 visibleScan(state), first, last, 447 len, data + first)); 448 if (state == scanMods) { 449 if (len > 1 && data[first] == '~') { 450 len--; 451 first++; 452 } 453 if (len == 7 && !x_strncasecmp(data + first, "button", len - 1)) { 454 have_button = data[first + 6] - '0'; 455 } else if (len == 5 && !x_strncasecmp(data + first, "shift", len)) { 456 have_shift = True; 457 } 458 } else if (state == scanKey) { 459 if (!x_strncasecmp(data + first, "<buttonpress>", len) || 460 !x_strncasecmp(data + first, "<buttonrelease>", len)) { 461 want_button = True; 462 } else if (want_button) { 463 have_button = data[first] - '0'; 464 want_button = False; 465 } 466 } else if (state == scanFunc && have_button > 0) { 467 Cardinal n; 468 unsigned bmask = 1U << (have_button - 1); 469 for (n = 0; n < XtNumber(table); ++n) { 470 if (!x_strncasecmp(table[n], data + first, len)) { 471 TRACE(("...button %d: %s%s\n", 472 have_button, table[n], 473 have_shift ? " (disallow)" : "")); 474 if (have_shift) 475 disallow |= bmask; 476 else 477 allowed |= bmask; 478 break; 479 } 480 } 481 } 482 if (state2 == scanMods && state >= scanColon) { 483 have_button = -1; 484 want_button = False; 485 have_shift = False; 486 } 487 state = state2; 488 data = next; 489 } 490 XFree((char *) result); 491 xw->keyboard.shift_buttons = allowed & ~disallow; 492#if OPT_TRACE 493 if (xw->keyboard.shift_buttons) { 494 int button = 0; 495 unsigned mask = xw->keyboard.shift_buttons; 496 TRACE(("...Buttons used for selection that can be overridden:")); 497 while (mask != 0) { 498 ++button; 499 if ((mask & 1) != 0) 500 TRACE((" %d", button)); 501 mask >>= 1; 502 } 503 TRACE(("\n")); 504 } else { 505 TRACE(("...No buttons used with selection can be overridden\n")); 506 } 507#endif 508 } 509 XSetErrorHandler(save); 510} 511 512/* 513 * Shift and control are regular X11 modifiers, but meta is not: 514 * + X10 (which had no xmodmap utility) had a meta mask, but X11 did not. 515 * + X11R1 introduced xmodmap, along with the current set of modifier masks. 516 * The meta key has been assumed to be mod1 since X11R1. 517 * The initial xterm logic in X11 was different, but gave the same result. 518 * + X11R2 modified xterm was to eliminate the X10 table which provided part of 519 * the meta logic. 520 * + X11R3 modified Xt, making Meta_L and Meta_R assignable via xmodmap, and 521 * equating Alt with Meta. Neither Alt/Meta are modifiers, but Alt is more 522 * likely to be on the keyboard. This release also added keymap tables for 523 * the server; Meta was used frequently in HP keymaps, which were the most 524 * extensive set of keymaps. 525 * + X11R4 mentions Meta in the ICCCM, stating that if Meta_L or Meta_R are 526 * found in the keysyms for a given modifier, that the client should use 527 * that modifier. 528 * 529 * This function follows the ICCCM, picking the modifier which contains the 530 * Meta_L/Meta_R keysyms (if available), falling back to the Alt_L/Alt_R 531 * (as per X11R3), and ultimately to mod1 (per X11R1). 532 */ 533static unsigned 534MetaMask(XtermWidget xw) 535{ 536#if OPT_NUM_LOCK 537 unsigned meta = xw->work.meta_mods; 538 if (meta == 0) 539 meta = xw->work.alt_mods; 540 if (meta == 0) 541 meta = Mod1Mask; 542#else 543 unsigned meta = Mod1Mask; 544 (void) xw; 545#endif 546 return meta; 547} 548 549/* 550 * Returns a mask of the modifiers we may use for modifying the mouse protocol 551 * response strings. 552 */ 553static unsigned 554OurModifiers(XtermWidget xw) 555{ 556 return (ShiftMask 557 | ControlMask 558 | MetaMask(xw)); 559} 560 561/* 562 * The actual check for the shift-mask, to see if it should tell xterm to 563 * override mouse-protocol in favor of select/paste actions depends upon 564 * whether the shiftEscape resource is set to true/always vs false/never. 565 */ 566static Boolean 567ShiftOverride(XtermWidget xw, unsigned state, int button) 568{ 569 unsigned check = (state & OurModifiers(xw)); 570 Boolean result = False; 571 572 if (check & ShiftMask) { 573 if (xw->keyboard.shift_escape == ssFalse || 574 xw->keyboard.shift_escape == ssNever) { 575 result = True; 576 } else if (xw->keyboard.shift_escape == ssTrue) { 577 /* 578 * Check if the button is one that we found does not directly use 579 * the shift-modifier in its bindings to select/copy actions. 580 */ 581 if (button > 0 && button <= MaxMouseBtn) { 582 if (xw->keyboard.shift_buttons & (1U << (button - 1))) { 583 result = True; 584 } 585 } else { 586 result = True; /* unlikely, and we don't care */ 587 } 588 } 589 } 590 TRACE2(("ShiftOverride ( %#x -> %#x ) %d\n", state, check, result)); 591 return result; 592} 593 594/* 595 * Normally xterm treats the shift-modifier specially when the mouse protocol 596 * is active. The translations resource binds otherwise unmodified button 597 * for these mouse-related events: 598 * 599 * ~Meta <Btn1Down>:select-start() \n\ 600 * ~Meta <Btn1Motion>:select-extend() \n\ 601 * ~Ctrl ~Meta <Btn2Up>:insert-selection(SELECT, CUT_BUFFER0) \n\ 602 * ~Ctrl ~Meta <Btn3Down>:start-extend() \n\ 603 * ~Meta <Btn3Motion>:select-extend() \n\ 604 * <BtnUp>:select-end(SELECT, CUT_BUFFER0) \n\ 605 * 606 * There is no API in the X libraries which would tell us if a given mouse 607 * button is bound to one of these actions. These functions make the choice 608 * configurable. 609 */ 610static Bool 611InterpretButton(XtermWidget xw, XButtonEvent *event) 612{ 613 Bool result = False; 614 615 if (ShiftOverride(xw, event->state, (int) event->button)) { 616 TRACE(("...shift-button #%d overrides mouse-protocol\n", event->button)); 617 result = True; 618 } 619 return result; 620} 621 622#define Button1Index 8 /* X.h should have done this */ 623 624static int 625MotionButton(unsigned state) 626{ 627 unsigned bmask = state >> Button1Index; 628 int result = 1; 629 630 if (bmask != 0) { 631 while (!(bmask & 1)) { 632 ++result; 633 bmask >>= 1; 634 } 635 } 636 return result; 637} 638 639static Bool 640InterpretEvent(XtermWidget xw, XEvent *event) 641{ 642 Bool result = False; /* if not a button, is motion */ 643 644 if (IsBtnEvent(event)) { 645 result = InterpretButton(xw, (XButtonEvent *) event); 646 } else if (event->type == MotionNotify) { 647 unsigned state = event->xmotion.state; 648 int button = MotionButton(state); 649 650 if (ShiftOverride(xw, state, button)) { 651 TRACE(("...shift-motion #%d (%d,%d) overrides mouse-protocol\n", 652 button, 653 event->xmotion.y, 654 event->xmotion.x)); 655 result = True; 656 } 657 } 658 return result; 659} 660 661#define OverrideEvent(event) InterpretEvent(xw, event) 662#define OverrideButton(event) InterpretButton(xw, event) 663 664/* 665 * Returns true if we handled the event here, and nothing more is needed. 666 */ 667Bool 668SendMousePosition(XtermWidget xw, XEvent *event) 669{ 670 XButtonEvent *my_event = (XButtonEvent *) event; 671 Bool result = False; 672 673 switch (okSendMousePos(xw)) { 674 case MOUSE_OFF: 675 /* If send_mouse_pos mode isn't on, we shouldn't be here */ 676 break; 677 678 case BTN_EVENT_MOUSE: 679 case ANY_EVENT_MOUSE: 680 if (!OverrideEvent(event)) { 681 /* xterm extension for motion reporting. June 1998 */ 682 /* EditorButton() will distinguish between the modes */ 683 switch (event->type) { 684 case MotionNotify: 685 my_event->button = 0; 686 /* FALLTHRU */ 687 case ButtonPress: 688 /* FALLTHRU */ 689 case ButtonRelease: 690 EditorButton(xw, my_event); 691 result = True; 692 break; 693 } 694 } 695 break; 696 697 case X10_MOUSE: /* X10 compatibility sequences */ 698 if (IsBtnEvent(event)) { 699 if (!OverrideButton(my_event)) { 700 if (my_event->type == ButtonPress) 701 EditorButton(xw, my_event); 702 result = True; 703 } 704 } 705 break; 706 707 case VT200_HIGHLIGHT_MOUSE: /* DEC vt200 hilite tracking */ 708 if (IsBtnEvent(event)) { 709 if (!OverrideButton(my_event)) { 710 if (my_event->type == ButtonPress && 711 my_event->button == Button1) { 712 TrackDown(xw, my_event); 713 } else { 714 EditorButton(xw, my_event); 715 } 716 result = True; 717 } 718 } 719 break; 720 721 case VT200_MOUSE: /* DEC vt200 compatible */ 722 if (IsBtnEvent(event)) { 723 if (!OverrideButton(my_event)) { 724 EditorButton(xw, my_event); 725 result = True; 726 } 727 } 728 break; 729 730 case DEC_LOCATOR: 731#if OPT_DEC_LOCATOR 732 if (IsBtnEvent(event) || event->type == MotionNotify) { 733 result = SendLocatorPosition(xw, my_event); 734 } 735#endif /* OPT_DEC_LOCATOR */ 736 break; 737 } 738 return result; 739} 740 741#if OPT_DEC_LOCATOR 742 743#define LocatorCoords( row, col, x, y, oor ) \ 744 if( screen->locator_pixels ) { \ 745 (oor)=False; (row) = (y)+1; (col) = (x)+1; \ 746 /* Limit to screen dimensions */ \ 747 if ((row) < 1) (row) = 1,(oor)=True; \ 748 else if ((row) > screen->border*2+Height(screen)) \ 749 (row) = screen->border*2+Height(screen),(oor)=True; \ 750 if ((col) < 1) (col) = 1,(oor)=True; \ 751 else if ((col) > OriginX(screen)*2+Width(screen)) \ 752 (col) = OriginX(screen)*2+Width(screen),(oor)=True; \ 753 } else { \ 754 (oor)=False; \ 755 /* Compute character position of mouse pointer */ \ 756 (row) = ((y) - screen->border) / FontHeight(screen); \ 757 (col) = ((x) - OriginX(screen)) / FontWidth(screen); \ 758 /* Limit to screen dimensions */ \ 759 if ((row) < 0) (row) = 0,(oor)=True; \ 760 else if ((row) > screen->max_row) \ 761 (row) = screen->max_row,(oor)=True; \ 762 if ((col) < 0) (col) = 0,(oor)=True; \ 763 else if ((col) > screen->max_col) \ 764 (col) = screen->max_col,(oor)=True; \ 765 (row)++; (col)++; \ 766 } 767 768static Bool 769SendLocatorPosition(XtermWidget xw, XButtonEvent *event) 770{ 771 ANSI reply; 772 TScreen *screen = TScreenOf(xw); 773 int row, col; 774 Bool oor; 775 int button; 776 unsigned state; 777 778 /* Make sure the event is an appropriate type */ 779 if (IsBtnEvent(event)) { 780 if (OverrideButton(event)) 781 return (False); 782 } else { 783 if (!screen->loc_filter) 784 return (False); 785 } 786 787 if ((event->type == ButtonPress && 788 !(screen->locator_events & LOC_BTNS_DN)) || 789 (event->type == ButtonRelease && 790 !(screen->locator_events & LOC_BTNS_UP))) 791 return (True); 792 793 if (event->type == MotionNotify) { 794 CheckLocatorPosition(xw, event); 795 return (True); 796 } 797 798 /* get button # */ 799 button = (int) event->button - 1; 800 801 LocatorCoords(row, col, event->x, event->y, oor); 802 803 /* 804 * DECterm mouse: 805 * 806 * ESCAPE '[' event ; mask ; row ; column '&' 'w' 807 */ 808 memset(&reply, 0, sizeof(reply)); 809 reply.a_type = ANSI_CSI; 810 811 if (oor) { 812 reply.a_nparam = 1; 813 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ 814 reply.a_inters = '&'; 815 reply.a_final = 'w'; 816 unparseseq(xw, &reply); 817 818 if (screen->locator_reset) { 819 MotionOff(screen, xw); 820 screen->send_mouse_pos = MOUSE_OFF; 821 } 822 return (True); 823 } 824 825 /* 826 * event: 827 * 1 no buttons 828 * 2 left button down 829 * 3 left button up 830 * 4 middle button down 831 * 5 middle button up 832 * 6 right button down 833 * 7 right button up 834 * 8 M4 down 835 * 9 M4 up 836 */ 837 reply.a_nparam = 4; 838 switch (event->type) { 839 case ButtonPress: 840 reply.a_param[0] = (ParmType) (2 + (button << 1)); 841 break; 842 case ButtonRelease: 843 reply.a_param[0] = (ParmType) (3 + (button << 1)); 844 break; 845 default: 846 return (True); 847 } 848 /* 849 * mask: 850 * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 851 * M4 down left down middle down right down 852 * 853 * Notice that Button1 (left) and Button3 (right) are swapped in the mask. 854 * Also, mask should be the state after the button press/release, 855 * X provides the state not including the button press/release. 856 */ 857 state = (event->state 858 & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8; 859 /* update mask to "after" state */ 860 state ^= ((unsigned) (1 << button)); 861 /* swap Button1 & Button3 */ 862 state = ((state & (unsigned) ~(4 | 1)) 863 | ((state & 1) ? 4 : 0) 864 | ((state & 4) ? 1 : 0)); 865 866 reply.a_param[1] = (ParmType) state; 867 reply.a_param[2] = (ParmType) row; 868 reply.a_param[3] = (ParmType) col; 869 reply.a_inters = '&'; 870 reply.a_final = 'w'; 871 872 unparseseq(xw, &reply); 873 874 if (screen->locator_reset) { 875 MotionOff(screen, xw); 876 screen->send_mouse_pos = MOUSE_OFF; 877 } 878 879 /* 880 * DECterm turns the Locator off if a button is pressed while a filter 881 * rectangle is active. This might be a bug, but I don't know, so I'll 882 * emulate it anyway. 883 */ 884 if (screen->loc_filter) { 885 screen->send_mouse_pos = MOUSE_OFF; 886 screen->loc_filter = False; 887 screen->locator_events = 0; 888 MotionOff(screen, xw); 889 } 890 891 return (True); 892} 893 894/* 895 * mask: 896 * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 897 * M4 down left down middle down right down 898 * 899 * Button1 (left) and Button3 (right) are swapped in the mask relative to X. 900 */ 901#define ButtonState(state, mask) \ 902{ int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8); \ 903 /* swap Button1 & Button3 */ \ 904 (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0); \ 905} 906 907void 908GetLocatorPosition(XtermWidget xw) 909{ 910 ANSI reply; 911 TScreen *screen = TScreenOf(xw); 912 Window root, child; 913 int rx, ry, x, y; 914 unsigned int mask = 0; 915 int row = 0, col = 0; 916 Bool oor = False; 917 Bool ret = False; 918 int state; 919 920 /* 921 * DECterm turns the Locator off if the position is requested while a 922 * filter rectangle is active. This might be a bug, but I don't know, so 923 * I'll emulate it anyways. 924 */ 925 if (screen->loc_filter) { 926 screen->send_mouse_pos = MOUSE_OFF; 927 screen->loc_filter = False; 928 screen->locator_events = 0; 929 MotionOff(screen, xw); 930 } 931 932 memset(&reply, 0, sizeof(reply)); 933 reply.a_type = ANSI_CSI; 934 935 if (okSendMousePos(xw) == DEC_LOCATOR) { 936 ret = XQueryPointer(screen->display, VWindow(screen), &root, 937 &child, &rx, &ry, &x, &y, &mask); 938 if (ret) { 939 LocatorCoords(row, col, x, y, oor); 940 } 941 } 942 if (ret == False || oor) { 943 reply.a_nparam = 1; 944 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ 945 reply.a_inters = '&'; 946 reply.a_final = 'w'; 947 unparseseq(xw, &reply); 948 949 if (screen->locator_reset) { 950 MotionOff(screen, xw); 951 screen->send_mouse_pos = MOUSE_OFF; 952 } 953 return; 954 } 955 956 ButtonState(state, mask); 957 958 reply.a_nparam = 4; 959 reply.a_param[0] = 1; /* Event - 1 = response to locator request */ 960 reply.a_param[1] = (ParmType) state; 961 reply.a_param[2] = (ParmType) row; 962 reply.a_param[3] = (ParmType) col; 963 reply.a_inters = '&'; 964 reply.a_final = 'w'; 965 unparseseq(xw, &reply); 966 967 if (screen->locator_reset) { 968 MotionOff(screen, xw); 969 screen->send_mouse_pos = MOUSE_OFF; 970 } 971} 972 973void 974InitLocatorFilter(XtermWidget xw) 975{ 976 ANSI reply; 977 TScreen *screen = TScreenOf(xw); 978 Window root, child; 979 int rx, ry, x, y; 980 unsigned int mask; 981 int row = 0, col = 0; 982 Bool oor = 0; 983 Bool ret; 984 985 ret = XQueryPointer(screen->display, VWindow(screen), 986 &root, &child, &rx, &ry, &x, &y, &mask); 987 if (ret) { 988 LocatorCoords(row, col, x, y, oor); 989 } 990 if (ret == False || oor) { 991 /* Locator is unavailable */ 992 993 if (screen->loc_filter_top != LOC_FILTER_POS || 994 screen->loc_filter_left != LOC_FILTER_POS || 995 screen->loc_filter_bottom != LOC_FILTER_POS || 996 screen->loc_filter_right != LOC_FILTER_POS) { 997 /* 998 * If any explicit coordinates were received, 999 * report immediately with no coordinates. 1000 */ 1001 memset(&reply, 0, sizeof(reply)); 1002 reply.a_type = ANSI_CSI; 1003 reply.a_nparam = 1; 1004 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ 1005 reply.a_inters = '&'; 1006 reply.a_final = 'w'; 1007 unparseseq(xw, &reply); 1008 1009 if (screen->locator_reset) { 1010 MotionOff(screen, xw); 1011 screen->send_mouse_pos = MOUSE_OFF; 1012 } 1013 } else { 1014 /* 1015 * No explicit coordinates were received, and the pointer is 1016 * unavailable. Report when the pointer re-enters the window. 1017 */ 1018 screen->loc_filter = True; 1019 MotionOn(screen, xw); 1020 } 1021 return; 1022 } 1023 1024 /* 1025 * Adjust rectangle coordinates: 1026 * 1. Replace "LOC_FILTER_POS" with current coordinates 1027 * 2. Limit coordinates to screen size 1028 * 3. make sure top and left are less than bottom and right, resp. 1029 */ 1030 if (screen->locator_pixels) { 1031 rx = OriginX(screen) * 2 + Width(screen); 1032 ry = screen->border * 2 + Height(screen); 1033 } else { 1034 rx = screen->max_col; 1035 ry = screen->max_row; 1036 } 1037 1038#define Adjust( coord, def, max ) \ 1039 if( (coord) == LOC_FILTER_POS ) (coord) = (def); \ 1040 else if ((coord) < 1) (coord) = 1; \ 1041 else if ((coord) > (max)) (coord) = (max) 1042 1043 Adjust(screen->loc_filter_top, row, ry); 1044 Adjust(screen->loc_filter_left, col, rx); 1045 Adjust(screen->loc_filter_bottom, row, ry); 1046 Adjust(screen->loc_filter_right, col, rx); 1047 1048 if (screen->loc_filter_top > screen->loc_filter_bottom) { 1049 ry = screen->loc_filter_top; 1050 screen->loc_filter_top = screen->loc_filter_bottom; 1051 screen->loc_filter_bottom = ry; 1052 } 1053 1054 if (screen->loc_filter_left > screen->loc_filter_right) { 1055 rx = screen->loc_filter_left; 1056 screen->loc_filter_left = screen->loc_filter_right; 1057 screen->loc_filter_right = rx; 1058 } 1059 1060 if ((col < screen->loc_filter_left) || 1061 (col > screen->loc_filter_right) || 1062 (row < screen->loc_filter_top) || 1063 (row > screen->loc_filter_bottom)) { 1064 int state; 1065 1066 /* Pointer is already outside the rectangle - report immediately */ 1067 ButtonState(state, mask); 1068 1069 memset(&reply, 0, sizeof(reply)); 1070 reply.a_type = ANSI_CSI; 1071 reply.a_nparam = 4; 1072 reply.a_param[0] = 10; /* Event - 10 = locator outside filter */ 1073 reply.a_param[1] = (ParmType) state; 1074 reply.a_param[2] = (ParmType) row; 1075 reply.a_param[3] = (ParmType) col; 1076 reply.a_inters = '&'; 1077 reply.a_final = 'w'; 1078 unparseseq(xw, &reply); 1079 1080 if (screen->locator_reset) { 1081 MotionOff(screen, xw); 1082 screen->send_mouse_pos = MOUSE_OFF; 1083 } 1084 return; 1085 } 1086 1087 /* 1088 * Rectangle is set up. Allow pointer tracking 1089 * to detect if the mouse leaves the rectangle. 1090 */ 1091 screen->loc_filter = True; 1092 MotionOn(screen, xw); 1093} 1094 1095static void 1096CheckLocatorPosition(XtermWidget xw, XButtonEvent *event) 1097{ 1098 ANSI reply; 1099 TScreen *screen = TScreenOf(xw); 1100 int row, col; 1101 Bool oor; 1102 1103 LocatorCoords(row, col, event->x, event->y, oor); 1104 1105 /* 1106 * Send report if the pointer left the filter rectangle, if 1107 * the pointer left the window, or if the filter rectangle 1108 * had no coordinates and the pointer re-entered the window. 1109 */ 1110 if (oor || (screen->loc_filter_top == LOC_FILTER_POS) || 1111 (col < screen->loc_filter_left) || 1112 (col > screen->loc_filter_right) || 1113 (row < screen->loc_filter_top) || 1114 (row > screen->loc_filter_bottom)) { 1115 /* Filter triggered - disable it */ 1116 screen->loc_filter = False; 1117 MotionOff(screen, xw); 1118 1119 memset(&reply, 0, sizeof(reply)); 1120 reply.a_type = ANSI_CSI; 1121 if (oor) { 1122 reply.a_nparam = 1; 1123 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ 1124 } else { 1125 int state; 1126 1127 ButtonState(state, event->state); 1128 1129 reply.a_nparam = 4; 1130 reply.a_param[0] = 10; /* Event - 10 = locator outside filter */ 1131 reply.a_param[1] = (ParmType) state; 1132 reply.a_param[2] = (ParmType) row; 1133 reply.a_param[3] = (ParmType) col; 1134 } 1135 1136 reply.a_inters = '&'; 1137 reply.a_final = 'w'; 1138 unparseseq(xw, &reply); 1139 1140 if (screen->locator_reset) { 1141 MotionOff(screen, xw); 1142 screen->send_mouse_pos = MOUSE_OFF; 1143 } 1144 } 1145} 1146#endif /* OPT_DEC_LOCATOR */ 1147 1148#if OPT_READLINE 1149static int 1150isClick1_clean(XtermWidget xw, XButtonEvent *event) 1151{ 1152 TScreen *screen = TScreenOf(xw); 1153 int delta; 1154 1155 /* Disable on Shift-Click-1, including the application-mouse modes */ 1156 if (OverrideButton(event) 1157 || (okSendMousePos(xw) != MOUSE_OFF) 1158 || ExtendingSelection) /* Was moved */ 1159 return 0; 1160 1161 if (event->type != ButtonRelease) 1162 return 0; 1163 1164 if (lastButtonDownTime == (Time) 0) { 1165 /* first time or once in a blue moon */ 1166 delta = screen->multiClickTime + 1; 1167 } else if (event->time > lastButtonDownTime) { 1168 /* most of the time */ 1169 delta = (int) (event->time - lastButtonDownTime); 1170 } else { 1171 /* time has rolled over since lastButtonUpTime */ 1172 delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time); 1173 } 1174 1175 return delta <= screen->multiClickTime; 1176} 1177 1178static int 1179isDoubleClick3(XtermWidget xw, TScreen *screen, XButtonEvent *event) 1180{ 1181 int delta; 1182 1183 if (event->type != ButtonRelease 1184 || OverrideButton(event) 1185 || event->button != Button3) { 1186 lastButton3UpTime = 0; /* Disable the cached info */ 1187 return 0; 1188 } 1189 /* Process Btn3Release. */ 1190 if (lastButton3DoubleDownTime == (Time) 0) { 1191 /* No previous click or once in a blue moon */ 1192 delta = screen->multiClickTime + 1; 1193 } else if (event->time > lastButton3DoubleDownTime) { 1194 /* most of the time */ 1195 delta = (int) (event->time - lastButton3DoubleDownTime); 1196 } else { 1197 /* time has rolled over since lastButton3DoubleDownTime */ 1198 delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time); 1199 } 1200 if (delta <= screen->multiClickTime) { 1201 /* Double click */ 1202 CELL cell; 1203 1204 /* Cannot check ExtendingSelection, since mouse-3 always sets it */ 1205 PointToCELL(screen, event->y, event->x, &cell); 1206 if (isSameCELL(&cell, &lastButton3)) { 1207 lastButton3DoubleDownTime = 0; /* Disable the third click */ 1208 return 1; 1209 } 1210 } 1211 /* Not a double click, memorize for future check. */ 1212 lastButton3UpTime = event->time; 1213 PointToCELL(screen, event->y, event->x, &lastButton3); 1214 return 0; 1215} 1216 1217static int 1218CheckSecondPress3(XtermWidget xw, TScreen *screen, XEvent *event) 1219{ 1220 int delta; 1221 1222 if (event->type != ButtonPress 1223 || OverrideEvent(event) 1224 || event->xbutton.button != Button3) { 1225 lastButton3DoubleDownTime = 0; /* Disable the cached info */ 1226 return 0; 1227 } 1228 /* Process Btn3Press. */ 1229 if (lastButton3UpTime == (Time) 0) { 1230 /* No previous click or once in a blue moon */ 1231 delta = screen->multiClickTime + 1; 1232 } else if (event->xbutton.time > lastButton3UpTime) { 1233 /* most of the time */ 1234 delta = (int) (event->xbutton.time - lastButton3UpTime); 1235 } else { 1236 /* time has rolled over since lastButton3UpTime */ 1237 delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time); 1238 } 1239 if (delta <= screen->multiClickTime) { 1240 CELL cell; 1241 1242 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell); 1243 if (isSameCELL(&cell, &lastButton3)) { 1244 /* A candidate for a double-click */ 1245 lastButton3DoubleDownTime = event->xbutton.time; 1246 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3); 1247 return 1; 1248 } 1249 lastButton3UpTime = 0; /* Disable the info about the previous click */ 1250 } 1251 /* Either too long, or moved, disable. */ 1252 lastButton3DoubleDownTime = 0; 1253 return 0; 1254} 1255 1256static int 1257rowOnCurrentLine(TScreen *screen, 1258 int line, 1259 int *deltap) /* must be XButtonEvent */ 1260{ 1261 int result = 1; 1262 1263 *deltap = 0; 1264 1265 if (line != screen->cur_row) { 1266 int l1, l2; 1267 1268 if (line < screen->cur_row) { 1269 l1 = line; 1270 l2 = screen->cur_row; 1271 } else { 1272 l2 = line; 1273 l1 = screen->cur_row; 1274 } 1275 l1--; 1276 while (++l1 < l2) { 1277 LineData *ld = GET_LINEDATA(screen, l1); 1278 if (!LineTstWrapped(ld)) { 1279 result = 0; 1280 break; 1281 } 1282 } 1283 if (result) { 1284 /* Everything is on one "wrapped line" now */ 1285 *deltap = line - screen->cur_row; 1286 } 1287 } 1288 return result; 1289} 1290 1291static int 1292eventRow(TScreen *screen, XEvent *event) /* must be XButtonEvent */ 1293{ 1294 return (event->xbutton.y - screen->border) / FontHeight(screen); 1295} 1296 1297static int 1298eventColBetween(TScreen *screen, XEvent *event) /* must be XButtonEvent */ 1299{ 1300 /* Correct by half a width - we are acting on a boundary, not on a cell. */ 1301 return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2) 1302 / FontWidth(screen)); 1303} 1304 1305static int 1306ReadLineMovePoint(XtermWidget xw, int col, int ldelta) 1307{ 1308 TScreen *screen = TScreenOf(xw); 1309 Char line[6]; 1310 unsigned count = 0; 1311 1312 col += ldelta * MaxCols(screen) - screen->cur_col; 1313 if (col == 0) 1314 return 0; 1315 if (screen->control_eight_bits) { 1316 line[count++] = ANSI_CSI; 1317 } else { 1318 line[count++] = ANSI_ESC; 1319 line[count++] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '['; 1320 } 1321 line[count] = CharOf(col > 0 ? 'C' : 'D'); 1322 if (col < 0) 1323 col = -col; 1324 while (col--) 1325 v_write(screen->respond, line, (size_t) 3); 1326 return 1; 1327} 1328 1329static int 1330ReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2) 1331{ 1332 int del; 1333 Char erases[2]; 1334 1335 erases[0] = (Char) get_tty_erase(screen->respond, XTERM_ERASE, "pty"); 1336 erases[1] = 0; 1337 1338 del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen)); 1339 if (del <= 0) /* Just in case... */ 1340 return 0; 1341 while (del--) 1342 v_write(screen->respond, erases, (size_t) 1); 1343 return 1; 1344} 1345 1346static void 1347readlineExtend(XtermWidget xw, XEvent *event) 1348{ 1349 TScreen *screen = TScreenOf(xw); 1350 int ldelta1, ldelta2; 1351 1352 if (IsBtnEvent(event)) { 1353 XButtonEvent *my_event = (XButtonEvent *) event; 1354 if (isClick1_clean(xw, my_event) 1355 && SCREEN_FLAG(screen, click1_moves) 1356 && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) { 1357 ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta1); 1358 } 1359 if (isDoubleClick3(xw, screen, my_event) 1360 && SCREEN_FLAG(screen, dclick3_deletes) 1361 && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1) 1362 && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) { 1363 ReadLineMovePoint(xw, screen->endSel.col, ldelta2); 1364 ReadLineDelete(screen, &screen->startSel, &(screen->endSel)); 1365 } 1366 } 1367} 1368#endif /* OPT_READLINE */ 1369 1370/* ^XM-G<line+' '><col+' '> */ 1371void 1372DiredButton(Widget w, 1373 XEvent *event, /* must be XButtonEvent */ 1374 String *params GCC_UNUSED, /* selections */ 1375 Cardinal *num_params GCC_UNUSED) 1376{ 1377 XtermWidget xw; 1378 1379 if ((xw = getXtermWidget(w)) != NULL) { 1380 TScreen *screen = TScreenOf(xw); 1381 1382 if (IsBtnEvent(event) 1383 && (event->xbutton.y >= screen->border) 1384 && (event->xbutton.x >= OriginX(screen))) { 1385 Char Line[6]; 1386 unsigned line, col; 1387 1388 line = (unsigned) ((event->xbutton.y - screen->border) 1389 / FontHeight(screen)); 1390 col = (unsigned) ((event->xbutton.x - OriginX(screen)) 1391 / FontWidth(screen)); 1392 Line[0] = CONTROL('X'); 1393 Line[1] = ANSI_ESC; 1394 Line[2] = 'G'; 1395 Line[3] = CharOf(' ' + col); 1396 Line[4] = CharOf(' ' + line); 1397 v_write(screen->respond, Line, (size_t) 5); 1398 } 1399 } 1400} 1401 1402#if OPT_READLINE 1403void 1404ReadLineButton(Widget w, 1405 XEvent *event, /* must be XButtonEvent */ 1406 String *params, /* selections */ 1407 Cardinal *num_params) 1408{ 1409 XtermWidget xw; 1410 1411 if ((xw = getXtermWidget(w)) != NULL) { 1412 TScreen *screen = TScreenOf(xw); 1413 Char Line[6]; 1414 int line, col, ldelta = 0; 1415 1416 if (!IsBtnEvent(event) 1417 || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection) 1418 goto finish; 1419 if (event->type == ButtonRelease) { 1420 int delta; 1421 1422 if (lastButtonDownTime == (Time) 0) { 1423 /* first time and once in a blue moon */ 1424 delta = screen->multiClickTime + 1; 1425 } else if (event->xbutton.time > lastButtonDownTime) { 1426 /* most of the time */ 1427 delta = (int) (event->xbutton.time - lastButtonDownTime); 1428 } else { 1429 /* time has rolled over since lastButtonUpTime */ 1430 delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time); 1431 } 1432 if (delta > screen->multiClickTime) 1433 goto finish; /* All this work for this... */ 1434 } 1435 line = (event->xbutton.y - screen->border) / FontHeight(screen); 1436 if (!rowOnCurrentLine(screen, line, &ldelta)) 1437 goto finish; 1438 /* Correct by half a width - we are acting on a boundary, not on a cell. */ 1439 col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) 1440 / 2) 1441 / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen); 1442 if (col == 0) 1443 goto finish; 1444 Line[0] = ANSI_ESC; 1445 Line[1] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '['; 1446 Line[2] = CharOf(col > 0 ? 'C' : 'D'); 1447 if (col < 0) 1448 col = -col; 1449 while (col--) 1450 v_write(screen->respond, Line, (size_t) 3); 1451 finish: 1452 if (event->type == ButtonRelease) 1453 do_select_end(xw, event, params, num_params, False); 1454 } 1455} 1456#endif /* OPT_READLINE */ 1457 1458/* repeats <ESC>n or <ESC>p */ 1459void 1460ViButton(Widget w, 1461 XEvent *event, /* must be XButtonEvent */ 1462 String *params GCC_UNUSED, /* selections */ 1463 Cardinal *num_params GCC_UNUSED) 1464{ 1465 XtermWidget xw; 1466 1467 if ((xw = getXtermWidget(w)) != NULL) { 1468 TScreen *screen = TScreenOf(xw); 1469 int pty = screen->respond; 1470 1471 if (IsBtnEvent(event)) { 1472 int line; 1473 1474 line = screen->cur_row - 1475 ((event->xbutton.y - screen->border) / FontHeight(screen)); 1476 1477 if (line != 0) { 1478 Char Line[6]; 1479 1480 Line[0] = ANSI_ESC; /* force an exit from insert-mode */ 1481 v_write(pty, Line, (size_t) 1); 1482 1483 if (line < 0) { 1484 line = -line; 1485 Line[0] = CONTROL('n'); 1486 } else { 1487 Line[0] = CONTROL('p'); 1488 } 1489 while (--line >= 0) 1490 v_write(pty, Line, (size_t) 1); 1491 } 1492 } 1493 } 1494} 1495 1496/* 1497 * This function handles button-motion events 1498 */ 1499/*ARGSUSED*/ 1500void 1501HandleSelectExtend(Widget w, 1502 XEvent *event, /* must be XMotionEvent */ 1503 String *params GCC_UNUSED, 1504 Cardinal *num_params GCC_UNUSED) 1505{ 1506 XtermWidget xw; 1507 1508 if ((xw = getXtermWidget(w)) != NULL) { 1509 TScreen *screen = TScreenOf(xw); 1510 CELL cell; 1511 1512 TRACE_EVENT("HandleSelectExtend", event, params, num_params); 1513 1514 screen->selection_time = event->xmotion.time; 1515 switch (screen->eventMode) { 1516 /* If not in one of the DEC mouse-reporting modes */ 1517 case LEFTEXTENSION: 1518 case RIGHTEXTENSION: 1519 PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell); 1520 ExtendExtend(xw, &cell); 1521 break; 1522 1523 /* If in motion reporting mode, send mouse position to 1524 character process as a key sequence \E[M... */ 1525 case NORMAL: 1526 /* will get here if send_mouse_pos != MOUSE_OFF */ 1527 if (okSendMousePos(xw) == BTN_EVENT_MOUSE 1528 || okSendMousePos(xw) == ANY_EVENT_MOUSE) { 1529 (void) SendMousePosition(xw, event); 1530 } 1531 break; 1532 } 1533 } 1534} 1535 1536void 1537HandleKeyboardSelectExtend(Widget w, 1538 XEvent *event GCC_UNUSED, /* must be XButtonEvent */ 1539 String *params GCC_UNUSED, 1540 Cardinal *num_params GCC_UNUSED) 1541{ 1542 XtermWidget xw; 1543 1544 if ((xw = getXtermWidget(w)) != NULL) { 1545 TScreen *screen = TScreenOf(xw); 1546 1547 TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params); 1548 ExtendExtend(xw, &screen->cursorp); 1549 } 1550} 1551 1552static void 1553do_select_end(XtermWidget xw, 1554 XEvent *event, /* must be XButtonEvent */ 1555 String *params, /* selections */ 1556 Cardinal *num_params, 1557 Bool use_cursor_loc) 1558{ 1559 TScreen *screen = TScreenOf(xw); 1560 1561 screen->selection_time = event->xbutton.time; 1562 1563 TRACE(("do_select_end %s @%ld\n", 1564 visibleEventMode(screen->eventMode), 1565 screen->selection_time)); 1566 1567 switch (screen->eventMode) { 1568 case NORMAL: 1569 (void) SendMousePosition(xw, event); 1570 break; 1571 case LEFTEXTENSION: 1572 case RIGHTEXTENSION: 1573 EndExtend(xw, event, params, *num_params, use_cursor_loc); 1574#if OPT_READLINE 1575 readlineExtend(xw, event); 1576#endif /* OPT_READLINE */ 1577 break; 1578 } 1579} 1580 1581void 1582HandleSelectEnd(Widget w, 1583 XEvent *event, /* must be XButtonEvent */ 1584 String *params, /* selections */ 1585 Cardinal *num_params) 1586{ 1587 XtermWidget xw; 1588 1589 if ((xw = getXtermWidget(w)) != NULL) { 1590 TRACE(("HandleSelectEnd\n")); 1591 do_select_end(xw, event, params, num_params, False); 1592 } 1593} 1594 1595void 1596HandleKeyboardSelectEnd(Widget w, 1597 XEvent *event, /* must be XButtonEvent */ 1598 String *params, /* selections */ 1599 Cardinal *num_params) 1600{ 1601 XtermWidget xw; 1602 1603 if ((xw = getXtermWidget(w)) != NULL) { 1604 TRACE(("HandleKeyboardSelectEnd\n")); 1605 do_select_end(xw, event, params, num_params, True); 1606 } 1607} 1608 1609void 1610HandlePointerMotion(Widget w, 1611 XEvent *event, 1612 String *params, /* selections */ 1613 Cardinal *num_params) 1614{ 1615 XtermWidget xw; 1616 1617 (void) params; 1618 (void) num_params; 1619 if ((xw = getXtermWidget(w)) != NULL) { 1620 TRACE(("HandlePointerMotion\n")); 1621 if (event->type == MotionNotify) 1622 (void) SendMousePosition(xw, event); 1623 } 1624} 1625 1626void 1627HandlePointerButton(Widget w, 1628 XEvent *event, 1629 String *params, /* selections */ 1630 Cardinal *num_params) 1631{ 1632 XtermWidget xw; 1633 1634 (void) params; 1635 (void) num_params; 1636 if ((xw = getXtermWidget(w)) != NULL) { 1637 TRACE(("HandlePointerButton\n")); 1638 if (IsBtnEvent(event)) 1639 (void) SendMousePosition(xw, event); 1640 } 1641} 1642 1643/* 1644 * Copy the selection data to the given target(s). 1645 */ 1646void 1647HandleCopySelection(Widget w, 1648 XEvent *event, 1649 String *params, /* list of targets */ 1650 Cardinal *num_params) 1651{ 1652 XtermWidget xw; 1653 1654 if ((xw = getXtermWidget(w)) != NULL) { 1655 TRACE_EVENT("HandleCopySelection", event, params, num_params); 1656 SelectSet(xw, event, params, *num_params); 1657 } 1658} 1659 1660struct _SelectionList { 1661 String *params; 1662 Cardinal count; 1663 Atom *targets; 1664 Time time; 1665}; 1666 1667static unsigned 1668DECtoASCII(unsigned ch) 1669{ 1670 if (xtermIsDecGraphic(ch)) { 1671 ch = CharOf("###########+++++##-##++++|######"[ch]); 1672 /* 01234567890123456789012345678901 */ 1673 } else { 1674 ch = '?'; /* DEC Technical has no mapping */ 1675 } 1676 return ch; 1677} 1678 1679#if OPT_WIDE_CHARS 1680static Cardinal 1681addXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value) 1682{ 1683 if (offset + 1 >= *used) { 1684 *used = 1 + (2 * (offset + 1)); 1685 allocXtermChars(buffer, *used); 1686 } 1687 (*buffer)[offset++] = (Char) value; 1688 return offset; 1689} 1690#define AddChar(buffer, used, offset, value) \ 1691 offset = addXtermChar(buffer, used, offset, (unsigned) value) 1692 1693/* 1694 * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#', 1695 * or ASCII/Latin-1 equivalents for special cases. 1696 */ 1697static Char * 1698UTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result) 1699{ 1700 static Char *buffer; 1701 static Cardinal used; 1702 1703 Cardinal offset = 0; 1704 1705 if (len != 0) { 1706 PtyData data; 1707 Boolean save_vt100 = screen->vt100_graphics; 1708 1709 fakePtyData(&data, s, s + len); 1710 screen->vt100_graphics = False; /* temporary override */ 1711 while (decodeUtf8(screen, &data)) { 1712 Bool fails = False; 1713 Bool extra = False; 1714 IChar value; 1715 skipPtyData(&data, value); 1716 if (is_UCS_SPECIAL(value)) { 1717 fails = True; 1718 } else if (value < 256) { 1719 AddChar(&buffer, &used, offset, CharOf(value)); 1720 } else { 1721 unsigned eqv = ucs2dec(screen, value); 1722 if (xtermIsInternalCs(eqv)) { 1723 AddChar(&buffer, &used, offset, DECtoASCII(eqv)); 1724 } else { 1725 eqv = AsciiEquivs(value); 1726 if (eqv == value) { 1727 fails = True; 1728 } else { 1729 AddChar(&buffer, &used, offset, eqv); 1730 } 1731 if (isWide((wchar_t) value)) 1732 extra = True; 1733 } 1734 } 1735 1736 /* 1737 * If we're not able to plug in a single-byte result, insert the 1738 * defaultString (which normally is a single "#", but could be 1739 * whatever the user wants). 1740 */ 1741 if (fails) { 1742 const Char *p; 1743 1744 for (p = (const Char *) screen->default_string; *p != '\0'; ++p) { 1745 AddChar(&buffer, &used, offset, *p); 1746 } 1747 } 1748 if (extra) 1749 AddChar(&buffer, &used, offset, ' '); 1750 } 1751 AddChar(&buffer, &used, offset, '\0'); 1752 screen->vt100_graphics = save_vt100; 1753 *result = (unsigned long) (offset - 1); 1754 } else { 1755 *result = 0; 1756 } 1757 return buffer; 1758} 1759 1760int 1761xtermUtf8ToTextList(XtermWidget xw, 1762 XTextProperty * text_prop, 1763 char ***text_list, 1764 int *text_list_count) 1765{ 1766 TScreen *screen = TScreenOf(xw); 1767 Display *dpy = screen->display; 1768 int rc = -1; 1769 1770 if (text_prop->format == 8 1771 && (rc = Xutf8TextPropertyToTextList(dpy, text_prop, 1772 text_list, 1773 text_list_count)) >= 0) { 1774 if (*text_list != NULL && *text_list_count != 0) { 1775 int i; 1776 Char *data; 1777 char **new_text_list, *tmp; 1778 unsigned long size, new_size; 1779 1780 TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count)); 1781 1782 /* 1783 * XLib StringList actually uses only two pointers, one for the 1784 * list itself, and one for the data. Pointer to the data is the 1785 * first element of the list, the rest (if any) list elements point 1786 * to the same memory block as the first element 1787 */ 1788 new_size = 0; 1789 for (i = 0; i < *text_list_count; ++i) { 1790 data = (Char *) (*text_list)[i]; 1791 size = strlen((*text_list)[i]) + 1; 1792 (void) UTF8toLatin1(screen, data, size, &size); 1793 new_size += size + 1; 1794 } 1795 new_text_list = TypeXtMallocN(char *, *text_list_count); 1796 new_text_list[0] = tmp = XtMalloc((Cardinal) new_size); 1797 for (i = 0; i < (*text_list_count); ++i) { 1798 data = (Char *) (*text_list)[i]; 1799 size = strlen((*text_list)[i]) + 1; 1800 if ((data = UTF8toLatin1(screen, data, size, &size)) != NULL) { 1801 memcpy(tmp, data, size + 1); 1802 new_text_list[i] = tmp; 1803 tmp += size + 1; 1804 } 1805 } 1806 XFreeStringList((*text_list)); 1807 *text_list = new_text_list; 1808 } else { 1809 rc = -1; 1810 } 1811 } 1812 return rc; 1813} 1814#endif /* OPT_WIDE_CHARS */ 1815 1816static char * 1817parseItem(char *value, char *nextc) 1818{ 1819 char *nextp = value; 1820 while (*nextp != '\0' && *nextp != ',') { 1821 *nextp = x_toupper(*nextp); 1822 ++nextp; 1823 } 1824 *nextc = *nextp; 1825 *nextp = '\0'; 1826 1827 return nextp; 1828} 1829 1830/* 1831 * All of the wanted strings are unique in the first character, so we can 1832 * use simple abbreviations. 1833 */ 1834static Bool 1835sameItem(const char *actual, const char *wanted) 1836{ 1837 Bool result = False; 1838 size_t have = strlen(actual); 1839 size_t need = strlen(wanted); 1840 1841 if (have != 0 && have <= need) { 1842 if (!strncmp(actual, wanted, have)) { 1843 TRACE(("...matched \"%s\"\n", wanted)); 1844 result = True; 1845 } 1846 } 1847 1848 return result; 1849} 1850 1851/* 1852 * Handle the eightBitSelectTypes or utf8SelectTypes resource values. 1853 */ 1854static Bool 1855overrideTargets(Widget w, String value, Atom **resultp) 1856{ 1857 Bool override = False; 1858 XtermWidget xw; 1859 1860 if ((xw = getXtermWidget(w)) != NULL) { 1861 TScreen *screen = TScreenOf(xw); 1862 1863 if (!IsEmpty(value)) { 1864 char *copied = x_strdup(value); 1865 if (copied != NULL) { 1866 Atom *result = NULL; 1867 Cardinal count = 1; 1868 int n; 1869 1870 TRACE(("decoding SelectTypes \"%s\"\n", value)); 1871 for (n = 0; copied[n] != '\0'; ++n) { 1872 if (copied[n] == ',') 1873 ++count; 1874 } 1875 result = TypeXtMallocN(Atom, (2 * count) + 1); 1876 if (result == NULL) { 1877 TRACE(("Couldn't allocate selection types\n")); 1878 } else { 1879 char nextc = '?'; 1880 char *listp = (char *) copied; 1881 count = 0; 1882 do { 1883 char *nextp = parseItem(listp, &nextc); 1884 char *item = x_strtrim(listp); 1885 size_t len = (item ? strlen(item) : 0); 1886 1887 if (len == 0) { 1888 /* EMPTY */ ; 1889 } 1890#if OPT_WIDE_CHARS 1891 else if (sameItem(item, "UTF8")) { 1892 result[count++] = XA_UTF8_STRING(XtDisplay(w)); 1893 } 1894#endif 1895 else if (sameItem(item, "I18N")) { 1896 if (screen->i18nSelections) { 1897 result[count++] = XA_TEXT(XtDisplay(w)); 1898 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1899 } 1900 } else if (sameItem(item, "TEXT")) { 1901 result[count++] = XA_TEXT(XtDisplay(w)); 1902 } else if (sameItem(item, "COMPOUND_TEXT")) { 1903 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1904 } else if (sameItem(item, "STRING")) { 1905 result[count++] = XA_STRING; 1906 } 1907 *nextp++ = nextc; 1908 listp = nextp; 1909 free(item); 1910 } while (nextc != '\0'); 1911 if (count) { 1912 result[count] = None; 1913 override = True; 1914 *resultp = result; 1915 } else { 1916 XtFree((char *) result); 1917 } 1918 } 1919 free(copied); 1920 } else { 1921 TRACE(("Couldn't allocate copy of selection types\n")); 1922 } 1923 } 1924 } 1925 return override; 1926} 1927 1928#if OPT_WIDE_CHARS 1929static Atom * 1930allocUtf8Targets(Widget w, TScreen *screen) 1931{ 1932 Atom **resultp = &(screen->selection_targets_utf8); 1933 1934 if (*resultp == NULL) { 1935 Atom *result; 1936 1937 if (!overrideTargets(w, screen->utf8_select_types, &result)) { 1938 result = TypeXtMallocN(Atom, 5); 1939 if (result == NULL) { 1940 TRACE(("Couldn't allocate utf-8 selection targets\n")); 1941 } else { 1942 int n = 0; 1943 1944 if (XSupportsLocale()) { 1945 result[n++] = XA_UTF8_STRING(XtDisplay(w)); 1946#ifdef X_HAVE_UTF8_STRING 1947 if (screen->i18nSelections) { 1948 result[n++] = XA_TEXT(XtDisplay(w)); 1949 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1950 } 1951#endif 1952 } 1953 result[n++] = XA_STRING; 1954 result[n] = None; 1955 } 1956 } 1957 1958 *resultp = result; 1959 } 1960 1961 return *resultp; 1962} 1963#endif 1964 1965static Atom * 1966alloc8bitTargets(Widget w, TScreen *screen) 1967{ 1968 Atom **resultp = &(screen->selection_targets_8bit); 1969 1970 if (*resultp == NULL) { 1971 Atom *result = NULL; 1972 1973 if (!overrideTargets(w, screen->eightbit_select_types, &result)) { 1974 result = TypeXtMallocN(Atom, 5); 1975 if (result == NULL) { 1976 TRACE(("Couldn't allocate 8bit selection targets\n")); 1977 } else { 1978 int n = 0; 1979 1980 if (XSupportsLocale()) { 1981#ifdef X_HAVE_UTF8_STRING 1982 result[n++] = XA_UTF8_STRING(XtDisplay(w)); 1983#endif 1984 if (screen->i18nSelections) { 1985 result[n++] = XA_TEXT(XtDisplay(w)); 1986 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1987 } 1988 } 1989 result[n++] = XA_STRING; 1990 result[n] = None; 1991 } 1992 } 1993 1994 *resultp = result; 1995 } 1996 1997 return *resultp; 1998} 1999 2000static Atom * 2001_SelectionTargets(Widget w) 2002{ 2003 Atom *result; 2004 XtermWidget xw; 2005 2006 if ((xw = getXtermWidget(w)) == NULL) { 2007 result = NULL; 2008 } else { 2009 TScreen *screen = TScreenOf(xw); 2010 2011#if OPT_WIDE_CHARS 2012 if (screen->wide_chars) { 2013 result = allocUtf8Targets(w, screen); 2014 } else 2015#endif 2016 { 2017 /* not screen->wide_chars */ 2018 result = alloc8bitTargets(w, screen); 2019 } 2020 } 2021 2022 return result; 2023} 2024 2025#define isSELECT(value) (!strcmp(NonNull(value), "SELECT")) 2026 2027static int 2028DefaultSelection(TScreen *screen) 2029{ 2030 return (screen->selectToClipboard ? 1 : 0); 2031} 2032 2033static int 2034TargetToSelection(TScreen *screen, String name) 2035{ 2036 int result = -1; 2037 int cutb; 2038 2039 if (isSELECT(name)) { 2040 result = DefaultSelection(screen); 2041 } else if (!strcmp(name, PRIMARY_NAME)) { 2042 result = PRIMARY_CODE; 2043 } else if (!strcmp(name, CLIPBOARD_NAME)) { 2044 result = CLIPBOARD_CODE; 2045 } else if (!strcmp(name, SECONDARY_NAME)) { 2046 result = SECONDARY_CODE; 2047 } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) { 2048 if (cutb >= 0 && cutb < MAX_CUT_BUFFER) { 2049 result = CutBufferToCode(cutb); 2050 } else { 2051 xtermWarning("unexpected cut-buffer code: %d\n", cutb); 2052 } 2053 } else { 2054 xtermWarning("unexpected selection target: %s\n", name); 2055 } 2056 TRACE2(("TargetToSelection(%s) ->%d\n", name, result)); 2057 return result; 2058} 2059 2060void 2061UnmapSelections(XtermWidget xw) 2062{ 2063 TScreen *screen = TScreenOf(xw); 2064 2065 FreeAndNull(screen->mappedSelect); 2066} 2067 2068/* 2069 * xterm generally uses the primary selection. Some applications prefer 2070 * (or are limited to) the clipboard. Since the translations resource is 2071 * complicated, users seldom change the way it affects selection. But it 2072 * is simple to remap the choice between primary and clipboard before the 2073 * call to XmuInternStrings(). 2074 */ 2075static String * 2076MapSelections(XtermWidget xw, String *params, Cardinal num_params) 2077{ 2078 String *result = params; 2079 2080 if (params != NULL && num_params > 0) { 2081 Cardinal j; 2082 Boolean map = False; 2083 2084 for (j = 0; j < num_params; ++j) { 2085 TRACE(("param[%d]:%s\n", j, params[j])); 2086 if (isSELECT(params[j])) { 2087 map = True; 2088 break; 2089 } 2090 } 2091 if (map) { 2092 TScreen *screen = TScreenOf(xw); 2093 const char *mapTo = (screen->selectToClipboard 2094 ? CLIPBOARD_NAME 2095 : PRIMARY_NAME); 2096 2097 UnmapSelections(xw); 2098 if ((result = TypeMallocN(String, num_params + 1)) != NULL) { 2099 result[num_params] = NULL; 2100 for (j = 0; j < num_params; ++j) { 2101 result[j] = (String) (isSELECT(params[j]) 2102 ? mapTo 2103 : params[j]); 2104 if (result[j] == NULL) { 2105 UnmapSelections(xw); 2106 FreeAndNull(result); 2107 break; 2108 } 2109 } 2110 screen->mappedSelect = result; 2111 } 2112 } 2113 } 2114 return result; 2115} 2116 2117/* 2118 * Lookup the cut-buffer number, which will be in the range 0-7. 2119 * If it is not a cut-buffer, it is a type of selection, e.g., primary. 2120 */ 2121static int 2122CutBuffer(Atom code) 2123{ 2124 int cutbuffer; 2125 switch ((unsigned) code) { 2126 case XA_CUT_BUFFER0: 2127 cutbuffer = 0; 2128 break; 2129 case XA_CUT_BUFFER1: 2130 cutbuffer = 1; 2131 break; 2132 case XA_CUT_BUFFER2: 2133 cutbuffer = 2; 2134 break; 2135 case XA_CUT_BUFFER3: 2136 cutbuffer = 3; 2137 break; 2138 case XA_CUT_BUFFER4: 2139 cutbuffer = 4; 2140 break; 2141 case XA_CUT_BUFFER5: 2142 cutbuffer = 5; 2143 break; 2144 case XA_CUT_BUFFER6: 2145 cutbuffer = 6; 2146 break; 2147 case XA_CUT_BUFFER7: 2148 cutbuffer = 7; 2149 break; 2150 default: 2151 cutbuffer = -1; 2152 break; 2153 } 2154 TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer)); 2155 return cutbuffer; 2156} 2157 2158#if OPT_PASTE64 2159static void 2160FinishPaste64(XtermWidget xw) 2161{ 2162 TScreen *screen = TScreenOf(xw); 2163 2164 TRACE(("FinishPaste64(%d)\n", screen->base64_paste)); 2165 if (screen->base64_paste) { 2166 screen->base64_paste = 0; 2167 unparseputc1(xw, screen->base64_final); 2168 unparse_end(xw); 2169 } 2170} 2171#endif 2172 2173#if !OPT_PASTE64 2174static 2175#endif 2176void 2177xtermGetSelection(Widget w, 2178 Time ev_time, 2179 String *params, /* selections in precedence order */ 2180 Cardinal num_params, 2181 Atom *targets) 2182{ 2183 Atom selection; 2184 int cutbuffer; 2185 Atom target; 2186 2187 XtermWidget xw; 2188 2189 if (num_params == 0) 2190 return; 2191 if ((xw = getXtermWidget(w)) == NULL) 2192 return; 2193 2194 TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time)); 2195 params = MapSelections(xw, params, num_params); 2196 2197 XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection); 2198 cutbuffer = CutBuffer(selection); 2199 2200 TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer, 2201 (targets 2202 ? visibleSelectionTarget(XtDisplay(w), targets[0]) 2203 : "None"))); 2204 2205 if (cutbuffer >= 0) { 2206 int inbytes; 2207 unsigned long nbytes; 2208 int fmt8 = 8; 2209 Atom type = XA_STRING; 2210 char *line; 2211 2212 /* 'line' is freed in SelectionReceived */ 2213 line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer); 2214 nbytes = (unsigned long) inbytes; 2215 2216 if (nbytes > 0) { 2217 SelectionReceived(w, NULL, &selection, &type, (XtPointer) line, 2218 &nbytes, &fmt8); 2219 } else if (num_params > 1) { 2220 xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL); 2221 } 2222#if OPT_PASTE64 2223 else { 2224 FinishPaste64(xw); 2225 } 2226#endif 2227 } else { 2228 2229 if (targets == NULL || targets[0] == None) { 2230 targets = _SelectionTargets(w); 2231 } 2232 2233 if (targets != NULL) { 2234 struct _SelectionList *list; 2235 2236 target = targets[0]; 2237 2238 if (targets[1] == None) { /* last target in list */ 2239 params++; 2240 num_params--; 2241 targets = _SelectionTargets(w); 2242 } else { 2243 targets = &(targets[1]); 2244 } 2245 2246 if (num_params) { 2247 /* 'list' is freed in SelectionReceived */ 2248 list = TypeXtMalloc(struct _SelectionList); 2249 if (list != NULL) { 2250 list->params = params; 2251 list->count = num_params; 2252 list->targets = targets; 2253 list->time = ev_time; 2254 } 2255 } else { 2256 list = NULL; 2257 } 2258 2259 XtGetSelectionValue(w, selection, 2260 target, 2261 SelectionReceived, 2262 (XtPointer) list, ev_time); 2263 } 2264 } 2265} 2266 2267#if OPT_TRACE && OPT_WIDE_CHARS 2268static void 2269GettingSelection(Display *dpy, Atom type, Char *line, unsigned long len) 2270{ 2271 Char *cp; 2272 const char *name = TraceAtomName(dpy, type); 2273 2274 TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len)); 2275 for (cp = line; cp < line + len; cp++) { 2276 TRACE(("[%d:%lu]", (int) (cp + 1 - line), len)); 2277 if (isprint(*cp)) { 2278 TRACE(("%c\n", *cp)); 2279 } else { 2280 TRACE(("\\x%02x\n", *cp)); 2281 } 2282 } 2283} 2284#else 2285#define GettingSelection(dpy,type,line,len) /* nothing */ 2286#endif 2287 2288#define tty_vwrite(pty,lag,l) v_write(pty,lag,(size_t) l) 2289 2290#if OPT_PASTE64 2291/* Return base64 code character given 6-bit number */ 2292static const char base64_code[] = "\ 2293ABCDEFGHIJKLMNOPQRSTUVWXYZ\ 2294abcdefghijklmnopqrstuvwxyz\ 22950123456789+/"; 2296static void 2297base64_flush(TScreen *screen) 2298{ 2299 Char x; 2300 2301 TRACE(("base64_flush count %d, pad %d (%d)\n", 2302 screen->base64_count, 2303 screen->base64_pad, 2304 screen->base64_pad & 3)); 2305 2306 switch (screen->base64_count) { 2307 case 0: 2308 break; 2309 case 2: 2310 x = CharOf(base64_code[screen->base64_accu << 4]); 2311 tty_vwrite(screen->respond, &x, 1); 2312 break; 2313 case 4: 2314 x = CharOf(base64_code[screen->base64_accu << 2]); 2315 tty_vwrite(screen->respond, &x, 1); 2316 break; 2317 } 2318 if (screen->base64_pad & 3) { 2319 tty_vwrite(screen->respond, 2320 (const Char *) "===", 2321 (unsigned) (3 - (screen->base64_pad & 3))); 2322 } 2323 screen->base64_count = 0; 2324 screen->base64_accu = 0; 2325 screen->base64_pad = 0; 2326} 2327#endif /* OPT_PASTE64 */ 2328 2329/* 2330 * Translate ISO-8859-1 or UTF-8 data to NRCS. 2331 */ 2332static void 2333ToNational(XtermWidget xw, Char *buffer, size_t *length) 2334{ 2335 TScreen *screen = TScreenOf(xw); 2336 DECNRCM_codes gsetL = screen->gsets[screen->curgl]; 2337 DECNRCM_codes gsetR = screen->gsets[screen->curgr]; 2338 2339#if OPT_WIDE_CHARS 2340 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) { 2341 Char *p; 2342 PtyData *data = TypeXtMallocX(PtyData, *length); 2343 2344 memset(data, 0, sizeof(*data)); 2345 data->next = data->buffer; 2346 data->last = data->buffer + *length; 2347 memcpy(data->buffer, buffer, *length); 2348 p = buffer; 2349 while (data->next < data->last) { 2350 unsigned chr, out, gl, gr; 2351 2352 if (!decodeUtf8(screen, data)) { 2353 data->utf_size = 1; 2354 data->utf_data = data->next[0]; 2355 } 2356 data->next += data->utf_size; 2357 chr = data->utf_data; 2358 out = chr; 2359 if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) { 2360 out = gl; 2361 } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) { 2362 out = gr; 2363 } 2364 *p++ = (Char) ((out < 256) ? out : ' '); 2365 } 2366 *length = (size_t) (p - buffer); 2367 free(data); 2368 } else 2369#endif 2370 { 2371 Char *p; 2372 2373 for (p = buffer; (size_t) (p - buffer) < *length; ++p) { 2374 unsigned gl, gr; 2375 unsigned chr = *p; 2376 unsigned out = chr; 2377 if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) { 2378 out = gl; 2379 } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) { 2380 out = gr; 2381 } 2382 *p = (Char) out; 2383 } 2384 } 2385} 2386 2387static void 2388_qWriteSelectionData(XtermWidget xw, Char *lag, size_t length) 2389{ 2390 TScreen *screen = TScreenOf(xw); 2391 2392 /* 2393 * If we are pasting into a window which is using NRCS, we want to map 2394 * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding 2395 * that an application would use to write characters with NRCS. 2396 * 2397 * TODO: handle conversion from UTF-8, and adjust length. This can be done 2398 * in the same buffer because the target is always 8-bit. 2399 */ 2400 if ((xw->flags & NATIONAL) && (length != 0)) { 2401 ToNational(xw, lag, &length); 2402 } 2403#if OPT_PASTE64 2404 if (screen->base64_paste) { 2405 /* Send data as base64 */ 2406 Char *p = lag; 2407 Char buf[64]; 2408 unsigned x = 0; 2409 2410 TRACE(("convert to base64 %lu:%s\n", 2411 (unsigned long) length, 2412 visibleChars(p, length))); 2413 2414 /* 2415 * Handle the case where the selection is from _this_ xterm, which 2416 * puts part of the reply in the buffer before the selection callback 2417 * happens. 2418 */ 2419 if (screen->base64_paste && screen->unparse_len) { 2420 unparse_end(xw); 2421 } 2422 while (length--) { 2423 switch (screen->base64_count) { 2424 case 0: 2425 buf[x++] = CharOf(base64_code[*p >> 2]); 2426 screen->base64_accu = (unsigned) (*p & 0x3); 2427 screen->base64_count = 2; 2428 ++p; 2429 break; 2430 case 2: 2431 buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) + 2432 (*p >> 4)]); 2433 screen->base64_accu = (unsigned) (*p & 0xF); 2434 screen->base64_count = 4; 2435 ++p; 2436 break; 2437 case 4: 2438 buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) + 2439 (*p >> 6)]); 2440 buf[x++] = CharOf(base64_code[*p & 0x3F]); 2441 screen->base64_accu = 0; 2442 screen->base64_count = 0; 2443 ++p; 2444 break; 2445 } 2446 if (x >= 63) { 2447 /* Write 63 or 64 characters */ 2448 screen->base64_pad += x; 2449 TRACE(("writing base64 interim %s\n", visibleChars(buf, x))); 2450 tty_vwrite(screen->respond, buf, x); 2451 x = 0; 2452 } 2453 } 2454 if (x != 0) { 2455 screen->base64_pad += x; 2456 TRACE(("writing base64 finish %s\n", visibleChars(buf, x))); 2457 tty_vwrite(screen->respond, buf, x); 2458 } 2459 } else 2460#endif /* OPT_PASTE64 */ 2461#if OPT_READLINE 2462 if (SCREEN_FLAG(screen, paste_quotes)) { 2463 Char quote[2]; 2464 quote[0] = (Char) get_tty_lnext(screen->respond, XTERM_LNEXT, "pty"); 2465 quote[1] = 0; 2466 TRACE(("writing quoted selection data %s\n", visibleChars(lag, length))); 2467 while (length--) { 2468 tty_vwrite(screen->respond, quote, 1); 2469 tty_vwrite(screen->respond, lag++, 1); 2470 } 2471 } else 2472#endif 2473 { 2474 TRACE(("writing selection data %s\n", visibleChars(lag, length))); 2475 tty_vwrite(screen->respond, lag, length); 2476 } 2477} 2478 2479static void 2480_WriteSelectionData(XtermWidget xw, Char *line, size_t length) 2481{ 2482#if OPT_PASTE64 || OPT_READLINE 2483 TScreen *screen = TScreenOf(xw); 2484#endif 2485 2486#if OPT_PASTE64 2487 if (screen->base64_paste) { 2488 _qWriteSelectionData(xw, line, length); 2489 base64_flush(screen); 2490 } else 2491#endif 2492 { 2493 if (!SCREEN_FLAG(screen, paste_literal_nl)) { 2494 size_t n; 2495 for (n = 0; n < length; ++n) { 2496 if (line[n] == '\n') { 2497 line[n] = '\r'; 2498 } 2499 } 2500 } 2501 2502 _qWriteSelectionData(xw, line, length); 2503 } 2504} 2505 2506#if OPT_PASTE64 || OPT_READLINE 2507static void 2508_WriteKey(TScreen *screen, const Char *in) 2509{ 2510 Char line[16]; 2511 unsigned count = 0; 2512 size_t length = strlen((const char *) in); 2513 2514 if (screen->control_eight_bits) { 2515 line[count++] = ANSI_CSI; 2516 } else { 2517 line[count++] = ANSI_ESC; 2518 line[count++] = '['; 2519 } 2520 while (length--) 2521 line[count++] = *in++; 2522 line[count++] = '~'; 2523 tty_vwrite(screen->respond, line, count); 2524} 2525#endif /* OPT_READLINE */ 2526 2527/* 2528 * Unless enabled by the user, strip control characters other than formatting. 2529 */ 2530static size_t 2531removeControls(XtermWidget xw, char *value) 2532{ 2533 TScreen *screen = TScreenOf(xw); 2534 size_t dst = 0; 2535 2536 if (screen->allowPasteControls) { 2537 dst = strlen(value); 2538 } else { 2539 size_t src = 0; 2540 Boolean *disallowed = screen->disallow_paste_ops; 2541 TERMIO_STRUCT data; 2542 char current_chars[epLAST]; 2543 2544 if (disallowed[epSTTY] && ttyGetAttr(screen->respond, &data) == 0) { 2545 int n; 2546 int disabled = xtermDisabledChar(); 2547 2548 TRACE(("disallow(STTY):")); 2549 memcpy(current_chars, disallowed, sizeof(current_chars)); 2550 2551 for (n = 0; n < NCCS; ++n) { 2552 PasteControls nc = (data.c_cc[n] < 32 2553 ? data.c_cc[n] 2554 : (data.c_cc[n] == 127 2555 ? epDEL 2556 : epLAST)); 2557 if (nc == epNUL || nc == epLAST) 2558 continue; 2559 if (CharOf(data.c_cc[n]) == CharOf(disabled)) 2560 continue; 2561 if ((n == VMIN || n == VTIME) && !(data.c_lflag & ICANON)) 2562 continue; 2563 switch (n) { 2564 /* POSIX */ 2565 case VEOF: 2566 case VEOL: 2567 case VERASE: 2568 case VINTR: 2569 case VKILL: 2570 case VQUIT: 2571 case VSTART: 2572 case VSTOP: 2573 case VSUSP: 2574 /* system-dependent */ 2575#ifdef VDISCARD 2576 case VDISCARD: 2577#endif 2578#ifdef VDSUSP 2579 case VDSUSP: 2580#endif 2581#ifdef VEOL2 2582 case VEOL2: 2583#endif 2584#ifdef VLNEXT 2585 case VLNEXT: 2586#endif 2587#ifdef VREPRINT 2588 case VREPRINT: 2589#endif 2590#ifdef VSTATUS 2591 case VSTATUS: 2592#endif 2593#ifdef VSWTC 2594 case VSWTC: /* System V SWTCH */ 2595#endif 2596#ifdef VWERASE 2597 case VWERASE: 2598#endif 2599 break; 2600 default: 2601 continue; 2602 } 2603 if (nc != epLAST) { 2604 TRACE((" \\%03o", data.c_cc[n])); 2605 current_chars[nc] = 1; 2606 } 2607 } 2608 TRACE(("\n")); 2609 disallowed = current_chars; 2610 } 2611 while ((value[dst] = value[src]) != '\0') { 2612 int ch = CharOf(value[src++]); 2613 2614#define ReplacePaste(n) \ 2615 if (disallowed[n]) \ 2616 value[dst] = ' ' 2617 2618 if (ch < 32) { 2619 ReplacePaste(epC0); 2620 ReplacePaste(ch); 2621 ++dst; 2622 } else if (ch == ANSI_DEL) { 2623 ReplacePaste(epDEL); 2624 ++dst; 2625 } 2626#if OPT_WIDE_CHARS 2627 else if (screen->utf8_inparse || screen->utf8_nrc_mode) 2628 ++dst; 2629#endif 2630#if OPT_C1_PRINT || OPT_WIDE_CHARS 2631 else if (screen->c1_printable) 2632 ++dst; 2633#endif 2634 else if (ch >= 128 && ch < 160) 2635 continue; 2636 else 2637 ++dst; 2638 } 2639 } 2640 return dst; 2641} 2642 2643#if OPT_SELECTION_OPS 2644static void 2645beginInternalSelect(XtermWidget xw) 2646{ 2647 TScreen *screen = TScreenOf(xw); 2648 InternalSelect *mydata = &(screen->internal_select); 2649 2650 (void) mydata; 2651 /* override flags so that SelectionReceived only updates a buffer */ 2652#if OPT_PASTE64 2653 mydata->base64_paste = screen->base64_paste; 2654 screen->base64_paste = 0; 2655#endif 2656#if OPT_PASTE64 || OPT_READLINE 2657 mydata->paste_brackets = screen->paste_brackets; 2658 SCREEN_FLAG_unset(screen, paste_brackets); 2659#endif 2660} 2661 2662static void 2663finishInternalSelect(XtermWidget xw) 2664{ 2665 TScreen *screen = TScreenOf(xw); 2666 InternalSelect *mydata = &(screen->internal_select); 2667 2668 (void) mydata; 2669#if OPT_PASTE64 2670 screen->base64_paste = mydata->base64_paste; 2671#endif 2672#if OPT_PASTE64 || OPT_READLINE 2673 screen->paste_brackets = mydata->paste_brackets; 2674#endif 2675} 2676 2677#else 2678#define finishInternalSelect(xw) /* nothing */ 2679#endif /* OPT_SELECTION_OPS */ 2680 2681/* SelectionReceived: stuff received selection text into pty */ 2682 2683/* ARGSUSED */ 2684static void 2685SelectionReceived(Widget w, 2686 XtPointer client_data, 2687 Atom *selection GCC_UNUSED, 2688 Atom *type, 2689 XtPointer value, 2690 unsigned long *length, 2691 int *format) 2692{ 2693 char **text_list = NULL; 2694 int text_list_count = 0; 2695 XTextProperty text_prop; 2696 TScreen *screen; 2697 Display *dpy; 2698#if OPT_TRACE && OPT_WIDE_CHARS 2699 Char *line = (Char *) value; 2700#endif 2701 2702 XtermWidget xw; 2703 2704 if ((xw = getXtermWidget(w)) == NULL) 2705 return; 2706 2707 screen = TScreenOf(xw); 2708 dpy = XtDisplay(w); 2709 2710 if (*type == 0 /*XT_CONVERT_FAIL */ 2711 || *length == 0 2712 || value == NULL) { 2713 TRACE(("...no data to convert\n")); 2714 goto fail; 2715 } 2716 2717 text_prop.value = (unsigned char *) value; 2718 text_prop.encoding = *type; 2719 text_prop.format = *format; 2720 text_prop.nitems = *length; 2721 2722 TRACE(("SelectionReceived %s %s format %d, nitems %ld\n", 2723 TraceAtomName(screen->display, *selection), 2724 visibleSelectionTarget(dpy, text_prop.encoding), 2725 text_prop.format, 2726 text_prop.nitems)); 2727 2728#if OPT_WIDE_CHARS 2729 if (XSupportsLocale() && screen->wide_chars) { 2730 if (*type == XA_UTF8_STRING(dpy) || 2731 *type == XA_STRING || 2732 *type == XA_COMPOUND_TEXT(dpy)) { 2733 GettingSelection(dpy, *type, line, *length); 2734 if (Xutf8TextPropertyToTextList(dpy, &text_prop, 2735 &text_list, 2736 &text_list_count) < 0) { 2737 TRACE(("default Xutf8 Conversion failed\n")); 2738 text_list = NULL; 2739 } 2740 } 2741 } else 2742#endif /* OPT_WIDE_CHARS */ 2743 { 2744 /* Convert the selection to locale's multibyte encoding. */ 2745 2746 if (*type == XA_UTF8_STRING(dpy) || 2747 *type == XA_STRING || 2748 *type == XA_COMPOUND_TEXT(dpy)) { 2749 Status rc; 2750 2751 GettingSelection(dpy, *type, line, *length); 2752 2753#if OPT_WIDE_CHARS 2754 if (*type == XA_UTF8_STRING(dpy) && 2755 !(screen->wide_chars || screen->c1_printable)) { 2756 rc = xtermUtf8ToTextList(xw, &text_prop, 2757 &text_list, &text_list_count); 2758 } else 2759#endif 2760 if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) { 2761 rc = XTextPropertyToStringList(&text_prop, 2762 &text_list, &text_list_count); 2763 } else { 2764 rc = XmbTextPropertyToTextList(dpy, &text_prop, 2765 &text_list, 2766 &text_list_count); 2767 } 2768 if (rc < 0) { 2769 TRACE(("Conversion failed\n")); 2770 text_list = NULL; 2771 } 2772 } 2773 } 2774 2775 if (text_list != NULL && text_list_count != 0) { 2776 int i; 2777 2778#if OPT_PASTE64 2779 if (screen->base64_paste) { 2780 /* EMPTY */ ; 2781 } else 2782#endif 2783#if OPT_PASTE64 || OPT_READLINE 2784 if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) { 2785 _WriteKey(screen, (const Char *) "200"); 2786 } 2787#endif 2788 for (i = 0; i < text_list_count; i++) { 2789 size_t len = removeControls(xw, text_list[i]); 2790 2791 if (screen->selectToBuffer) { 2792 InternalSelect *mydata = &(screen->internal_select); 2793 if (!mydata->done) { 2794 size_t have = (mydata->buffer 2795 ? strlen(mydata->buffer) 2796 : 0); 2797 size_t need = have + len + 1; 2798 char *buffer = realloc(mydata->buffer, need); 2799 2800 if (buffer != NULL) { 2801 strcpy(buffer + have, text_list[i]); 2802 mydata->buffer = buffer; 2803 } 2804 TRACE(("FormatSelect %d.%d .. %d.%d %s\n", 2805 screen->startSel.row, 2806 screen->startSel.col, 2807 screen->endSel.row, 2808 screen->endSel.col, 2809 mydata->buffer)); 2810 mydata->format_select(w, mydata->format, mydata->buffer, 2811 &(screen->startSel), 2812 &(screen->endSel)); 2813 mydata->done = True; 2814 } 2815 2816 } else { 2817 _WriteSelectionData(xw, (Char *) text_list[i], len); 2818 } 2819 } 2820#if OPT_PASTE64 2821 if (screen->base64_paste) { 2822 FinishPaste64(xw); 2823 } else 2824#endif 2825#if OPT_PASTE64 || OPT_READLINE 2826 if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) { 2827 _WriteKey(screen, (const Char *) "201"); 2828 } 2829#endif 2830 if (screen->selectToBuffer) { 2831 InternalSelect *mydata = &(screen->internal_select); 2832 finishInternalSelect(xw); 2833 if (mydata->done) { 2834 free(mydata->format); 2835 free(mydata->buffer); 2836 memset(mydata, 0, sizeof(*mydata)); 2837 } 2838 screen->selectToBuffer = False; 2839 } 2840 XFreeStringList(text_list); 2841 } else { 2842 TRACE(("...empty text-list\n")); 2843 goto fail; 2844 } 2845 2846 XtFree((char *) client_data); 2847 XtFree((char *) value); 2848 2849 return; 2850 2851 fail: 2852 if (client_data != NULL) { 2853 struct _SelectionList *list = (struct _SelectionList *) client_data; 2854 2855 TRACE(("SelectionReceived ->xtermGetSelection\n")); 2856 xtermGetSelection(w, list->time, 2857 list->params, list->count, list->targets); 2858 XtFree((char *) client_data); 2859#if OPT_PASTE64 2860 } else { 2861 FinishPaste64(xw); 2862#endif 2863 } 2864 return; 2865} 2866 2867void 2868HandleInsertSelection(Widget w, 2869 XEvent *event, /* assumed to be XButtonEvent* */ 2870 String *params, /* selections in precedence order */ 2871 Cardinal *num_params) 2872{ 2873 XtermWidget xw; 2874 2875 if ((xw = getXtermWidget(w)) != NULL) { 2876 TRACE_EVENT("HandleInsertSelection", event, params, num_params); 2877 if (!SendMousePosition(xw, event)) { 2878#if OPT_READLINE 2879 int ldelta; 2880 TScreen *screen = TScreenOf(xw); 2881 if (IsBtnEvent(event) 2882 && !OverrideEvent(event) 2883 && (okSendMousePos(xw) == MOUSE_OFF) 2884 && SCREEN_FLAG(screen, paste_moves) 2885 && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta)) 2886 ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta); 2887#endif /* OPT_READLINE */ 2888 2889 xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL); 2890 } 2891 } 2892} 2893 2894static SelectUnit 2895EvalSelectUnit(XtermWidget xw, 2896 Time buttonDownTime, 2897 SelectUnit defaultUnit, 2898 unsigned int button) 2899{ 2900 TScreen *screen = TScreenOf(xw); 2901 SelectUnit result; 2902 int delta; 2903 2904 if (button != screen->lastButton) { 2905 delta = screen->multiClickTime + 1; 2906 } else if (screen->lastButtonUpTime == (Time) 0) { 2907 /* first time and once in a blue moon */ 2908 delta = screen->multiClickTime + 1; 2909 } else if (buttonDownTime > screen->lastButtonUpTime) { 2910 /* most of the time */ 2911 delta = (int) (buttonDownTime - screen->lastButtonUpTime); 2912 } else { 2913 /* time has rolled over since lastButtonUpTime */ 2914 delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime); 2915 } 2916 2917#if OPT_BLOCK_SELECT 2918 if (screen->blockSelecting 2919 || screen->blockSelecting != screen->lastSelectWasBlock) { 2920 /* No word, line, paragraph selecting when block selecting 2921 or when our last click was a block select */ 2922 screen->numberOfClicks = 1; 2923 result = defaultUnit; 2924 } else 2925#endif 2926 if (delta > screen->multiClickTime) { 2927 screen->numberOfClicks = 1; 2928 result = defaultUnit; 2929 } else { 2930 result = screen->selectMap[screen->numberOfClicks % screen->maxClicks]; 2931 screen->numberOfClicks += 1; 2932 } 2933 TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result)); 2934 return result; 2935} 2936 2937static void 2938do_select_start(XtermWidget xw, 2939 XEvent *event, /* must be XButtonEvent* */ 2940 CELL *cell) 2941{ 2942 TScreen *screen = TScreenOf(xw); 2943 2944 if (SendMousePosition(xw, event)) 2945 return; 2946 screen->selectUnit = EvalSelectUnit(xw, 2947 event->xbutton.time, 2948 Select_CHAR, 2949 event->xbutton.button); 2950 screen->replyToEmacs = False; 2951 2952#if OPT_READLINE 2953 lastButtonDownTime = event->xbutton.time; 2954#endif 2955 2956 StartSelect(xw, cell); 2957} 2958 2959/* ARGSUSED */ 2960void 2961HandleSelectStart(Widget w, 2962 XEvent *event, /* must be XButtonEvent* */ 2963 String *params GCC_UNUSED, 2964 Cardinal *num_params GCC_UNUSED) 2965{ 2966 XtermWidget xw; 2967 2968 if ((xw = getXtermWidget(w)) != NULL) { 2969 TScreen *screen = TScreenOf(xw); 2970 CELL cell; 2971 2972 TRACE_EVENT("HandleSelectStart", event, params, num_params); 2973 screen->firstValidRow = 0; 2974 screen->lastValidRow = screen->max_row; 2975 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell); 2976 2977#if OPT_READLINE 2978 ExtendingSelection = 0; 2979#endif 2980 2981#if OPT_BLOCK_SELECT 2982 screen->blockSelecting = 2983 (*num_params >= 1 && !strcmp(params[0], "block")) ? 1 : 0; 2984#endif 2985 2986 do_select_start(xw, event, &cell); 2987 } 2988} 2989 2990/* ARGSUSED */ 2991void 2992HandleKeyboardSelectStart(Widget w, 2993 XEvent *event, /* must be XButtonEvent* */ 2994 String *params GCC_UNUSED, 2995 Cardinal *num_params GCC_UNUSED) 2996{ 2997 XtermWidget xw; 2998 2999 if ((xw = getXtermWidget(w)) != NULL) { 3000 TScreen *screen = TScreenOf(xw); 3001 3002 TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params); 3003 do_select_start(xw, event, &screen->cursorp); 3004 } 3005} 3006 3007static void 3008TrackDown(XtermWidget xw, XButtonEvent *event) 3009{ 3010 TScreen *screen = TScreenOf(xw); 3011 CELL cell; 3012 3013 screen->selectUnit = EvalSelectUnit(xw, 3014 event->time, 3015 Select_CHAR, 3016 event->button); 3017 if (screen->numberOfClicks > 1) { 3018 PointToCELL(screen, event->y, event->x, &cell); 3019 screen->replyToEmacs = True; 3020 StartSelect(xw, &cell); 3021 } else { 3022 screen->waitingForTrackInfo = True; 3023 EditorButton(xw, event); 3024 } 3025} 3026 3027#define boundsCheck(x) if (x < 0) \ 3028 x = 0; \ 3029 else if (x >= screen->max_row) \ 3030 x = screen->max_row 3031 3032void 3033TrackMouse(XtermWidget xw, 3034 int func, 3035 const CELL *start, 3036 int firstrow, 3037 int lastrow) 3038{ 3039 TScreen *screen = TScreenOf(xw); 3040 3041 if (screen->waitingForTrackInfo) { /* if Timed, ignore */ 3042 screen->waitingForTrackInfo = False; 3043 3044 if (func != 0) { 3045 CELL first = *start; 3046 3047 boundsCheck(first.row); 3048 boundsCheck(firstrow); 3049 boundsCheck(lastrow); 3050 screen->firstValidRow = firstrow; 3051 screen->lastValidRow = lastrow; 3052 screen->replyToEmacs = True; 3053 StartSelect(xw, &first); 3054 } 3055 } 3056} 3057 3058static void 3059StartSelect(XtermWidget xw, const CELL *cell) 3060{ 3061 TScreen *screen = TScreenOf(xw); 3062 3063 TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col)); 3064 if (screen->cursor_state) 3065 HideCursor(xw); 3066 if (screen->numberOfClicks == 1) { 3067 /* set start of selection */ 3068 screen->rawPos = *cell; 3069 } 3070 /* else use old values in rawPos */ 3071 screen->saveStartR = screen->startExt = screen->rawPos; 3072 screen->saveEndR = screen->endExt = screen->rawPos; 3073 if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) { 3074 screen->eventMode = LEFTEXTENSION; 3075 screen->startExt = *cell; 3076 } else { 3077 screen->eventMode = RIGHTEXTENSION; 3078 screen->endExt = *cell; 3079 } 3080 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True); 3081} 3082 3083static void 3084EndExtend(XtermWidget xw, 3085 XEvent *event, /* must be XButtonEvent */ 3086 String *params, /* selections */ 3087 Cardinal num_params, 3088 Bool use_cursor_loc) 3089{ 3090 CELL cell; 3091 TScreen *screen = TScreenOf(xw); 3092 3093 TRACE_EVENT("EndExtend", event, params, &num_params); 3094 if (use_cursor_loc) { 3095 cell = screen->cursorp; 3096 } else { 3097 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell); 3098 } 3099 ExtendExtend(xw, &cell); 3100 3101 screen->lastButtonUpTime = event->xbutton.time; 3102 screen->lastButton = event->xbutton.button; 3103#if OPT_BLOCK_SELECT 3104 screen->lastSelectWasBlock = screen->blockSelecting; 3105#endif 3106 3107 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) { 3108 if (screen->replyToEmacs) { 3109 Char line[64]; 3110 unsigned count = 0; 3111 3112 if (screen->control_eight_bits) { 3113 line[count++] = ANSI_CSI; 3114 } else { 3115 line[count++] = ANSI_ESC; 3116 line[count++] = '['; 3117 } 3118 if (isSameCELL(&(screen->rawPos), &(screen->startSel)) 3119 && isSameCELL(&cell, &(screen->endSel))) { 3120 /* Use short-form emacs select */ 3121 3122 switch (screen->extend_coords) { 3123 case 0: 3124 case SET_EXT_MODE_MOUSE: 3125 line[count++] = 't'; 3126 break; 3127 case SET_SGR_EXT_MODE_MOUSE: 3128 case SET_PIXEL_POSITION_MOUSE: 3129 line[count++] = '<'; 3130 break; 3131 } 3132 3133 count = EmitMousePosition(screen, line, count, screen->endSel.col); 3134 count = EmitMousePositionSeparator(screen, line, count); 3135 count = EmitMousePosition(screen, line, count, screen->endSel.row); 3136 3137 switch (screen->extend_coords) { 3138 case SET_SGR_EXT_MODE_MOUSE: 3139 case SET_URXVT_EXT_MODE_MOUSE: 3140 case SET_PIXEL_POSITION_MOUSE: 3141 line[count++] = 't'; 3142 break; 3143 } 3144 } else { 3145 /* long-form, specify everything */ 3146 3147 switch (screen->extend_coords) { 3148 case 0: 3149 case SET_EXT_MODE_MOUSE: 3150 line[count++] = 'T'; 3151 break; 3152 case SET_SGR_EXT_MODE_MOUSE: 3153 case SET_PIXEL_POSITION_MOUSE: 3154 line[count++] = '<'; 3155 break; 3156 } 3157 3158 count = EmitMousePosition(screen, line, count, screen->startSel.col); 3159 count = EmitMousePositionSeparator(screen, line, count); 3160 count = EmitMousePosition(screen, line, count, screen->startSel.row); 3161 count = EmitMousePositionSeparator(screen, line, count); 3162 count = EmitMousePosition(screen, line, count, screen->endSel.col); 3163 count = EmitMousePositionSeparator(screen, line, count); 3164 count = EmitMousePosition(screen, line, count, screen->endSel.row); 3165 count = EmitMousePositionSeparator(screen, line, count); 3166 count = EmitMousePosition(screen, line, count, cell.col); 3167 count = EmitMousePositionSeparator(screen, line, count); 3168 count = EmitMousePosition(screen, line, count, cell.row); 3169 3170 switch (screen->extend_coords) { 3171 case SET_SGR_EXT_MODE_MOUSE: 3172 case SET_URXVT_EXT_MODE_MOUSE: 3173 case SET_PIXEL_POSITION_MOUSE: 3174 line[count++] = 'T'; 3175 break; 3176 } 3177 } 3178 v_write(screen->respond, line, (size_t) count); 3179 UnHiliteText(xw); 3180 } 3181 } 3182 SelectSet(xw, event, params, num_params); 3183 screen->eventMode = NORMAL; 3184} 3185 3186void 3187HandleSelectSet(Widget w, 3188 XEvent *event, 3189 String *params, 3190 Cardinal *num_params) 3191{ 3192 XtermWidget xw; 3193 3194 if ((xw = getXtermWidget(w)) != NULL) { 3195 TRACE_EVENT("HandleSelectSet", event, params, num_params); 3196 SelectSet(xw, event, params, *num_params); 3197 } 3198} 3199 3200/* ARGSUSED */ 3201static void 3202SelectSet(XtermWidget xw, 3203 XEvent *event GCC_UNUSED, 3204 String *params, 3205 Cardinal num_params) 3206{ 3207 TScreen *screen = TScreenOf(xw); 3208 3209 TRACE(("SelectSet\n")); 3210 /* Only do select stuff if non-null select */ 3211 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) { 3212 Cardinal n; 3213 for (n = 0; n < num_params; ++n) { 3214 SaltTextAway(xw, 3215 TargetToSelection(screen, params[n]), 3216 &(screen->startSel), &(screen->endSel)); 3217 } 3218 _OwnSelection(xw, params, num_params); 3219 } else { 3220 ScrnDisownSelection(xw); 3221 } 3222} 3223 3224#define Abs(x) ((x) < 0 ? -(x) : (x)) 3225 3226/* ARGSUSED */ 3227static void 3228do_start_extend(XtermWidget xw, 3229 XEvent *event, /* must be XButtonEvent* */ 3230 String *params GCC_UNUSED, 3231 Cardinal *num_params GCC_UNUSED, 3232 Bool use_cursor_loc) 3233{ 3234 TScreen *screen = TScreenOf(xw); 3235 int coord; 3236 CELL cell; 3237 3238 if (SendMousePosition(xw, event)) 3239 return; 3240 3241 screen->firstValidRow = 0; 3242 screen->lastValidRow = screen->max_row; 3243#if OPT_READLINE 3244 if (OverrideEvent(event) 3245 || event->xbutton.button != Button3 3246 || !(SCREEN_FLAG(screen, dclick3_deletes))) 3247#endif 3248 screen->selectUnit = EvalSelectUnit(xw, 3249 event->xbutton.time, 3250 screen->selectUnit, 3251 event->xbutton.button); 3252 screen->replyToEmacs = False; 3253 3254#if OPT_READLINE 3255 CheckSecondPress3(xw, screen, event); 3256#endif 3257 3258 if (screen->numberOfClicks == 1 3259 || (SCREEN_FLAG(screen, dclick3_deletes) 3260 && !OverrideEvent(event))) { 3261 /* Save existing selection so we can reestablish it if the guy 3262 extends past the other end of the selection */ 3263 screen->saveStartR = screen->startExt = screen->startRaw; 3264 screen->saveEndR = screen->endExt = screen->endRaw; 3265 } else { 3266 /* He just needed the selection mode changed, use old values. */ 3267 screen->startExt = screen->startRaw = screen->saveStartR; 3268 screen->endExt = screen->endRaw = screen->saveEndR; 3269 } 3270 if (use_cursor_loc) { 3271 cell = screen->cursorp; 3272 } else { 3273 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell); 3274 } 3275 coord = Coordinate(screen, &cell); 3276 3277 if (Abs(coord - Coordinate(screen, &(screen->startSel))) 3278 < Abs(coord - Coordinate(screen, &(screen->endSel))) 3279 || coord < Coordinate(screen, &(screen->startSel))) { 3280 /* point is close to left side of selection */ 3281 screen->eventMode = LEFTEXTENSION; 3282 screen->startExt = cell; 3283 } else { 3284 /* point is close to left side of selection */ 3285 screen->eventMode = RIGHTEXTENSION; 3286 screen->endExt = cell; 3287 } 3288 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True); 3289 3290#if OPT_READLINE 3291 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) 3292 ExtendingSelection = 1; 3293#endif 3294} 3295 3296static void 3297ExtendExtend(XtermWidget xw, const CELL *cell) 3298{ 3299 TScreen *screen = TScreenOf(xw); 3300 int coord = Coordinate(screen, cell); 3301 3302 TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col)); 3303 if (screen->eventMode == LEFTEXTENSION 3304 && ((coord + (screen->selectUnit != Select_CHAR)) 3305 > Coordinate(screen, &(screen->endSel)))) { 3306 /* Whoops, he's changed his mind. Do RIGHTEXTENSION */ 3307 screen->eventMode = RIGHTEXTENSION; 3308 screen->startExt = screen->saveStartR; 3309 } else if (screen->eventMode == RIGHTEXTENSION 3310 && coord < Coordinate(screen, &(screen->startSel))) { 3311 /* Whoops, he's changed his mind. Do LEFTEXTENSION */ 3312 screen->eventMode = LEFTEXTENSION; 3313 screen->endExt = screen->saveEndR; 3314 } 3315 if (screen->eventMode == LEFTEXTENSION) { 3316 screen->startExt = *cell; 3317 } else { 3318 screen->endExt = *cell; 3319 } 3320 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True); 3321 3322#if OPT_READLINE 3323 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) 3324 ExtendingSelection = 1; 3325#endif 3326} 3327 3328void 3329HandleStartExtend(Widget w, 3330 XEvent *event, /* must be XButtonEvent* */ 3331 String *params, /* unused */ 3332 Cardinal *num_params) /* unused */ 3333{ 3334 XtermWidget xw; 3335 3336 if ((xw = getXtermWidget(w)) != NULL) { 3337 TRACE_EVENT("HandleStartExtend", event, params, num_params); 3338 do_start_extend(xw, event, params, num_params, False); 3339 } 3340} 3341 3342void 3343HandleKeyboardStartExtend(Widget w, 3344 XEvent *event, /* must be XButtonEvent* */ 3345 String *params, /* unused */ 3346 Cardinal *num_params) /* unused */ 3347{ 3348 XtermWidget xw; 3349 3350 if ((xw = getXtermWidget(w)) != NULL) { 3351 TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params); 3352 do_start_extend(xw, event, params, num_params, True); 3353 } 3354} 3355 3356void 3357ScrollSelection(TScreen *screen, int amount, Bool always) 3358{ 3359 int minrow = INX2ROW(screen, -screen->savedlines); 3360 int maxrow = INX2ROW(screen, screen->max_row); 3361 int maxcol = screen->max_col; 3362 3363#define scroll_update_one(cell) \ 3364 (cell)->row += amount; \ 3365 if ((cell)->row < minrow) { \ 3366 (cell)->row = minrow; \ 3367 (cell)->col = 0; \ 3368 } \ 3369 if ((cell)->row > maxrow) { \ 3370 (cell)->row = maxrow; \ 3371 (cell)->col = maxcol; \ 3372 } 3373 3374 scroll_update_one(&(screen->startRaw)); 3375 scroll_update_one(&(screen->endRaw)); 3376 scroll_update_one(&(screen->startSel)); 3377 scroll_update_one(&(screen->endSel)); 3378 3379 scroll_update_one(&(screen->rawPos)); 3380 3381 /* 3382 * If we are told to scroll the selection but it lies outside the scrolling 3383 * margins, then that could cause the selection to move (bad). It is not 3384 * simple to fix, because this function is called both for the scrollbar 3385 * actions as well as application scrolling. The 'always' flag is set in 3386 * the former case. The rest of the logic handles the latter. 3387 */ 3388 if (ScrnHaveSelection(screen)) { 3389 int adjust; 3390 3391 adjust = ROW2INX(screen, screen->startH.row); 3392 if (always 3393 || !ScrnHaveRowMargins(screen) 3394 || ScrnIsRowInMargins(screen, adjust)) { 3395 scroll_update_one(&screen->startH); 3396 } 3397 adjust = ROW2INX(screen, screen->endH.row); 3398 if (always 3399 || !ScrnHaveRowMargins(screen) 3400 || ScrnIsRowInMargins(screen, adjust)) { 3401 scroll_update_one(&screen->endH); 3402 } 3403 } 3404 3405 screen->startHCoord = Coordinate(screen, &screen->startH); 3406 screen->endHCoord = Coordinate(screen, &screen->endH); 3407} 3408 3409/*ARGSUSED*/ 3410void 3411ResizeSelection(TScreen *screen, int rows, int cols) 3412{ 3413 rows--; /* decr to get 0-max */ 3414 cols--; 3415 3416 if (screen->startRaw.row > rows) 3417 screen->startRaw.row = rows; 3418 if (screen->startSel.row > rows) 3419 screen->startSel.row = rows; 3420 if (screen->endRaw.row > rows) 3421 screen->endRaw.row = rows; 3422 if (screen->endSel.row > rows) 3423 screen->endSel.row = rows; 3424 if (screen->rawPos.row > rows) 3425 screen->rawPos.row = rows; 3426 3427 if (screen->startRaw.col > cols) 3428 screen->startRaw.col = cols; 3429 if (screen->startSel.col > cols) 3430 screen->startSel.col = cols; 3431 if (screen->endRaw.col > cols) 3432 screen->endRaw.col = cols; 3433 if (screen->endSel.col > cols) 3434 screen->endSel.col = cols; 3435 if (screen->rawPos.col > cols) 3436 screen->rawPos.col = cols; 3437} 3438 3439#if OPT_WIDE_CHARS 3440#define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col)) 3441#endif 3442 3443static void 3444PointToCELL(TScreen *screen, 3445 int y, 3446 int x, 3447 CELL *cell) 3448/* Convert pixel coordinates to character coordinates. 3449 Rows are clipped between firstValidRow and lastValidRow. 3450 Columns are clipped between to be 0 or greater, but are not clipped to some 3451 maximum value. */ 3452{ 3453 cell->row = (y - screen->border) / FontHeight(screen); 3454 if (cell->row < screen->firstValidRow) 3455 cell->row = screen->firstValidRow; 3456 else if (cell->row > screen->lastValidRow) 3457 cell->row = screen->lastValidRow; 3458 cell->col = (x - OriginX(screen)) / FontWidth(screen); 3459 if (cell->col < 0) 3460 cell->col = 0; 3461 else if (cell->col > MaxCols(screen)) { 3462 cell->col = MaxCols(screen); 3463 } 3464#if OPT_WIDE_CHARS 3465 /* 3466 * If we got a click on the right half of a doublewidth character, 3467 * pretend it happened on the left half. 3468 */ 3469 if (cell->col > 0 3470 && isWideCell(cell->row, cell->col - 1) 3471 && (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) { 3472 cell->col -= 1; 3473 } 3474#endif 3475} 3476 3477/* 3478 * Find the last column at which text was drawn on the given row. 3479 */ 3480static int 3481LastTextCol(TScreen *screen, CLineData *ld, int row) 3482{ 3483 int i = -1; 3484 3485 if (ld != NULL) { 3486 if (okScrnRow(screen, row)) { 3487 const IAttr *ch; 3488 for (i = screen->max_col, 3489 ch = ld->attribs + i; 3490 i >= 0 && !(*ch & CHARDRAWN); 3491 ch--, i--) { 3492 ; 3493 } 3494#if OPT_DEC_CHRSET 3495 if (CSET_DOUBLE(GetLineDblCS(ld))) { 3496 i *= 2; 3497 } 3498#endif 3499 } 3500 } 3501 return (i); 3502} 3503 3504#if !OPT_WIDE_CHARS 3505/* 3506** double click table for cut and paste in 8 bits 3507** 3508** This table is divided in four parts : 3509** 3510** - control characters [0,0x1f] U [0x80,0x9f] 3511** - separators [0x20,0x3f] U [0xa0,0xb9] 3512** - binding characters [0x40,0x7f] U [0xc0,0xff] 3513** - exceptions 3514*/ 3515/* *INDENT-OFF* */ 3516static int charClass[256] = 3517{ 3518/* NUL SOH STX ETX EOT ENQ ACK BEL */ 3519 32, 1, 1, 1, 1, 1, 1, 1, 3520/* BS HT NL VT FF CR SO SI */ 3521 1, 32, 1, 1, 1, 1, 1, 1, 3522/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ 3523 1, 1, 1, 1, 1, 1, 1, 1, 3524/* CAN EM SUB ESC FS GS RS US */ 3525 1, 1, 1, 1, 1, 1, 1, 1, 3526/* SP ! " # $ % & ' */ 3527 32, 33, 34, 35, 36, 37, 38, 39, 3528/* ( ) * + , - . / */ 3529 40, 41, 42, 43, 44, 45, 46, 47, 3530/* 0 1 2 3 4 5 6 7 */ 3531 48, 48, 48, 48, 48, 48, 48, 48, 3532/* 8 9 : ; < = > ? */ 3533 48, 48, 58, 59, 60, 61, 62, 63, 3534/* @ A B C D E F G */ 3535 64, 48, 48, 48, 48, 48, 48, 48, 3536/* H I J K L M N O */ 3537 48, 48, 48, 48, 48, 48, 48, 48, 3538/* P Q R S T U V W */ 3539 48, 48, 48, 48, 48, 48, 48, 48, 3540/* X Y Z [ \ ] ^ _ */ 3541 48, 48, 48, 91, 92, 93, 94, 48, 3542/* ` a b c d e f g */ 3543 96, 48, 48, 48, 48, 48, 48, 48, 3544/* h i j k l m n o */ 3545 48, 48, 48, 48, 48, 48, 48, 48, 3546/* p q r s t u v w */ 3547 48, 48, 48, 48, 48, 48, 48, 48, 3548/* x y z { | } ~ DEL */ 3549 48, 48, 48, 123, 124, 125, 126, 1, 3550/* x80 x81 x82 x83 IND NEL SSA ESA */ 3551 1, 1, 1, 1, 1, 1, 1, 1, 3552/* HTS HTJ VTS PLD PLU RI SS2 SS3 */ 3553 1, 1, 1, 1, 1, 1, 1, 1, 3554/* DCS PU1 PU2 STS CCH MW SPA EPA */ 3555 1, 1, 1, 1, 1, 1, 1, 1, 3556/* x98 x99 x9A CSI ST OSC PM APC */ 3557 1, 1, 1, 1, 1, 1, 1, 1, 3558/* - i c/ L ox Y- | So */ 3559 160, 161, 162, 163, 164, 165, 166, 167, 3560/* .. c0 ip << _ R0 - */ 3561 168, 169, 170, 171, 172, 173, 174, 175, 3562/* o +- 2 3 ' u q| . */ 3563 176, 177, 178, 179, 180, 181, 182, 183, 3564/* , 1 2 >> 1/4 1/2 3/4 ? */ 3565 184, 185, 186, 187, 188, 189, 190, 191, 3566/* A` A' A^ A~ A: Ao AE C, */ 3567 48, 48, 48, 48, 48, 48, 48, 48, 3568/* E` E' E^ E: I` I' I^ I: */ 3569 48, 48, 48, 48, 48, 48, 48, 48, 3570/* D- N~ O` O' O^ O~ O: X */ 3571 48, 48, 48, 48, 48, 48, 48, 215, 3572/* O/ U` U' U^ U: Y' P B */ 3573 48, 48, 48, 48, 48, 48, 48, 48, 3574/* a` a' a^ a~ a: ao ae c, */ 3575 48, 48, 48, 48, 48, 48, 48, 48, 3576/* e` e' e^ e: i` i' i^ i: */ 3577 48, 48, 48, 48, 48, 48, 48, 48, 3578/* d n~ o` o' o^ o~ o: -: */ 3579 48, 48, 48, 48, 48, 48, 48, 247, 3580/* o/ u` u' u^ u: y' P y: */ 3581 48, 48, 48, 48, 48, 48, 48, 48}; 3582/* *INDENT-ON* */ 3583 3584int 3585SetCharacterClassRange(int low, /* in range of [0..255] */ 3586 int high, 3587 int value) /* arbitrary */ 3588{ 3589 3590 if (low < 0 || high > 255 || high < low) 3591 return (-1); 3592 3593 for (; low <= high; low++) 3594 charClass[low] = value; 3595 3596 return (0); 3597} 3598#endif 3599 3600static int 3601class_of(LineData *ld, const CELL *cell) 3602{ 3603 CELL temp = *cell; 3604 int result = 0; 3605 3606#if OPT_DEC_CHRSET 3607 if (CSET_DOUBLE(GetLineDblCS(ld))) { 3608 temp.col /= 2; 3609 } 3610#endif 3611 if (temp.col < (int) ld->lineSize) 3612 result = CharacterClass((int) (ld->charData[temp.col])); 3613 return result; 3614} 3615 3616#if OPT_WIDE_CHARS 3617#define CClassSelects(name, cclass) \ 3618 (CClassOf(name) == cclass \ 3619 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR) 3620#else 3621#define CClassSelects(name, cclass) \ 3622 (class_of(ld.name, &((screen->name))) == cclass) 3623#endif 3624 3625#define CClassOf(name) class_of(ld.name, &((screen->name))) 3626 3627#if OPT_REPORT_CCLASS 3628static int 3629show_cclass_range(int lo, int hi) 3630{ 3631 int cclass = CharacterClass(lo); 3632 int ident = (cclass == lo); 3633 int more = 0; 3634 if (ident) { 3635 int ch; 3636 for (ch = lo + 1; ch <= hi; ch++) { 3637 if (CharacterClass(ch) != ch) { 3638 ident = 0; 3639 break; 3640 } 3641 } 3642 if (ident && (hi < 255)) { 3643 ch = hi + 1; 3644 if (CharacterClass(ch) == ch) { 3645 if (ch >= 255 || CharacterClass(ch + 1) != ch) { 3646 more = 1; 3647 } 3648 } 3649 } 3650 } 3651 if (!more) { 3652 if (lo == hi) { 3653 printf("\t%d", lo); 3654 } else { 3655 printf("\t%d-%d", lo, hi); 3656 } 3657 if (!ident) 3658 printf(":%d", cclass); 3659 if (hi < 255) 3660 printf(", \\"); 3661 printf("\n"); 3662 } 3663 return !more; 3664} 3665 3666void 3667report_char_class(XtermWidget xw) 3668{ 3669 /* simple table, to match documentation */ 3670 static const char charnames[] = 3671 "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0" 3672 " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0" 3673 "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0" 3674 "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0" 3675 " SP\0" " !\0" " \"\0" " #\0" " $\0" " %\0" " &\0" " '\0" 3676 " (\0" " )\0" " *\0" " +\0" " ,\0" " -\0" " .\0" " /\0" 3677 " 0\0" " 1\0" " 2\0" " 3\0" " 4\0" " 5\0" " 6\0" " 7\0" 3678 " 8\0" " 9\0" " :\0" " ;\0" " <\0" " =\0" " >\0" " ?\0" 3679 " @\0" " A\0" " B\0" " C\0" " D\0" " E\0" " F\0" " G\0" 3680 " H\0" " I\0" " J\0" " K\0" " L\0" " M\0" " N\0" " O\0" 3681 " P\0" " Q\0" " R\0" " S\0" " T\0" " U\0" " V\0" " W\0" 3682 " X\0" " Y\0" " Z\0" " [\0" " \\\0" " ]\0" " ^\0" " _\0" 3683 " `\0" " a\0" " b\0" " c\0" " d\0" " e\0" " f\0" " g\0" 3684 " h\0" " i\0" " j\0" " k\0" " l\0" " m\0" " n\0" " o\0" 3685 " p\0" " q\0" " r\0" " s\0" " t\0" " u\0" " v\0" " w\0" 3686 " x\0" " y\0" " z\0" " {\0" " |\0" " }\0" " ~\0" "DEL\0" 3687 "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0" 3688 "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0" 3689 "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0" 3690 "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0" 3691 " -\0" " i\0" " c/\0" " L\0" " ox\0" " Y-\0" " |\0" " So\0" 3692 " ..\0" " c0\0" " ip\0" " <<\0" " _\0" " \0" " R0\0" " -\0" 3693 " o\0" " +-\0" " 2\0" " 3\0" " '\0" " u\0" " q|\0" " .\0" 3694 " ,\0" " 1\0" " 2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" " ?\0" 3695 " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0" 3696 " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0" 3697 " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" " X\0" 3698 " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" " P\0" " B\0" 3699 " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0" 3700 " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0" 3701 " d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0" 3702 " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" " P\0" " y:\0"; 3703 int ch, dh; 3704 int class_p; 3705 3706 (void) xw; 3707 3708 printf("static int charClass[256] = {\n"); 3709 for (ch = 0; ch < 256; ++ch) { 3710 const char *s = charnames + (ch * 4); 3711 if ((ch & 7) == 0) 3712 printf("/*"); 3713 printf(" %s ", s); 3714 if (((ch + 1) & 7) == 0) { 3715 printf("*/\n "); 3716 for (dh = ch - 7; dh <= ch; ++dh) { 3717 printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ","); 3718 } 3719 printf("\n"); 3720 } 3721 } 3722 3723 /* print the table as if it were the charClass resource */ 3724 printf("\n"); 3725 printf("The table is equivalent to this \"charClass\" resource:\n"); 3726 class_p = CharacterClass(dh = 0); 3727 for (ch = 0; ch < 256; ++ch) { 3728 int class_c = CharacterClass(ch); 3729 if (class_c != class_p) { 3730 if (show_cclass_range(dh, ch - 1)) { 3731 dh = ch; 3732 class_p = class_c; 3733 } 3734 } 3735 } 3736 if (dh < 255) { 3737 show_cclass_range(dh, 255); 3738 } 3739 3740 if_OPT_WIDE_CHARS(TScreenOf(xw), { 3741 /* if this is a wide-character configuration, print all intervals */ 3742 report_wide_char_class(); 3743 }); 3744} 3745#endif 3746 3747/* 3748 * If the given column is past the end of text on the given row, bump to the 3749 * beginning of the next line. 3750 */ 3751static Boolean 3752okPosition(TScreen *screen, 3753 LineData **ld, 3754 CELL *cell) 3755{ 3756 Boolean result = True; 3757 3758 assert(ld != NULL); 3759 assert(*ld != NULL); 3760 3761 if (*ld == NULL) { 3762 result = False; 3763 TRACE(("okPosition LineData is null!\n")); 3764 } else if (cell->row > screen->max_row) { 3765 result = False; 3766 TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row)); 3767 } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) { 3768 TRACE(("okPosition cell col %d > screen max %d\n", cell->col, 3769 (LastTextCol(screen, *ld, cell->row) + 1))); 3770 if (cell->row < screen->max_row) { 3771 TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row)); 3772 cell->col = 0; 3773 *ld = GET_LINEDATA(screen, ++cell->row); 3774 result = False; 3775 } 3776 } 3777 return result; 3778} 3779 3780static void 3781trimLastLine(TScreen *screen, 3782 LineData **ld, 3783 CELL *last) 3784{ 3785 if (screen->cutNewline && last->row < screen->max_row) { 3786 last->col = 0; 3787 *ld = GET_LINEDATA(screen, ++last->row); 3788 } else { 3789 last->col = LastTextCol(screen, *ld, last->row) + 1; 3790 } 3791} 3792 3793#if OPT_SELECT_REGEX 3794/* 3795 * Returns the first row of a wrapped line. 3796 */ 3797static int 3798firstRowOfLine(TScreen *screen, int row, Bool visible) 3799{ 3800 LineData *ld = NULL; 3801 int limit = visible ? 0 : -screen->savedlines; 3802 3803 while (row > limit && 3804 (ld = GET_LINEDATA(screen, row - 1)) != NULL && 3805 LineTstWrapped(ld)) { 3806 --row; 3807 } 3808 return row; 3809} 3810 3811/* 3812 * Returns the last row of a wrapped line. 3813 */ 3814static int 3815lastRowOfLine(TScreen *screen, int row) 3816{ 3817 LineData *ld; 3818 3819 while (row < screen->max_row && 3820 (ld = GET_LINEDATA(screen, row)) != NULL && 3821 LineTstWrapped(ld)) { 3822 ++row; 3823 } 3824 return row; 3825} 3826 3827/* 3828 * Returns the number of cells on the range of rows. 3829 */ 3830static unsigned 3831lengthOfLines(TScreen *screen, int firstRow, int lastRow) 3832{ 3833 unsigned length = 0; 3834 int n; 3835 3836 for (n = firstRow; n <= lastRow; ++n) { 3837 LineData *ld = GET_LINEDATA(screen, n); 3838 int value = LastTextCol(screen, ld, n); 3839 if (value >= 0) 3840 length += (unsigned) (value + 1); 3841 } 3842 return length; 3843} 3844 3845/* 3846 * Make a copy of the wrapped-line which corresponds to the given row as a 3847 * string of bytes. Construct an index for the columns from the beginning of 3848 * the line. 3849 */ 3850static char * 3851make_indexed_text(TScreen *screen, int row, unsigned length, int *indexed) 3852{ 3853 Char *result = NULL; 3854 size_t need = (length + 1); 3855 3856 /* 3857 * Get a quick upper bound to the number of bytes needed, if the whole 3858 * string were UTF-8. 3859 */ 3860 if_OPT_WIDE_CHARS(screen, { 3861 need *= ((screen->lineExtra + 1) * 6); 3862 }); 3863 3864 if ((result = TypeCallocN(Char, need + 1)) != NULL) { 3865 LineData *ld = GET_LINEDATA(screen, row); 3866 unsigned used = 0; 3867 Char *last = result; 3868 3869 do { 3870 int col = 0; 3871 int limit = LastTextCol(screen, ld, row); 3872 3873 while (col <= limit) { 3874 Char *next = last; 3875 unsigned data = ld->charData[col]; 3876 3877 assert(col < (int) ld->lineSize); 3878 /* some internal points may not be drawn */ 3879 if (data == 0) 3880 data = ' '; 3881 3882 if_WIDE_OR_NARROW(screen, { 3883 next = convertToUTF8(last, data); 3884 } 3885 , { 3886 *next++ = CharOf(data); 3887 }); 3888 3889 if_OPT_WIDE_CHARS(screen, { 3890 size_t off; 3891 for_each_combData(off, ld) { 3892 data = ld->combData[off][col]; 3893 if (data == 0) 3894 break; 3895 next = convertToUTF8(next, data); 3896 } 3897 }); 3898 3899 indexed[used] = (int) (last - result); 3900 *next = 0; 3901 /* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */ 3902 last = next; 3903 ++used; 3904 ++col; 3905 indexed[used] = (int) (next - result); 3906 } 3907 } while (used < length && 3908 LineTstWrapped(ld) && 3909 (ld = GET_LINEDATA(screen, ++row)) != NULL && 3910 row < screen->max_row); 3911 } 3912 /* TRACE(("result:%s\n", result)); */ 3913 return (char *) result; 3914} 3915 3916/* 3917 * Find the column given an offset into the character string by using the 3918 * index constructed in make_indexed_text(). 3919 */ 3920static int 3921indexToCol(const int *indexed, int len, int off) 3922{ 3923 int col = 0; 3924 while (indexed[col] < len) { 3925 if (indexed[col] >= off) 3926 break; 3927 ++col; 3928 } 3929 return col; 3930} 3931 3932/* 3933 * Given a row number, and a column offset from that (which may be wrapped), 3934 * set the cell to the actual row/column values. 3935 */ 3936static void 3937columnToCell(TScreen *screen, int row, int col, CELL *cell) 3938{ 3939 while (row < screen->max_row) { 3940 CLineData *ld = GET_LINEDATA(screen, row); 3941 int last = LastTextCol(screen, ld, row); 3942 3943 /* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */ 3944 if (col <= last) { 3945 break; 3946 } 3947 /* 3948 * Stop if the current row does not wrap (does not continue the current 3949 * line). 3950 */ 3951 if (!LineTstWrapped(ld)) { 3952 col = last + 1; 3953 break; 3954 } 3955 col -= (last + 1); 3956 ++row; 3957 } 3958 if (col < 0) 3959 col = 0; 3960 cell->row = row; 3961 cell->col = col; 3962} 3963 3964/* 3965 * Given a cell, find the corresponding column offset. 3966 */ 3967static int 3968cellToColumn(TScreen *screen, CELL *cell) 3969{ 3970 CLineData *ld = NULL; 3971 int col = cell->col; 3972 int row = firstRowOfLine(screen, cell->row, False); 3973 while (row < cell->row) { 3974 ld = GET_LINEDATA(screen, row); 3975 col += LastTextCol(screen, ld, row++); 3976 } 3977#if OPT_DEC_CHRSET 3978 if (ld == NULL) 3979 ld = GET_LINEDATA(screen, row); 3980 if (CSET_DOUBLE(GetLineDblCS(ld))) 3981 col /= 2; 3982#endif 3983 return col; 3984} 3985 3986static void 3987do_select_regex(TScreen *screen, CELL *startc, CELL *endc) 3988{ 3989 LineData *ld = GET_LINEDATA(screen, startc->row); 3990 int inx = ((screen->numberOfClicks - 1) % screen->maxClicks); 3991 char *expr = screen->selectExpr[inx]; 3992 regex_t preg; 3993 regmatch_t match; 3994 3995 TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr))); 3996 if (okPosition(screen, &ld, startc) && expr != NULL) { 3997 if (regcomp(&preg, expr, REG_EXTENDED) == 0) { 3998 int firstRow = firstRowOfLine(screen, startc->row, True); 3999 int lastRow = lastRowOfLine(screen, firstRow); 4000 unsigned size = lengthOfLines(screen, firstRow, lastRow); 4001 int actual = cellToColumn(screen, startc); 4002 int *indexed; 4003 4004 TRACE(("regcomp ok rows %d..%d bytes %d\n", 4005 firstRow, lastRow, size)); 4006 4007 if ((indexed = TypeCallocN(int, size + 1)) != NULL) { 4008 char *search; 4009 if ((search = make_indexed_text(screen, 4010 firstRow, 4011 size, 4012 indexed)) != NULL) { 4013 int len = (int) strlen(search); 4014 int col; 4015 int offset; 4016 int best_col = -1; 4017 int best_len = -1; 4018 4019 startc->row = 0; 4020 startc->col = 0; 4021 endc->row = 0; 4022 endc->col = 0; 4023 4024 for (col = 0; (offset = indexed[col]) < len; ++col) { 4025 if (regexec(&preg, 4026 search + offset, 4027 (size_t) 1, &match, 4028 col ? REG_NOTBOL : 0) == 0) { 4029 int start_inx = (int) (match.rm_so + offset); 4030 int finis_inx = (int) (match.rm_eo + offset); 4031 int start_col = indexToCol(indexed, len, start_inx); 4032 int finis_col = indexToCol(indexed, len, finis_inx); 4033 4034 if (start_col <= actual && 4035 actual <= finis_col) { 4036 int test = finis_col - start_col; 4037 if (best_len < test) { 4038 best_len = test; 4039 best_col = start_col; 4040 TRACE(("match column %d len %d\n", 4041 best_col, 4042 best_len)); 4043 } 4044 } 4045 } 4046 } 4047 if (best_col >= 0) { 4048 int best_nxt = best_col + best_len; 4049 columnToCell(screen, firstRow, best_col, startc); 4050 columnToCell(screen, firstRow, best_nxt, endc); 4051 TRACE(("search::%s\n", search)); 4052 TRACE(("indexed:%d..%d -> %d..%d\n", 4053 best_col, best_nxt, 4054 indexed[best_col], 4055 indexed[best_nxt])); 4056 TRACE(("matched:%d:%s\n", 4057 indexed[best_nxt] - 4058 indexed[best_col], 4059 visibleChars((Char *) (search + indexed[best_col]), 4060 (unsigned) (indexed[best_nxt] - 4061 indexed[best_col])))); 4062 } 4063 free(search); 4064 } 4065 free(indexed); 4066#if OPT_DEC_CHRSET 4067 if ((ld = GET_LINEDATA(screen, startc->row)) != NULL) { 4068 if (CSET_DOUBLE(GetLineDblCS(ld))) 4069 startc->col *= 2; 4070 } 4071 if ((ld = GET_LINEDATA(screen, endc->row)) != NULL) { 4072 if (CSET_DOUBLE(GetLineDblCS(ld))) 4073 endc->col *= 2; 4074 } 4075#endif 4076 } 4077 regfree(&preg); 4078 } 4079 } 4080} 4081#endif /* OPT_SELECT_REGEX */ 4082 4083#define InitRow(name) \ 4084 ld.name = GET_LINEDATA(screen, screen->name.row) 4085 4086#define NextRow(name) \ 4087 ld.name = GET_LINEDATA(screen, ++screen->name.row) 4088 4089#define PrevRow(name) \ 4090 ld.name = GET_LINEDATA(screen, --screen->name.row) 4091 4092#define MoreRows(name) \ 4093 (screen->name.row < screen->max_row) 4094 4095#define isPrevWrapped(name) \ 4096 (screen->name.row > 0 \ 4097 && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != NULL \ 4098 && LineTstWrapped(ltmp)) 4099 4100/* 4101 * sets startSel endSel 4102 * ensuring that they have legal values 4103 */ 4104static void 4105ComputeSelect(XtermWidget xw, 4106 const CELL *startc, 4107 const CELL *endc, 4108 Bool extend, 4109 Bool normal) 4110{ 4111 TScreen *screen = TScreenOf(xw); 4112 4113 int cclass; 4114 CELL first = *startc; 4115 CELL last = *endc; 4116 Boolean ignored = False; 4117 4118 struct { 4119 LineData *startSel; 4120 LineData *endSel; 4121 } ld; 4122 LineData *ltmp; 4123 4124 TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n", 4125 first.row, first.col, 4126 last.row, last.col, 4127 extend ? "" : "no")); 4128 4129#if OPT_WIDE_CHARS 4130 if (first.col > 1 4131 && isWideCell(first.row, first.col - 1) 4132 && XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) { 4133 TRACE(("Adjusting start. Changing downwards from %i.\n", first.col)); 4134 first.col -= 1; 4135 if (last.col == (first.col + 1)) 4136 last.col--; 4137 } 4138 4139 if (last.col > 1 4140 && isWideCell(last.row, last.col - 1) 4141 && XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) { 4142 last.col += 1; 4143 } 4144#endif 4145 4146 if (Coordinate(screen, &first) <= Coordinate(screen, &last)) { 4147 screen->startSel = screen->startRaw = first; 4148 screen->endSel = screen->endRaw = last; 4149 } else { /* Swap them */ 4150 screen->startSel = screen->startRaw = last; 4151 screen->endSel = screen->endRaw = first; 4152 } 4153 4154 InitRow(startSel); 4155 InitRow(endSel); 4156 4157 switch (screen->selectUnit) { 4158 case Select_CHAR: 4159#if OPT_BLOCK_SELECT 4160 /* Allow block selecting past EOL */ 4161 if (screen->blockSelecting) 4162 break; 4163#endif 4164 (void) okPosition(screen, &(ld.startSel), &(screen->startSel)); 4165 (void) okPosition(screen, &(ld.endSel), &(screen->endSel)); 4166 break; 4167 4168 case Select_WORD: 4169 TRACE(("Select_WORD\n")); 4170 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) { 4171 CELL mark; 4172 cclass = CClassOf(startSel); 4173 TRACE(("...starting with class %d\n", cclass)); 4174 do { 4175 mark = screen->startSel; 4176 --screen->startSel.col; 4177 if (screen->startSel.col < 0 4178 && isPrevWrapped(startSel)) { 4179 PrevRow(startSel); 4180 screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row); 4181 } 4182 } while (screen->startSel.col >= 0 4183 && CClassSelects(startSel, cclass)); 4184 if (normal) 4185 ++screen->startSel.col; 4186 else 4187 screen->startSel = mark; 4188 } 4189#if OPT_WIDE_CHARS 4190#define SkipHiddenCell(mark) \ 4191 if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \ 4192 mark.col++ 4193#else 4194#define SkipHiddenCell(mark) /* nothing */ 4195#endif 4196 SkipHiddenCell(screen->startSel); 4197 4198 if (!normal) { 4199 screen->endSel = screen->startSel; 4200 ld.endSel = ld.startSel; 4201 } 4202 4203 if (okPosition(screen, &(ld.endSel), &(screen->endSel))) { 4204 int length = LastTextCol(screen, ld.endSel, screen->endSel.row); 4205 cclass = CClassOf(endSel); 4206 TRACE(("...ending with class %d\n", cclass)); 4207 do { 4208 ++screen->endSel.col; 4209 if (screen->endSel.col > length 4210 && LineTstWrapped(ld.endSel)) { 4211 if (!MoreRows(endSel)) 4212 break; 4213 screen->endSel.col = 0; 4214 NextRow(endSel); 4215 length = LastTextCol(screen, ld.endSel, screen->endSel.row); 4216 } 4217 } while (screen->endSel.col <= length 4218 && CClassSelects(endSel, cclass)); 4219 if (normal 4220 && screen->endSel.col > length + 1 4221 && MoreRows(endSel)) { 4222 screen->endSel.col = 0; 4223 NextRow(endSel); 4224 } 4225 } 4226 SkipHiddenCell(screen->endSel); 4227 4228 screen->saveStartW = screen->startSel; 4229 break; 4230 4231 case Select_LINE: 4232 TRACE(("Select_LINE\n")); 4233 while (LineTstWrapped(ld.endSel) 4234 && MoreRows(endSel)) { 4235 NextRow(endSel); 4236 } 4237 if (screen->cutToBeginningOfLine 4238 || screen->startSel.row < screen->saveStartW.row) { 4239 screen->startSel.col = 0; 4240 while (isPrevWrapped(startSel)) { 4241 PrevRow(startSel); 4242 } 4243 } else if (!extend) { 4244 if ((first.row < screen->saveStartW.row) 4245 || (isSameRow(&first, &(screen->saveStartW)) 4246 && first.col < screen->saveStartW.col)) { 4247 screen->startSel.col = 0; 4248 while (isPrevWrapped(startSel)) { 4249 PrevRow(startSel); 4250 } 4251 } else { 4252 screen->startSel = screen->saveStartW; 4253 } 4254 } 4255 trimLastLine(screen, &(ld.endSel), &(screen->endSel)); 4256 break; 4257 4258 case Select_GROUP: /* paragraph */ 4259 TRACE(("Select_GROUP\n")); 4260 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) { 4261 /* scan backward for beginning of group */ 4262 while (screen->startSel.row > 0 && 4263 (LastTextCol(screen, ld.startSel, screen->startSel.row - 4264 1) > 0 || 4265 isPrevWrapped(startSel))) { 4266 PrevRow(startSel); 4267 } 4268 screen->startSel.col = 0; 4269 /* scan forward for end of group */ 4270 while (MoreRows(endSel) && 4271 (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) > 4272 0 || 4273 LineTstWrapped(ld.endSel))) { 4274 NextRow(endSel); 4275 } 4276 trimLastLine(screen, &(ld.endSel), &(screen->endSel)); 4277 } 4278 break; 4279 4280 case Select_PAGE: /* everything one can see */ 4281 TRACE(("Select_PAGE\n")); 4282 screen->startSel.row = 0; 4283 screen->startSel.col = 0; 4284 screen->endSel.row = MaxRows(screen); 4285 screen->endSel.col = 0; 4286 break; 4287 4288 case Select_ALL: /* counts scrollback if in normal screen */ 4289 TRACE(("Select_ALL\n")); 4290 screen->startSel.row = -screen->savedlines; 4291 screen->startSel.col = 0; 4292 screen->endSel.row = MaxRows(screen); 4293 screen->endSel.col = 0; 4294 break; 4295 4296#if OPT_SELECT_REGEX 4297 case Select_REGEX: 4298 do_select_regex(screen, &(screen->startSel), &(screen->endSel)); 4299 break; 4300#endif 4301 4302 case NSELECTUNITS: /* always ignore */ 4303 ignored = True; 4304 break; 4305 } 4306 4307 if (!ignored) { 4308 /* check boundaries */ 4309 ScrollSelection(screen, 0, False); 4310 TrackText(xw, &(screen->startSel), &(screen->endSel)); 4311 } 4312 4313 return; 4314} 4315 4316/* Guaranteed (first.row, first.col) <= (last.row, last.col) */ 4317static void 4318TrackText(XtermWidget xw, 4319 const CELL *firstp, 4320 const CELL *lastp) 4321{ 4322 TScreen *screen = TScreenOf(xw); 4323 int from, to; 4324 CELL old_start, old_end; 4325 CELL first = *firstp; 4326 CELL last = *lastp; 4327 4328 TRACE(("TrackText(first=%d,%d, last=%d,%d)\n", 4329 first.row, first.col, last.row, last.col)); 4330 4331 old_start = screen->startH; 4332 old_end = screen->endH; 4333 TRACE(("...previous(first=%d,%d, last=%d,%d)\n", 4334 old_start.row, old_start.col, 4335 old_end.row, old_end.col)); 4336 if (isSameCELL(&first, &old_start) && 4337 isSameCELL(&last, &old_end)) { 4338 return; 4339 } 4340 4341 screen->startH = first; 4342 screen->endH = last; 4343 from = Coordinate(screen, &screen->startH); 4344 to = Coordinate(screen, &screen->endH); 4345 if (to <= screen->startHCoord || from > screen->endHCoord 4346#if OPT_BLOCK_SELECT 4347 || screen->blockSelecting 4348 || screen->blockSelecting != screen->lastSelectWasBlock 4349#endif 4350 ) { 4351#if OPT_BLOCK_SELECT 4352 /* Either no overlap whatsoever between old and new hilite, 4353 or we're in block select mode (or just came from it), 4354 in which case there is no optimization possible. */ 4355 if (screen->lastSelectWasBlock) { 4356 /* If we just came from block select mode, we need to 4357 unhighlight more aggressively. */ 4358 old_start.col = 0; 4359 old_end.col = MaxCols(screen); 4360 } 4361#endif 4362 ReHiliteText(xw, &old_start, &old_end); 4363 ReHiliteText(xw, &first, &last); 4364 } else { 4365 if (from < screen->startHCoord) { 4366 /* Extend left end */ 4367 ReHiliteText(xw, &first, &old_start); 4368 } else if (from > screen->startHCoord) { 4369 /* Shorten left end */ 4370 ReHiliteText(xw, &old_start, &first); 4371 } 4372 if (to > screen->endHCoord) { 4373 /* Extend right end */ 4374 ReHiliteText(xw, &old_end, &last); 4375 } else if (to < screen->endHCoord) { 4376 /* Shorten right end */ 4377 ReHiliteText(xw, &last, &old_end); 4378 } 4379 } 4380 screen->startHCoord = from; 4381 screen->endHCoord = to; 4382} 4383 4384static void 4385UnHiliteText(XtermWidget xw) 4386{ 4387 TrackText(xw, &zeroCELL, &zeroCELL); 4388} 4389 4390/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */ 4391static void 4392ReHiliteText(XtermWidget xw, 4393 const CELL *firstp, 4394 const CELL *lastp) 4395{ 4396 TScreen *screen = TScreenOf(xw); 4397 CELL first = *firstp; 4398 CELL last = *lastp; 4399 4400 TRACE(("ReHiliteText from %d.%d to %d.%d\n", 4401 first.row, first.col, last.row, last.col)); 4402 4403 if (first.row < 0) 4404 first.row = first.col = 0; 4405 else if (first.row > screen->max_row) 4406 return; /* nothing to do, since last.row >= first.row */ 4407 4408 if (last.row < 0) 4409 return; /* nothing to do, since first.row <= last.row */ 4410 else if (last.row > screen->max_row) { 4411 last.row = screen->max_row; 4412 last.col = MaxCols(screen); 4413 } 4414 if (isSameCELL(&first, &last)) 4415 return; 4416 4417#if OPT_BLOCK_SELECT 4418 if (screen->blockSelecting) { 4419 /* In block select mode, there is no special case for the first or 4420 last rows. Also, unlike normal selections, there can be 4421 unselected text on both sides of the selection per row, so we 4422 refresh all columns. */ 4423 int row; 4424 for (row = first.row; row <= last.row; row++) { 4425 ScrnRefresh(xw, row, 0, 1, MaxCols(screen), True); 4426 } 4427 } else 4428#endif 4429 if (!isSameRow(&first, &last)) { /* do multiple rows */ 4430 int i; 4431 if ((i = screen->max_col - first.col + 1) > 0) { /* first row */ 4432 ScrnRefresh(xw, first.row, first.col, 1, i, True); 4433 } 4434 if ((i = last.row - first.row - 1) > 0) { /* middle rows */ 4435 ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True); 4436 } 4437 if (last.col > 0 && last.row <= screen->max_row) { /* last row */ 4438 ScrnRefresh(xw, last.row, 0, 1, last.col, True); 4439 } 4440 } else { /* do single row */ 4441 ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True); 4442 } 4443} 4444 4445/* 4446 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), 4447 * and that both points are valid 4448 * (may have cell->row = screen->max_row+1, cell->col = 0). 4449 */ 4450static void 4451SaltTextAway(XtermWidget xw, 4452 int which, 4453 const CELL *cellc, 4454 const CELL *cell) 4455{ 4456 TScreen *screen = TScreenOf(xw); 4457 SelectedCells *scp; 4458 int i; 4459 int eol; 4460 int need = 0; 4461 size_t have = 0; 4462 Char *line; 4463 Char *lp; 4464 CELL first = *cellc; 4465 CELL last = *cell; 4466 4467 if (which < 0 || which >= MAX_SELECTIONS) { 4468 TRACE(("SaltTextAway - which selection?\n")); 4469 return; 4470 } 4471 scp = &(screen->selected_cells[which]); 4472 4473 TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n", 4474 which, first.row, first.col, last.row, last.col)); 4475 4476 if (isSameRow(&first, &last) && first.col > last.col) { 4477 int tmp; 4478 EXCHANGE(first.col, last.col, tmp); 4479 } 4480 4481 --last.col; 4482 /* first we need to know how long the string is before we can save it */ 4483 4484 if (isSameRow(&last, &first)) { 4485 need = Length(screen, first.row, first.col, last.col); 4486 } else { /* two cases, cut is on same line, cut spans multiple lines */ 4487 need += Length(screen, first.row, first.col, screen->max_col) + 1; 4488 for (i = first.row + 1; i < last.row; i++) 4489 need += Length(screen, i, 0, screen->max_col) + 1; 4490 if (last.col >= 0) 4491 need += Length(screen, last.row, 0, last.col); 4492 } 4493 4494 /* UTF-8 may require more space */ 4495 if_OPT_WIDE_CHARS(screen, { 4496 if (need > 0) { 4497 if (screen->max_combining > 0) 4498 need += screen->max_combining; 4499 need *= 6; 4500 } 4501 }); 4502 4503 /* now get some memory to save it in */ 4504 if (need < 0) 4505 return; 4506 4507 if (scp->data_limit <= (unsigned) need) { 4508 if ((line = (Char *) malloc((size_t) need + 1)) == NULL) 4509 SysError(ERROR_BMALLOC2); 4510 free(scp->data_buffer); 4511 scp->data_buffer = line; 4512 scp->data_limit = (size_t) (need + 1); 4513 } else { 4514 line = scp->data_buffer; 4515 } 4516 4517 if (line == NULL) 4518 return; 4519 4520 line[need] = '\0'; /* make sure it is null terminated */ 4521 lp = line; /* lp points to where to save the text */ 4522 if (isSameRow(&last, &first)) { 4523 lp = SaveText(screen, last.row, first.col, last.col, lp, &eol); 4524 } 4525#if OPT_BLOCK_SELECT 4526 else if (screen->blockSelecting) { 4527 /* In block select mode, find the left most column of the block. 4528 This can be from either the start or the end of the selection. */ 4529 int blockFirst, blockLast; 4530 if (first.col < last.col) { 4531 blockFirst = first.col; 4532 blockLast = last.col; 4533 } else { 4534 blockFirst = last.col; 4535 blockLast = first.col; 4536 } 4537 for (i = first.row; i <= last.row; i++) { 4538 lp = SaveText(screen, i, blockFirst, blockLast, lp, &eol); 4539 if (i < last.row || eol) 4540 *lp++ = '\n'; 4541 } 4542 } 4543#endif 4544 else { 4545 lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol); 4546 if (eol) 4547 *lp++ = '\n'; /* put in newline at end of line */ 4548 for (i = first.row + 1; i < last.row; i++) { 4549 lp = SaveText(screen, i, 0, screen->max_col, lp, &eol); 4550 if (eol) 4551 *lp++ = '\n'; 4552 } 4553 if (last.col >= 0) 4554 lp = SaveText(screen, last.row, 0, last.col, lp, &eol); 4555 } 4556 *lp = '\0'; /* make sure we have end marked */ 4557 4558 have = (size_t) (lp - line); 4559 /* 4560 * Scanning the buffer twice is unnecessary. Discard unwanted memory if 4561 * the estimate is too-far off. 4562 */ 4563 if ((have * 2) < (size_t) need) { 4564 Char *next; 4565 scp->data_limit = have + 1; 4566 next = realloc(line, scp->data_limit); 4567 if (next == NULL) { 4568 free(line); 4569 scp->data_length = 0; 4570 scp->data_limit = 0; 4571 } 4572 scp->data_buffer = next; 4573 } 4574 scp->data_length = have; 4575 4576 TRACE(("Salted TEXT:%u:%s\n", (unsigned) have, 4577 visibleChars(scp->data_buffer, (unsigned) have))); 4578} 4579 4580#if OPT_PASTE64 4581void 4582ClearSelectionBuffer(TScreen *screen, String selection) 4583{ 4584 int which = TargetToSelection(screen, selection); 4585 SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]); 4586 FreeAndNull(scp->data_buffer); 4587 scp->data_limit = 0; 4588 scp->data_length = 0; 4589 screen->base64_count = 0; 4590} 4591 4592static void 4593AppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len) 4594{ 4595 if (len != 0) { 4596 size_t j = (scp->data_length + len); 4597 size_t k = j + (j >> 2) + 80; 4598 if (j + 1 >= scp->data_limit) { 4599 Char *line; 4600 if (!scp->data_length) { 4601 line = (Char *) malloc(k); 4602 } else { 4603 line = (Char *) realloc(scp->data_buffer, k); 4604 } 4605 if (line == NULL) 4606 SysError(ERROR_BMALLOC2); 4607 scp->data_buffer = line; 4608 scp->data_limit = k; 4609 } 4610 if (scp->data_buffer != NULL) { 4611 memcpy(scp->data_buffer + scp->data_length, text, len); 4612 scp->data_length += len; 4613 scp->data_buffer[scp->data_length] = 0; 4614 } 4615 } 4616} 4617 4618void 4619AppendToSelectionBuffer(TScreen *screen, unsigned c, String selection) 4620{ 4621 int which = TargetToSelection(screen, selection); 4622 SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]); 4623 unsigned six; 4624 Char ch; 4625 4626 /* Decode base64 character */ 4627 if (c >= 'A' && c <= 'Z') 4628 six = c - 'A'; 4629 else if (c >= 'a' && c <= 'z') 4630 six = c - 'a' + 26; 4631 else if (c >= '0' && c <= '9') 4632 six = c - '0' + 52; 4633 else if (c == '+') 4634 six = 62; 4635 else if (c == '/') 4636 six = 63; 4637 else 4638 return; 4639 4640 /* Accumulate bytes */ 4641 switch (screen->base64_count) { 4642 case 0: 4643 screen->base64_accu = six; 4644 screen->base64_count = 6; 4645 break; 4646 4647 case 2: 4648 ch = CharOf((screen->base64_accu << 6) + six); 4649 screen->base64_count = 0; 4650 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1); 4651 break; 4652 4653 case 4: 4654 ch = CharOf((screen->base64_accu << 4) + (six >> 2)); 4655 screen->base64_accu = (six & 0x3); 4656 screen->base64_count = 2; 4657 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1); 4658 break; 4659 4660 case 6: 4661 ch = CharOf((screen->base64_accu << 2) + (six >> 4)); 4662 screen->base64_accu = (six & 0xF); 4663 screen->base64_count = 4; 4664 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1); 4665 break; 4666 } 4667} 4668 4669void 4670CompleteSelection(XtermWidget xw, String *args, Cardinal len) 4671{ 4672 TScreen *screen = TScreenOf(xw); 4673 4674 screen->base64_count = 0; 4675 screen->base64_accu = 0; 4676 _OwnSelection(xw, args, len); 4677} 4678#endif /* OPT_PASTE64 */ 4679 4680static Bool 4681_ConvertSelectionHelper(Widget w, 4682 SelectedCells * scp, 4683 Atom *type, 4684 XtPointer *value, 4685 unsigned long *length, 4686 int *format, 4687 int (*conversion_function) (Display *, 4688 char **, int, 4689 XICCEncodingStyle, 4690 XTextProperty *), 4691 XICCEncodingStyle conversion_style) 4692{ 4693 *value = NULL; 4694 *length = 0; 4695 *type = 0; 4696 *format = 0; 4697 4698 if (getXtermWidget(w) != NULL) { 4699 Display *dpy = XtDisplay(w); 4700 XTextProperty textprop; 4701 int out_n = 0; 4702 char *result = NULL; 4703 char *the_data = (char *) scp->data_buffer; 4704 char *the_next; 4705 unsigned long remaining = scp->data_length; 4706 4707 TRACE(("converting %ld:'%s'\n", 4708 (long) scp->data_length, 4709 visibleChars(scp->data_buffer, (unsigned) scp->data_length))); 4710 /* 4711 * For most selections, we can convert in one pass. It is possible 4712 * that some applications contain embedded nulls, e.g., using xterm's 4713 * paste64 feature. For those cases, we will build up the result in 4714 * parts. 4715 */ 4716 if (memchr(the_data, 0, scp->data_length) != NULL) { 4717 TRACE(("selection contains embedded nulls\n")); 4718 result = calloc(scp->data_length + 1, sizeof(char)); 4719 } 4720 4721 next_try: 4722 memset(&textprop, 0, sizeof(textprop)); 4723 if (conversion_function(dpy, &the_data, 1, 4724 conversion_style, 4725 &textprop) >= Success) { 4726 if ((result != NULL) 4727 && (textprop.value != NULL) 4728 && (textprop.format == 8)) { 4729 char *text_values = (char *) textprop.value; 4730 unsigned long in_n; 4731 4732 if (out_n == 0) { 4733 *value = result; 4734 *type = textprop.encoding; 4735 *format = textprop.format; 4736 } 4737 for (in_n = 0; in_n < textprop.nitems; ++in_n) { 4738 result[out_n++] = text_values[in_n]; 4739 } 4740 *length += textprop.nitems; 4741 if ((the_next = memchr(the_data, 0, remaining)) != NULL) { 4742 unsigned long this_was = (unsigned long) (the_next - the_data); 4743 this_was++; 4744 the_data += this_was; 4745 remaining -= this_was; 4746 result[out_n++] = 0; 4747 *length += 1; 4748 if (remaining) 4749 goto next_try; 4750 } 4751 return True; 4752 } else { 4753 free(result); 4754 *value = (XtPointer) textprop.value; 4755 *length = textprop.nitems; 4756 *type = textprop.encoding; 4757 *format = textprop.format; 4758 return True; 4759 } 4760 } 4761 free(result); 4762 } 4763 return False; 4764} 4765 4766static Boolean 4767SaveConvertedLength(XtPointer *target, unsigned long source) 4768{ 4769 Boolean result = False; 4770 4771 *target = XtMalloc(4); 4772 if (*target != NULL) { 4773 result = True; 4774 if (sizeof(unsigned long) == 4) { 4775 *(unsigned long *) *target = source; 4776 } else if (sizeof(unsigned) == 4) { 4777 *(unsigned *) *target = (unsigned) source; 4778 } else if (sizeof(unsigned short) == 4) { 4779 *(unsigned short *) *target = (unsigned short) source; 4780 } else { 4781 /* FIXME - does this depend on byte-order? */ 4782 unsigned long temp = source; 4783 memcpy((char *) *target, 4784 ((char *) &temp) + sizeof(temp) - 4, 4785 (size_t) 4); 4786 } 4787 } 4788 return result; 4789} 4790 4791#define keepClipboard(d,atom) ((screen->keepClipboard) && \ 4792 (atom == XA_CLIPBOARD(d))) 4793 4794static Boolean 4795ConvertSelection(Widget w, 4796 Atom *selection, 4797 Atom *target, 4798 Atom *type, 4799 XtPointer *value, 4800 unsigned long *length, 4801 int *format) 4802{ 4803 Display *dpy = XtDisplay(w); 4804 TScreen *screen; 4805 SelectedCells *scp; 4806 Bool result = False; 4807 4808 Char *data; 4809 unsigned long data_length; 4810 4811 XtermWidget xw; 4812 4813 if ((xw = getXtermWidget(w)) == NULL) 4814 return False; 4815 4816 screen = TScreenOf(xw); 4817 4818 TRACE(("ConvertSelection %s -> %s\n", 4819 TraceAtomName(screen->display, *selection), 4820 visibleSelectionTarget(dpy, *target))); 4821 4822 if (keepClipboard(dpy, *selection)) { 4823 TRACE(("asked for clipboard\n")); 4824 scp = &(screen->clipboard_data); 4825 } else { 4826 TRACE(("asked for selection\n")); 4827 scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]); 4828 } 4829 4830 data = scp->data_buffer; 4831 data_length = scp->data_length; 4832 if (data == NULL) { 4833 TRACE(("...no selection-data\n")); 4834 return False; 4835 } 4836 4837 if (*target == XA_TARGETS(dpy)) { 4838 Atom *targetP; 4839 XPointer std_return = NULL; 4840 unsigned long std_length; 4841 4842 if (XmuConvertStandardSelection(w, screen->selection_time, selection, 4843 target, type, &std_return, 4844 &std_length, format)) { 4845 Atom *my_targets = _SelectionTargets(w); 4846 Atom *allocP; 4847 Atom *std_targets; 4848 4849 TRACE(("XmuConvertStandardSelection - success\n")); 4850 std_targets = (Atom *) (void *) (std_return); 4851 *length = std_length + 6; 4852 4853 targetP = TypeXtMallocN(Atom, *length); 4854 allocP = targetP; 4855 4856 *value = (XtPointer) targetP; 4857 4858 if (my_targets != NULL) { 4859 while (*my_targets != None) { 4860 *targetP++ = *my_targets++; 4861 } 4862 } 4863 *targetP++ = XA_LENGTH(dpy); 4864 *targetP++ = XA_LIST_LENGTH(dpy); 4865 4866 *length = std_length + (unsigned long) (targetP - allocP); 4867 4868 memcpy(targetP, std_targets, sizeof(Atom) * std_length); 4869 XtFree((char *) std_targets); 4870 *type = XA_ATOM; 4871 *format = 32; 4872 result = True; 4873 } else { 4874 TRACE(("XmuConvertStandardSelection - failed\n")); 4875 } 4876 } 4877#if OPT_WIDE_CHARS 4878 else if (screen->wide_chars && *target == XA_STRING) { 4879 result = 4880 _ConvertSelectionHelper(w, scp, 4881 type, value, length, format, 4882 Xutf8TextListToTextProperty, 4883 XStringStyle); 4884 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 4885 } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) { 4886 result = 4887 _ConvertSelectionHelper(w, scp, 4888 type, value, length, format, 4889 Xutf8TextListToTextProperty, 4890 XUTF8StringStyle); 4891 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 4892 } else if (screen->wide_chars && *target == XA_TEXT(dpy)) { 4893 result = 4894 _ConvertSelectionHelper(w, scp, 4895 type, value, length, format, 4896 Xutf8TextListToTextProperty, 4897 XStdICCTextStyle); 4898 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 4899 } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) { 4900 result = 4901 _ConvertSelectionHelper(w, scp, 4902 type, value, length, format, 4903 Xutf8TextListToTextProperty, 4904 XCompoundTextStyle); 4905 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 4906 } 4907#endif 4908 4909 else if (*target == XA_STRING) { /* not wide_chars */ 4910 /* We can only reach this point if the selection requestor 4911 requested STRING before any of TEXT, COMPOUND_TEXT or 4912 UTF8_STRING. We therefore assume that the requestor is not 4913 properly internationalised, and dump raw eight-bit data 4914 with no conversion into the selection. Yes, this breaks 4915 the ICCCM in non-Latin-1 locales. */ 4916 *type = XA_STRING; 4917 *value = (XtPointer) data; 4918 *length = data_length; 4919 *format = 8; 4920 result = True; 4921 TRACE(("...raw 8-bit data:%d\n", result)); 4922 } else if (*target == XA_TEXT(dpy)) { /* not wide_chars */ 4923 result = 4924 _ConvertSelectionHelper(w, scp, 4925 type, value, length, format, 4926 XmbTextListToTextProperty, 4927 XStdICCTextStyle); 4928 TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result)); 4929 } else if (*target == XA_COMPOUND_TEXT(dpy)) { /* not wide_chars */ 4930 result = 4931 _ConvertSelectionHelper(w, scp, 4932 type, value, length, format, 4933 XmbTextListToTextProperty, 4934 XCompoundTextStyle); 4935 TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result)); 4936 } 4937#ifdef X_HAVE_UTF8_STRING 4938 else if (*target == XA_UTF8_STRING(dpy)) { /* not wide_chars */ 4939 result = 4940 _ConvertSelectionHelper(w, scp, 4941 type, value, length, format, 4942 XmbTextListToTextProperty, 4943 XUTF8StringStyle); 4944 TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result)); 4945 } 4946#endif 4947 else if (*target == XA_LIST_LENGTH(dpy)) { 4948 result = SaveConvertedLength(value, (unsigned long) 1); 4949 *type = XA_INTEGER; 4950 *length = 1; 4951 *format = 32; 4952 TRACE(("...list of values:%d\n", result)); 4953 } else if (*target == XA_LENGTH(dpy)) { 4954 /* This value is wrong if we have UTF-8 text */ 4955 result = SaveConvertedLength(value, scp->data_length); 4956 *type = XA_INTEGER; 4957 *length = 1; 4958 *format = 32; 4959 TRACE(("...list of values:%d\n", result)); 4960 } else if (XmuConvertStandardSelection(w, 4961 screen->selection_time, selection, 4962 target, type, (XPointer *) value, 4963 length, format)) { 4964 result = True; 4965 TRACE(("...XmuConvertStandardSelection:%d\n", result)); 4966 } 4967 4968 /* else */ 4969 return (Boolean) result; 4970} 4971 4972static void 4973LoseSelection(Widget w, Atom *selection) 4974{ 4975 TScreen *screen; 4976 Atom *atomP; 4977 Cardinal i; 4978 4979 XtermWidget xw; 4980 4981 if ((xw = getXtermWidget(w)) == NULL) 4982 return; 4983 4984 screen = TScreenOf(xw); 4985 TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection))); 4986 4987 for (i = 0, atomP = screen->selection_atoms; 4988 i < screen->selection_count; i++, atomP++) { 4989 if (*selection == *atomP) 4990 *atomP = (Atom) 0; 4991 if (CutBuffer(*atomP) >= 0) { 4992 *atomP = (Atom) 0; 4993 } 4994 } 4995 4996 for (i = screen->selection_count; i; i--) { 4997 if (screen->selection_atoms[i - 1] != 0) 4998 break; 4999 } 5000 screen->selection_count = i; 5001 5002 for (i = 0, atomP = screen->selection_atoms; 5003 i < screen->selection_count; i++, atomP++) { 5004 if (*atomP == (Atom) 0) { 5005 *atomP = screen->selection_atoms[--screen->selection_count]; 5006 } 5007 } 5008 5009 if (screen->selection_count == 0) 5010 UnHiliteText(xw); 5011} 5012 5013/* ARGSUSED */ 5014static void 5015SelectionDone(Widget w GCC_UNUSED, 5016 Atom *selection GCC_UNUSED, 5017 Atom *target GCC_UNUSED) 5018{ 5019 /* empty proc so Intrinsics know we want to keep storage */ 5020 TRACE(("SelectionDone\n")); 5021} 5022 5023static void 5024_OwnSelection(XtermWidget xw, 5025 String *selections, 5026 Cardinal count) 5027{ 5028 TScreen *screen = TScreenOf(xw); 5029 Display *dpy = screen->display; 5030 Atom *atoms = screen->selection_atoms; 5031 Cardinal i; 5032 Bool have_selection = False; 5033 SelectedCells *scp; 5034 5035 if (count == 0) 5036 return; 5037 5038 TRACE(("_OwnSelection count %d\n", count)); 5039 selections = MapSelections(xw, selections, count); 5040 5041 if (count > screen->sel_atoms_size) { 5042 XtFree((char *) atoms); 5043 atoms = TypeXtMallocN(Atom, count); 5044 screen->selection_atoms = atoms; 5045 screen->sel_atoms_size = count; 5046 } 5047 XmuInternStrings(dpy, selections, count, atoms); 5048 for (i = 0; i < count; i++) { 5049 int cutbuffer = CutBuffer(atoms[i]); 5050 if (cutbuffer >= 0) { 5051 unsigned long limit = 5052 (unsigned long) (4 * XMaxRequestSize(dpy) - 32); 5053 scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]); 5054 if (scp->data_length > limit) { 5055 TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n", 5056 (unsigned long) scp->data_length, cutbuffer)); 5057 xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n", 5058 (unsigned long) scp->data_length, cutbuffer); 5059 } else { 5060 /* This used to just use the UTF-8 data, which was totally 5061 * broken as not even the corresponding paste code in xterm 5062 * understood this! So now it converts to Latin1 first. 5063 * Robert Brady, 2000-09-05 5064 */ 5065 unsigned long length = scp->data_length; 5066 Char *data = scp->data_buffer; 5067 if_OPT_WIDE_CHARS((screen), { 5068 data = UTF8toLatin1(screen, data, length, &length); 5069 }); 5070 TRACE(("XStoreBuffer(%d)\n", cutbuffer)); 5071 XStoreBuffer(dpy, 5072 (char *) data, 5073 (int) length, 5074 cutbuffer); 5075 } 5076 } else { 5077 int which = AtomToSelection(dpy, atoms[i]); 5078 if (keepClipboard(dpy, atoms[i])) { 5079 Char *buf; 5080 SelectedCells *tcp = &(screen->clipboard_data); 5081 TRACE(("saving selection to clipboard buffer\n")); 5082 scp = &(screen->selected_cells[CLIPBOARD_CODE]); 5083 if ((buf = (Char *) malloc((size_t) scp->data_length)) == NULL) { 5084 SysError(ERROR_BMALLOC2); 5085 } else { 5086 free(tcp->data_buffer); 5087 memcpy(buf, scp->data_buffer, scp->data_length); 5088 tcp->data_buffer = buf; 5089 tcp->data_limit = scp->data_length; 5090 tcp->data_length = scp->data_length; 5091 } 5092 } 5093 scp = &(screen->selected_cells[which]); 5094 if (scp->data_length == 0) { 5095 TRACE(("XtDisownSelection(%s, @%ld)\n", 5096 TraceAtomName(screen->display, atoms[i]), 5097 (long) screen->selection_time)); 5098 XtDisownSelection((Widget) xw, 5099 atoms[i], 5100 screen->selection_time); 5101 } else if (!screen->replyToEmacs && atoms[i] != 0) { 5102 TRACE(("XtOwnSelection(%s, @%ld)\n", 5103 TraceAtomName(screen->display, atoms[i]), 5104 (long) screen->selection_time)); 5105 have_selection |= 5106 XtOwnSelection((Widget) xw, atoms[i], 5107 screen->selection_time, 5108 ConvertSelection, 5109 LoseSelection, 5110 SelectionDone); 5111 } 5112 } 5113 TRACE(("... _OwnSelection used length %lu value %s\n", 5114 (unsigned long) scp->data_length, 5115 visibleChars(scp->data_buffer, 5116 (unsigned) scp->data_length))); 5117 } 5118 if (!screen->replyToEmacs) 5119 screen->selection_count = count; 5120 if (!have_selection) 5121 UnHiliteText(xw); 5122} 5123 5124static void 5125ResetSelectionState(TScreen *screen) 5126{ 5127 screen->selection_count = 0; 5128 screen->startH = zeroCELL; 5129 screen->endH = zeroCELL; 5130} 5131 5132void 5133DisownSelection(XtermWidget xw) 5134{ 5135 TScreen *screen = TScreenOf(xw); 5136 Atom *atoms = screen->selection_atoms; 5137 Cardinal count = screen->selection_count; 5138 Cardinal i; 5139 5140 TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n", 5141 count, 5142 screen->startH.row, 5143 screen->startH.col, 5144 screen->endH.row, 5145 screen->endH.col)); 5146 5147 for (i = 0; i < count; i++) { 5148 int cutbuffer = CutBuffer(atoms[i]); 5149 if (cutbuffer < 0) { 5150 XtDisownSelection((Widget) xw, atoms[i], 5151 screen->selection_time); 5152 } 5153 } 5154 /* 5155 * If none of the callbacks via XtDisownSelection() reset highlighting 5156 * do it now. 5157 */ 5158 if (ScrnHaveSelection(screen)) { 5159 /* save data which will be reset */ 5160 CELL first = screen->startH; 5161 CELL last = screen->endH; 5162 5163 ResetSelectionState(screen); 5164 ReHiliteText(xw, &first, &last); 5165 } else { 5166 ResetSelectionState(screen); 5167 } 5168} 5169 5170void 5171UnhiliteSelection(XtermWidget xw) 5172{ 5173 TScreen *screen = TScreenOf(xw); 5174 5175 if (ScrnHaveSelection(screen)) { 5176 CELL first = screen->startH; 5177 CELL last = screen->endH; 5178 5179 screen->startH = zeroCELL; 5180 screen->endH = zeroCELL; 5181 ReHiliteText(xw, &first, &last); 5182 } 5183} 5184 5185/* returns number of chars in line from scol to ecol out */ 5186/* ARGSUSED */ 5187static int 5188Length(TScreen *screen, 5189 int row, 5190 int scol, 5191 int ecol) 5192{ 5193 CLineData *ld = GET_LINEDATA(screen, row); 5194 const int lastcol = LastTextCol(screen, ld, row); 5195 5196 if (ecol > lastcol) 5197 ecol = lastcol; 5198 return (ecol - scol + 1); 5199} 5200 5201/* copies text into line, preallocated */ 5202static Char * 5203SaveText(TScreen *screen, 5204 int row, 5205 int scol, 5206 int ecol, 5207 Char *lp, /* pointer to where to put the text */ 5208 int *eol) 5209{ 5210 LineData *ld; 5211 int i = 0; 5212 Char *result = lp; 5213#if OPT_WIDE_CHARS 5214 unsigned previous = 0; 5215#endif 5216 5217 ld = GET_LINEDATA(screen, row); 5218 i = Length(screen, row, scol, ecol); 5219 ecol = scol + i; 5220#if OPT_DEC_CHRSET 5221 if (CSET_DOUBLE(GetLineDblCS(ld))) { 5222 scol = (scol + 0) / 2; 5223 ecol = (ecol + 1) / 2; 5224 } 5225#endif 5226 *eol = !LineTstWrapped(ld); 5227 for (i = scol; i < ecol; i++) { 5228 unsigned c; 5229 if (i >= (int) ld->lineSize) { 5230 /* The terminal was probably resized */ 5231 *lp++ = CharOf(' '); 5232 continue; 5233 } 5234 c = ld->charData[i]; 5235 if (ld->attribs[i] & INVISIBLE) 5236 continue; 5237#if OPT_WIDE_CHARS 5238 /* We want to strip out every occurrence of HIDDEN_CHAR AFTER a 5239 * wide character. 5240 */ 5241 if (c == HIDDEN_CHAR) { 5242 if (isWide((int) previous)) { 5243 previous = c; 5244 /* Combining characters attached to double-width characters 5245 are in memory attached to the HIDDEN_CHAR */ 5246 if_OPT_WIDE_CHARS(screen, { 5247 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) { 5248 size_t off; 5249 for_each_combData(off, ld) { 5250 unsigned ch = ld->combData[off][i]; 5251 if (ch == 0) 5252 break; 5253 lp = convertToUTF8(lp, ch); 5254 } 5255 } 5256 }); 5257 continue; 5258 } else { 5259 c = ' '; /* should not happen, but just in case... */ 5260 } 5261 } 5262 previous = c; 5263 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) { 5264 lp = convertToUTF8(lp, (c != 0) ? c : ' '); 5265 if_OPT_WIDE_CHARS(screen, { 5266 size_t off; 5267 for_each_combData(off, ld) { 5268 unsigned ch = ld->combData[off][i]; 5269 if (ch == 0) 5270 break; 5271 lp = convertToUTF8(lp, ch); 5272 } 5273 }); 5274 } else 5275#endif 5276 { 5277 if (c == 0) { 5278 c = ' '; 5279 } else if (c < ' ') { 5280 c = DECtoASCII(c); 5281 } else if (c == 0x7f) { 5282 c = 0x5f; 5283 } 5284 *lp++ = CharOf(c); 5285 } 5286 if (c != ' ') 5287 result = lp; 5288 } 5289 5290 /* 5291 * If requested, trim trailing blanks from selected lines. Do not do this 5292 * if the line is wrapped. 5293 */ 5294 if (!*eol || !screen->trim_selection) 5295 result = lp; 5296 5297 return (result); 5298} 5299 5300/* 5301 * This adds together the bits: 5302 * shift key -> 1 5303 * meta key -> 2 5304 * control key -> 4 5305 */ 5306static unsigned 5307KeyState(XtermWidget xw, unsigned x) 5308{ 5309 return ((((x) & (ShiftMask | ControlMask))) 5310 + (((x) & MetaMask(xw)) ? 2 : 0)); 5311} 5312 5313/* 32 + following 8-bit word: 5314 5315 1:0 Button no: 0, 1, 2. 3=release. 5316 2 shift 5317 3 meta 5318 4 ctrl 5319 5 set for motion notify 5320 6 set for wheel (and button 6 and 7) 5321 7 set for buttons 8 to 11 5322*/ 5323 5324/* Position: 32 - 255. */ 5325static int 5326BtnCode(XtermWidget xw, XButtonEvent *event, int button) 5327{ 5328 int result = (int) (32 + (KeyState(xw, event->state) << 2)); 5329 5330 if (event->type == MotionNotify) 5331 result += 32; 5332 5333 if (button < 0) { 5334 result += 3; 5335 } else { 5336 result += button & 3; 5337 if (button & 4) 5338 result += 64; 5339 if (button & 8) 5340 result += 128; 5341 } 5342 TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n", 5343 button, 5344 visibleEventType(event->type), 5345 ARG_MODIFIER_NAMES(event->state), 5346 result)); 5347 return result; 5348} 5349 5350static unsigned 5351EmitButtonCode(XtermWidget xw, 5352 Char *line, 5353 unsigned count, 5354 XButtonEvent *event, 5355 int button) 5356{ 5357 TScreen *screen = TScreenOf(xw); 5358 int value; 5359 5360 if (okSendMousePos(xw) == X10_MOUSE) { 5361 value = CharOf(' ' + button); 5362 } else { 5363 value = BtnCode(xw, event, button); 5364 } 5365 5366 switch (screen->extend_coords) { 5367 default: 5368 line[count++] = CharOf(value); 5369 break; 5370 case SET_SGR_EXT_MODE_MOUSE: 5371 case SET_PIXEL_POSITION_MOUSE: 5372 value -= 32; /* encoding starts at zero */ 5373 /* FALLTHRU */ 5374 case SET_URXVT_EXT_MODE_MOUSE: 5375 count += (unsigned) sprintf((char *) line + count, "%d", value); 5376 break; 5377 case SET_EXT_MODE_MOUSE: 5378 if (value < 128) { 5379 line[count++] = CharOf(value); 5380 } else { 5381 line[count++] = CharOf(0xC0 + (value >> 6)); 5382 line[count++] = CharOf(0x80 + (value & 0x3F)); 5383 } 5384 break; 5385 } 5386 return count; 5387} 5388 5389static int 5390FirstBitN(int bits) 5391{ 5392 int result = -1; 5393 if (bits > 0) { 5394 result = 0; 5395 while (!(bits & 1)) { 5396 bits /= 2; 5397 ++result; 5398 } 5399 } 5400 return result; 5401} 5402 5403#define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0) 5404 5405#define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button) 5406 5407static void 5408EditorButton(XtermWidget xw, XButtonEvent *event) 5409{ 5410 TScreen *screen = TScreenOf(xw); 5411 int pty = screen->respond; 5412 int mouse_limit = MouseLimit(screen); 5413 Char line[32]; 5414 Char final = 'M'; 5415 int row, col; 5416 int button; 5417 unsigned count = 0; 5418 Boolean changed = True; 5419 5420 /* If button event, get button # adjusted for DEC compatibility */ 5421 button = (int) (event->button - 1); 5422 if (button >= 3) 5423 button++; 5424 5425 /* Ignore buttons that cannot be encoded */ 5426 if (screen->send_mouse_pos == X10_MOUSE) { 5427 if (button > 3) 5428 return; 5429 } else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE 5430 || screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE 5431 || screen->extend_coords == SET_PIXEL_POSITION_MOUSE) { 5432 if (button > 15) { 5433 return; 5434 } 5435 } else { 5436 if (button > 11) { 5437 return; 5438 } 5439 } 5440 5441 if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) { 5442 row = event->y - OriginY(screen); 5443 col = event->x - OriginX(screen); 5444 } else { 5445 /* Compute character position of mouse pointer */ 5446 row = (event->y - screen->border) / FontHeight(screen); 5447 col = (event->x - OriginX(screen)) / FontWidth(screen); 5448 5449 /* Limit to screen dimensions */ 5450 if (row < 0) 5451 row = 0; 5452 else if (row > screen->max_row) 5453 row = screen->max_row; 5454 5455 if (col < 0) 5456 col = 0; 5457 else if (col > screen->max_col) 5458 col = screen->max_col; 5459 5460 if (mouse_limit > 0) { 5461 /* Limit to representable mouse dimensions */ 5462 if (row > mouse_limit) 5463 row = mouse_limit; 5464 if (col > mouse_limit) 5465 col = mouse_limit; 5466 } 5467 } 5468 5469 /* Build key sequence starting with \E[M */ 5470 if (screen->control_eight_bits) { 5471 line[count++] = ANSI_CSI; 5472 } else { 5473 line[count++] = ANSI_ESC; 5474 line[count++] = '['; 5475 } 5476 switch (screen->extend_coords) { 5477 case 0: 5478 case SET_EXT_MODE_MOUSE: 5479#if OPT_SCO_FUNC_KEYS 5480 if (xw->keyboard.type == keyboardIsSCO) { 5481 /* 5482 * SCO function key F1 is \E[M, which would conflict with xterm's 5483 * normal kmous. 5484 */ 5485 line[count++] = '>'; 5486 } 5487#endif 5488 line[count++] = final; 5489 break; 5490 case SET_SGR_EXT_MODE_MOUSE: 5491 case SET_PIXEL_POSITION_MOUSE: 5492 line[count++] = '<'; 5493 break; 5494 } 5495 5496 /* Add event code to key sequence */ 5497 if (okSendMousePos(xw) == X10_MOUSE) { 5498 count = EMIT_BUTTON(button); 5499 } else { 5500 /* Button-Motion events */ 5501 switch (event->type) { 5502 case ButtonPress: 5503 screen->mouse_button |= ButtonBit(button); 5504 count = EMIT_BUTTON(button); 5505 break; 5506 case ButtonRelease: 5507 /* 5508 * The (vertical) wheel mouse interface generates release-events 5509 * for buttons 4 and 5. 5510 * 5511 * The X10/X11 xterm protocol maps the release for buttons 1..3 to 5512 * a -1, which will be later mapped into a "0" (some button was 5513 * released), At this point, buttons 1..3 are encoded 0..2 (the 5514 * code 3 is unused). 5515 * 5516 * The SGR (extended) xterm mouse protocol keeps the button number 5517 * and uses a "m" to indicate button release. 5518 * 5519 * The behavior for mice with more buttons is unclear, and may be 5520 * revised -TD 5521 */ 5522 screen->mouse_button &= ~ButtonBit(button); 5523 if (button < 3 || button > 5) { 5524 switch (screen->extend_coords) { 5525 case SET_SGR_EXT_MODE_MOUSE: 5526 case SET_PIXEL_POSITION_MOUSE: 5527 final = 'm'; 5528 break; 5529 default: 5530 button = -1; 5531 break; 5532 } 5533 } 5534 count = EMIT_BUTTON(button); 5535 break; 5536 case MotionNotify: 5537 /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion 5538 * events only if character cell has changed. 5539 */ 5540 if ((row == screen->mouse_row) 5541 && (col == screen->mouse_col)) { 5542 changed = False; 5543 } else { 5544 count = EMIT_BUTTON(FirstBitN(screen->mouse_button)); 5545 } 5546 break; 5547 default: 5548 changed = False; 5549 break; 5550 } 5551 } 5552 5553 if (changed) { 5554 screen->mouse_row = row; 5555 screen->mouse_col = col; 5556 5557 TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1])); 5558 5559 /* Add pointer position to key sequence */ 5560 count = EmitMousePositionSeparator(screen, line, count); 5561 count = EmitMousePosition(screen, line, count, col); 5562 count = EmitMousePositionSeparator(screen, line, count); 5563 count = EmitMousePosition(screen, line, count, row); 5564 5565 switch (screen->extend_coords) { 5566 case SET_SGR_EXT_MODE_MOUSE: 5567 case SET_URXVT_EXT_MODE_MOUSE: 5568 case SET_PIXEL_POSITION_MOUSE: 5569 line[count++] = final; 5570 break; 5571 } 5572 5573 /* Transmit key sequence to process running under xterm */ 5574 TRACE(("EditorButton -> %s\n", visibleChars(line, count))); 5575 v_write(pty, line, (size_t) count); 5576 } 5577 return; 5578} 5579 5580/* 5581 * Check the current send_mouse_pos against allowed mouse-operations, returning 5582 * none if it is disallowed. 5583 */ 5584XtermMouseModes 5585okSendMousePos(XtermWidget xw) 5586{ 5587 TScreen *screen = TScreenOf(xw); 5588 XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos; 5589 5590 switch ((int) result) { 5591 case MOUSE_OFF: 5592 break; 5593 case X10_MOUSE: 5594 if (!AllowMouseOps(xw, emX10)) 5595 result = MOUSE_OFF; 5596 break; 5597 case VT200_MOUSE: 5598 if (!AllowMouseOps(xw, emVT200Click)) 5599 result = MOUSE_OFF; 5600 break; 5601 case VT200_HIGHLIGHT_MOUSE: 5602 if (!AllowMouseOps(xw, emVT200Hilite)) 5603 result = MOUSE_OFF; 5604 break; 5605 case BTN_EVENT_MOUSE: 5606 if (!AllowMouseOps(xw, emAnyButton)) 5607 result = MOUSE_OFF; 5608 break; 5609 case ANY_EVENT_MOUSE: 5610 if (!AllowMouseOps(xw, emAnyEvent)) 5611 result = MOUSE_OFF; 5612 break; 5613 case DEC_LOCATOR: 5614 if (!AllowMouseOps(xw, emLocator)) 5615 result = MOUSE_OFF; 5616 break; 5617 } 5618 return result; 5619} 5620 5621#if OPT_FOCUS_EVENT 5622/* 5623 * Check the current send_focus_pos against allowed mouse-operations, returning 5624 * none if it is disallowed. 5625 */ 5626static int 5627okSendFocusPos(XtermWidget xw) 5628{ 5629 TScreen *screen = TScreenOf(xw); 5630 int result = screen->send_focus_pos; 5631 5632 if (!AllowMouseOps(xw, emFocusEvent)) { 5633 result = False; 5634 } 5635 return result; 5636} 5637 5638void 5639SendFocusButton(XtermWidget xw, XFocusChangeEvent *event) 5640{ 5641 if (okSendFocusPos(xw)) { 5642 ANSI reply; 5643 5644 memset(&reply, 0, sizeof(reply)); 5645 reply.a_type = ANSI_CSI; 5646 5647#if OPT_SCO_FUNC_KEYS 5648 if (xw->keyboard.type == keyboardIsSCO) { 5649 reply.a_pintro = '>'; 5650 } 5651#endif 5652 reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O'); 5653 unparseseq(xw, &reply); 5654 } 5655 return; 5656} 5657#endif /* OPT_FOCUS_EVENT */ 5658 5659#if OPT_SELECTION_OPS 5660/* 5661 * Get the event-time, needed to process selections. 5662 */ 5663static Time 5664getEventTime(XEvent *event) 5665{ 5666 Time result; 5667 5668 if (IsBtnEvent(event)) { 5669 result = ((XButtonEvent *) event)->time; 5670 } else if (IsKeyEvent(event)) { 5671 result = ((XKeyEvent *) event)->time; 5672 } else { 5673 result = 0; 5674 } 5675 5676 return result; 5677} 5678 5679/* obtain the selection string, passing the endpoints to caller's parameters */ 5680static void 5681doSelectionFormat(XtermWidget xw, 5682 Widget w, 5683 XEvent *event, 5684 String *params, 5685 const Cardinal *num_params, 5686 FormatSelect format_select) 5687{ 5688 TScreen *screen = TScreenOf(xw); 5689 InternalSelect *mydata = &(screen->internal_select); 5690 5691 memset(mydata, 0, sizeof(*mydata)); 5692 mydata->format = x_strdup(params[0]); 5693 mydata->format_select = format_select; 5694 5695 screen->selectToBuffer = True; 5696 beginInternalSelect(xw); 5697 5698 xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL); 5699 5700 if (screen->selectToBuffer) 5701 finishInternalSelect(xw); 5702} 5703 5704/* obtain data from the screen, passing the endpoints to caller's parameters */ 5705static char * 5706getDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish) 5707{ 5708 TScreen *screen = TScreenOf(xw); 5709 5710 CELL save_old_start = screen->startH; 5711 CELL save_old_end = screen->endH; 5712 5713 CELL save_startSel = screen->startSel; 5714 CELL save_startRaw = screen->startRaw; 5715 CELL save_finishSel = screen->endSel; 5716 CELL save_finishRaw = screen->endRaw; 5717 5718 int save_firstValidRow = screen->firstValidRow; 5719 int save_lastValidRow = screen->lastValidRow; 5720 5721 const Cardinal noClick = 0; 5722 int save_numberOfClicks = screen->numberOfClicks; 5723 5724 SelectUnit saveUnits = screen->selectUnit; 5725 SelectUnit saveMap = screen->selectMap[noClick]; 5726#if OPT_SELECT_REGEX 5727 char *saveExpr = screen->selectExpr[noClick]; 5728#endif 5729 SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]); 5730 SelectedCells save_selection = *scp; 5731 5732 char *result = NULL; 5733 5734 TRACE(("getDataFromScreen %s\n", method)); 5735 5736 memset(scp, 0, sizeof(*scp)); 5737 5738 screen->numberOfClicks = 1; 5739 lookupSelectUnit(xw, noClick, method); 5740 screen->selectUnit = screen->selectMap[noClick]; 5741 5742 memset(start, 0, sizeof(*start)); 5743 if (IsBtnEvent(event)) { 5744 XButtonEvent *btn_event = (XButtonEvent *) event; 5745 CELL cell; 5746 screen->firstValidRow = 0; 5747 screen->lastValidRow = screen->max_row; 5748 PointToCELL(screen, btn_event->y, btn_event->x, &cell); 5749 start->row = cell.row; 5750 start->col = cell.col; 5751 finish->row = cell.row; 5752 finish->col = screen->max_col; 5753 } else { 5754 start->row = screen->cur_row; 5755 start->col = screen->cur_col; 5756 finish->row = screen->cur_row; 5757 finish->col = screen->max_col; 5758 } 5759 5760 ComputeSelect(xw, start, finish, False, False); 5761 SaltTextAway(xw, 5762 TargetToSelection(screen, PRIMARY_NAME), 5763 &(screen->startSel), &(screen->endSel)); 5764 5765 if (scp->data_limit && scp->data_buffer) { 5766 TRACE(("...getDataFromScreen selection-data %.*s\n", 5767 (int) scp->data_limit, 5768 scp->data_buffer)); 5769 result = malloc(scp->data_limit + 1); 5770 if (result) { 5771 memcpy(result, scp->data_buffer, scp->data_limit); 5772 result[scp->data_limit] = 0; 5773 } 5774 free(scp->data_buffer); 5775 scp->data_limit = 0; 5776 } 5777 5778 TRACE(("...getDataFromScreen restoring previous selection\n")); 5779 5780 screen->startSel = save_startSel; 5781 screen->startRaw = save_startRaw; 5782 screen->endSel = save_finishSel; 5783 screen->endRaw = save_finishRaw; 5784 5785 screen->firstValidRow = save_firstValidRow; 5786 screen->lastValidRow = save_lastValidRow; 5787 5788 screen->numberOfClicks = save_numberOfClicks; 5789 screen->selectUnit = saveUnits; 5790 screen->selectMap[noClick] = saveMap; 5791#if OPT_SELECT_REGEX 5792 screen->selectExpr[noClick] = saveExpr; 5793#endif 5794 5795 screen->selected_cells[0] = save_selection; 5796 5797 TrackText(xw, &save_old_start, &save_old_end); 5798 5799 TRACE(("...getDataFromScreen done\n")); 5800 return result; 5801} 5802 5803#if OPT_EXEC_SELECTION 5804/* 5805 * Split-up the format before substituting data, to avoid quoting issues. 5806 * The resource mechanism has a limited ability to handle escapes. We take 5807 * the result as if it were an sh-type string and parse it into a regular 5808 * argv array. 5809 */ 5810static char ** 5811tokenizeFormat(String format) 5812{ 5813 char **result = NULL; 5814 5815 format = x_skip_blanks(format); 5816 if (*format != '\0') { 5817 char *blob = x_strdup(format); 5818 int pass; 5819 5820 for (pass = 0; pass < 2; ++pass) { 5821 int used = 0; 5822 int first = 1; 5823 int escaped = 0; 5824 int squoted = 0; 5825 int dquoted = 0; 5826 int n; 5827 int argc = 0; 5828 5829 for (n = 0; format[n] != '\0'; ++n) { 5830 if (escaped) { 5831 blob[used++] = format[n]; 5832 escaped = 0; 5833 } else if (format[n] == '"') { 5834 if (!squoted) { 5835 if (!dquoted) 5836 blob[used++] = format[n]; 5837 dquoted = !dquoted; 5838 } 5839 } else if (format[n] == '\'') { 5840 if (!dquoted) { 5841 if (!squoted) 5842 blob[used++] = format[n]; 5843 squoted = !squoted; 5844 } 5845 } else if (format[n] == '\\') { 5846 blob[used++] = format[n]; 5847 escaped = 1; 5848 } else { 5849 if (first) { 5850 first = 0; 5851 if (pass) { 5852 result[argc] = &blob[n]; 5853 } 5854 ++argc; 5855 } 5856 if (isspace((Char) format[n])) { 5857 first = !isspace((Char) format[n + 1]); 5858 if (squoted || dquoted) { 5859 blob[used++] = format[n]; 5860 } else if (first) { 5861 blob[used++] = '\0'; 5862 } 5863 } else { 5864 blob[used++] = format[n]; 5865 } 5866 } 5867 } 5868 blob[used] = '\0'; 5869 assert(strlen(blob) <= strlen(format)); 5870 if (!pass) { 5871 result = TypeCallocN(char *, argc + 1); 5872 if (result == NULL) { 5873 free(blob); 5874 break; 5875 } 5876 } 5877 } 5878 } 5879#if OPT_TRACE 5880 if (result) { 5881 int n; 5882 TRACE(("tokenizeFormat %s\n", format)); 5883 for (n = 0; result[n]; ++n) { 5884 TRACE(("argv[%d] = %s\n", n, result[n])); 5885 } 5886 } 5887#endif 5888 5889 return result; 5890} 5891#endif /* OPT_EXEC_SELECTION */ 5892 5893static void 5894formatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell) 5895{ 5896 TScreen *screen = TScreenOf(xw); 5897 LineData *ld = GET_LINEDATA(screen, cell->row); 5898 5899 *buffer = '\0'; 5900 if (ld != NULL && cell->col < (int) ld->lineSize) { 5901 IAttr attribs = ld->attribs[cell->col]; 5902 const char *delim = ""; 5903 5904 if (attribs & INVERSE) { 5905 buffer += sprintf(buffer, "7"); 5906 delim = ";"; 5907 } 5908 if (attribs & UNDERLINE) { 5909 buffer += sprintf(buffer, "%s4", delim); 5910 delim = ";"; 5911 } 5912 if (attribs & BOLD) { 5913 buffer += sprintf(buffer, "%s1", delim); 5914 delim = ";"; 5915 } 5916 if (attribs & BLINK) { 5917 buffer += sprintf(buffer, "%s5", delim); 5918 delim = ";"; 5919 } 5920#if OPT_ISO_COLORS 5921 if (attribs & FG_COLOR) { 5922 Pixel fg = extract_fg(xw, ld->color[cell->col], attribs); 5923 if (fg < 8) { 5924 fg += 30; 5925 } else if (fg < 16) { 5926 fg += 90; 5927 } else { 5928 buffer += sprintf(buffer, "%s38;5", delim); 5929 delim = ";"; 5930 } 5931 buffer += sprintf(buffer, "%s%lu", delim, fg); 5932 delim = ";"; 5933 } 5934 if (attribs & BG_COLOR) { 5935 Pixel bg = extract_bg(xw, ld->color[cell->col], attribs); 5936 if (bg < 8) { 5937 bg += 40; 5938 } else if (bg < 16) { 5939 bg += 100; 5940 } else { 5941 buffer += sprintf(buffer, "%s48;5", delim); 5942 delim = ";"; 5943 } 5944 (void) sprintf(buffer, "%s%lu", delim, bg); 5945 } 5946#endif 5947 } 5948} 5949 5950static char * 5951formatStrlen(char *target, char *source, int freeit) 5952{ 5953 if (source != NULL) { 5954 sprintf(target, "%u", (unsigned) strlen(source)); 5955 if (freeit) { 5956 free(source); 5957 } 5958 } else { 5959 strcpy(target, "0"); 5960 } 5961 return target; 5962} 5963 5964/* substitute data into format, reallocating the result */ 5965static char * 5966expandFormat(XtermWidget xw, 5967 const char *format, 5968 char *data, 5969 CELL *start, 5970 CELL *finish) 5971{ 5972 char *result = NULL; 5973 if (!IsEmpty(format)) { 5974 static char empty[1]; 5975 int pass; 5976 int n; 5977 char numbers[80]; 5978 5979 if (data == NULL) 5980 data = empty; 5981 5982 for (pass = 0; pass < 2; ++pass) { 5983 size_t need = 0; 5984 5985 for (n = 0; format[n] != '\0'; ++n) { 5986 5987 if (format[n] == '%') { 5988 char *value = NULL; 5989 5990 switch (format[++n]) { 5991 case '%': 5992 if (pass) { 5993 result[need] = format[n]; 5994 } 5995 ++need; 5996 break; 5997 case 'P': 5998 sprintf(numbers, "%d;%d", 5999 TScreenOf(xw)->topline + start->row + 1, 6000 start->col + 1); 6001 value = numbers; 6002 break; 6003 case 'p': 6004 sprintf(numbers, "%d;%d", 6005 TScreenOf(xw)->topline + finish->row + 1, 6006 finish->col + 1); 6007 value = numbers; 6008 break; 6009 case 'R': 6010 value = formatStrlen(numbers, x_strrtrim(data), 1); 6011 break; 6012 case 'r': 6013 value = x_strrtrim(data); 6014 break; 6015 case 'S': 6016 value = formatStrlen(numbers, data, 0); 6017 break; 6018 case 's': 6019 value = data; 6020 break; 6021 case 'T': 6022 value = formatStrlen(numbers, x_strtrim(data), 1); 6023 break; 6024 case 't': 6025 value = x_strtrim(data); 6026 break; 6027 case 'V': 6028 formatVideoAttrs(xw, numbers, start); 6029 value = numbers; 6030 break; 6031 case 'v': 6032 formatVideoAttrs(xw, numbers, finish); 6033 value = numbers; 6034 break; 6035 default: 6036 if (pass) { 6037 result[need] = format[n]; 6038 } 6039 --n; 6040 ++need; 6041 break; 6042 } 6043 if (value != NULL) { 6044 if (pass) { 6045 strcpy(result + need, value); 6046 } 6047 need += strlen(value); 6048 if (value != numbers && value != data) { 6049 free(value); 6050 } 6051 } 6052 } else { 6053 if (pass) { 6054 result[need] = format[n]; 6055 } 6056 ++need; 6057 } 6058 } 6059 if (pass) { 6060 result[need] = '\0'; 6061 } else { 6062 ++need; 6063 result = malloc(need); 6064 if (result == NULL) { 6065 break; 6066 } 6067 } 6068 } 6069 } 6070 TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result))); 6071 return result; 6072} 6073 6074#if OPT_EXEC_SELECTION 6075/* execute the command after forking. The main process frees its data */ 6076static void 6077executeCommand(pid_t pid, char **argv) 6078{ 6079 (void) pid; 6080 if (argv != NULL && argv[0] != NULL) { 6081 char *child_cwd = ProcGetCWD(pid); 6082 6083 if (fork() == 0) { 6084 if (child_cwd) { 6085 IGNORE_RC(chdir(child_cwd)); /* We don't care if this fails */ 6086 } 6087 execvp(argv[0], argv); 6088 exit(EXIT_FAILURE); 6089 } 6090 free(child_cwd); 6091 } 6092} 6093 6094static void 6095freeArgv(char *blob, char **argv) 6096{ 6097 if (blob) { 6098 free(blob); 6099 if (argv) { 6100 int n; 6101 for (n = 0; argv[n]; ++n) 6102 free(argv[n]); 6103 free(argv); 6104 } 6105 } 6106} 6107 6108static void 6109reallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish) 6110{ 6111 XtermWidget xw; 6112 6113 if ((xw = getXtermWidget(w)) != NULL) { 6114 char **argv; 6115 6116 if ((argv = tokenizeFormat(format)) != NULL) { 6117 char *blob = argv[0]; 6118 int argc; 6119 6120 for (argc = 0; argv[argc] != NULL; ++argc) { 6121 argv[argc] = expandFormat(xw, argv[argc], data, start, finish); 6122 } 6123 executeCommand(TScreenOf(xw)->pid, argv); 6124 freeArgv(blob, argv); 6125 } 6126 } 6127} 6128 6129void 6130HandleExecFormatted(Widget w, 6131 XEvent *event, 6132 String *params, /* selections */ 6133 Cardinal *num_params) 6134{ 6135 XtermWidget xw; 6136 6137 TRACE_EVENT("HandleExecFormatted", event, params, num_params); 6138 if ((xw = getXtermWidget(w)) != NULL && 6139 (*num_params > 1)) { 6140 doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted); 6141 } 6142} 6143 6144void 6145HandleExecSelectable(Widget w, 6146 XEvent *event, 6147 String *params, /* selections */ 6148 Cardinal *num_params) 6149{ 6150 XtermWidget xw; 6151 6152 if ((xw = getXtermWidget(w)) != NULL) { 6153 TRACE_EVENT("HandleExecSelectable", event, params, num_params); 6154 6155 if (*num_params == 2) { 6156 CELL start, finish; 6157 char *data; 6158 char **argv; 6159 6160 data = getDataFromScreen(xw, event, params[1], &start, &finish); 6161 if (data != NULL) { 6162 if ((argv = tokenizeFormat(params[0])) != NULL) { 6163 char *blob = argv[0]; 6164 int argc; 6165 6166 for (argc = 0; argv[argc] != NULL; ++argc) { 6167 argv[argc] = expandFormat(xw, argv[argc], data, 6168 &start, &finish); 6169 } 6170 executeCommand(TScreenOf(xw)->pid, argv); 6171 freeArgv(blob, argv); 6172 } 6173 free(data); 6174 } 6175 } 6176 } 6177} 6178#endif /* OPT_EXEC_SELECTION */ 6179 6180static void 6181reallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish) 6182{ 6183 XtermWidget xw; 6184 6185 if ((xw = getXtermWidget(w)) != NULL) { 6186 char *exps; 6187 6188 if ((exps = expandFormat(xw, format, data, start, finish)) != NULL) { 6189 unparseputs(xw, exps); 6190 unparse_end(xw); 6191 free(exps); 6192 } 6193 } 6194} 6195 6196void 6197HandleInsertFormatted(Widget w, 6198 XEvent *event, 6199 String *params, /* selections */ 6200 Cardinal *num_params) 6201{ 6202 XtermWidget xw; 6203 6204 TRACE_EVENT("HandleInsertFormatted", event, params, num_params); 6205 if ((xw = getXtermWidget(w)) != NULL && 6206 (*num_params > 1)) { 6207 doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted); 6208 } 6209} 6210 6211void 6212HandleInsertSelectable(Widget w, 6213 XEvent *event, 6214 String *params, /* selections */ 6215 Cardinal *num_params) 6216{ 6217 XtermWidget xw; 6218 6219 if ((xw = getXtermWidget(w)) != NULL) { 6220 TRACE_EVENT("HandleInsertSelectable", event, params, num_params); 6221 6222 if (*num_params == 2) { 6223 CELL start, finish; 6224 char *data; 6225 char *temp = x_strdup(params[0]); 6226 6227 data = getDataFromScreen(xw, event, params[1], &start, &finish); 6228 if (data != NULL) { 6229 char *exps = expandFormat(xw, temp, data, &start, &finish); 6230 if (exps != NULL) { 6231 unparseputs(xw, exps); 6232 unparse_end(xw); 6233 free(exps); 6234 } 6235 free(data); 6236 } 6237 free(temp); 6238 } 6239 } 6240} 6241#endif /* OPT_SELECTION_OPS */ 6242