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