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