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