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