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