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