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