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