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