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