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