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