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