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