button.c revision 492d43a5
1/* $XTermId: button.c,v 1.390 2010/10/22 00:53:57 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 * Like "select-end" (optionally copies the selection to the given targets), 1121 * but also sets the modes so that releasing the mouse button or moving the 1122 * mouse does not alter the selection. 1123 */ 1124static void 1125do_select_stop(XtermWidget xw, 1126 XEvent * event, /* must be XButtonEvent */ 1127 String * params, /* selections */ 1128 Cardinal *num_params, 1129 Bool use_cursor_loc) 1130{ 1131 TScreen *screen = TScreenOf(xw); 1132 1133 screen->selection_time = event->xbutton.time; 1134 switch (screen->eventMode) { 1135 case NORMAL: 1136 (void) SendMousePosition(xw, event); 1137 break; 1138 case LEFTEXTENSION: 1139 case RIGHTEXTENSION: 1140 EndExtend(xw, event, params, *num_params, use_cursor_loc); 1141 break; 1142 } 1143} 1144 1145void 1146HandleSelectStop(Widget w, 1147 XEvent * event, /* must be XButtonEvent */ 1148 String * params, /* selections */ 1149 Cardinal *num_params) 1150{ 1151 XtermWidget xw; 1152 1153 if ((xw = getXtermWidget(w)) != 0) { 1154 do_select_stop(xw, event, params, num_params, False); 1155 } 1156} 1157 1158void 1159HandleKeyboardSelectStop(Widget w, 1160 XEvent * event, /* must be XButtonEvent */ 1161 String * params, /* selections */ 1162 Cardinal *num_params) 1163{ 1164 XtermWidget xw; 1165 1166 if ((xw = getXtermWidget(w)) != 0) { 1167 do_select_stop(xw, event, params, num_params, True); 1168 } 1169} 1170 1171struct _SelectionList { 1172 String *params; 1173 Cardinal count; 1174 Atom *targets; 1175 Time time; 1176}; 1177 1178static unsigned 1179DECtoASCII(unsigned ch) 1180{ 1181 if (xtermIsDecGraphic(ch)) { 1182 ch = CharOf("###########+++++##-##++++|######"[ch]); 1183 /* 01234567890123456789012345678901 */ 1184 } 1185 return ch; 1186} 1187 1188#if OPT_WIDE_CHARS 1189static Cardinal 1190addXtermChar(Char ** buffer, Cardinal *used, Cardinal offset, unsigned value) 1191{ 1192 if (offset + 1 >= *used) { 1193 *used = 1 + (2 * (offset + 1)); 1194 allocXtermChars(buffer, *used); 1195 } 1196 (*buffer)[offset++] = (Char) value; 1197 return offset; 1198} 1199#define AddChar(buffer, used, offset, value) \ 1200 offset = addXtermChar(buffer, used, offset, (unsigned) value) 1201 1202/* 1203 * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#', 1204 * or ASCII/Latin-1 equivalents for special cases. 1205 */ 1206static Char * 1207UTF8toLatin1(TScreen * screen, Char * s, unsigned long len, unsigned long *result) 1208{ 1209 static Char *buffer; 1210 static Cardinal used; 1211 1212 Cardinal offset = 0; 1213 1214 const Char *p; 1215 1216 if (len != 0) { 1217 PtyData data; 1218 1219 fakePtyData(&data, s, s + len); 1220 while (decodeUtf8(&data)) { 1221 Bool fails = False; 1222 Bool extra = False; 1223 IChar value = skipPtyData(&data); 1224 if (value == UCS_REPL) { 1225 fails = True; 1226 } else if (value < 256) { 1227 AddChar(&buffer, &used, offset, CharOf(value)); 1228 } else { 1229 unsigned eqv = ucs2dec(value); 1230 if (xtermIsDecGraphic(eqv)) { 1231 AddChar(&buffer, &used, offset, DECtoASCII(eqv)); 1232 } else { 1233 eqv = AsciiEquivs(value); 1234 if (eqv == value) { 1235 fails = True; 1236 } else { 1237 AddChar(&buffer, &used, offset, eqv); 1238 } 1239 if (isWide((wchar_t) value)) 1240 extra = True; 1241 } 1242 } 1243 1244 /* 1245 * If we're not able to plug in a single-byte result, insert the 1246 * defaultString (which normally is a single "#", but could be 1247 * whatever the user wants). 1248 */ 1249 if (fails) { 1250 for (p = (const Char *) screen->default_string; *p != '\0'; ++p) { 1251 AddChar(&buffer, &used, offset, *p); 1252 } 1253 } 1254 if (extra) 1255 AddChar(&buffer, &used, offset, ' '); 1256 } 1257 AddChar(&buffer, &used, offset, '\0'); 1258 *result = (unsigned long) (offset - 1); 1259 } else { 1260 *result = 0; 1261 } 1262 return buffer; 1263} 1264 1265int 1266xtermUtf8ToTextList(XtermWidget xw, 1267 XTextProperty * text_prop, 1268 char ***text_list, 1269 int *text_list_count) 1270{ 1271 TScreen *screen = TScreenOf(xw); 1272 Display *dpy = screen->display; 1273 int rc = -1; 1274 1275 if (text_prop->format == 8 1276 && (rc = Xutf8TextPropertyToTextList(dpy, text_prop, 1277 text_list, 1278 text_list_count)) >= 0) { 1279 if (*text_list != NULL && *text_list_count != 0) { 1280 int i; 1281 Char *data; 1282 char **new_text_list, *tmp; 1283 unsigned long size, new_size; 1284 1285 TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count)); 1286 1287 /* 1288 * XLib StringList actually uses only two pointers, one for the 1289 * list itself, and one for the data. Pointer to the data is the 1290 * first element of the list, the rest (if any) list elements point 1291 * to the same memory block as the first element 1292 */ 1293 new_size = 0; 1294 for (i = 0; i < *text_list_count; ++i) { 1295 data = (Char *) (*text_list)[i]; 1296 size = strlen((*text_list)[i]) + 1; 1297 (void) UTF8toLatin1(screen, data, size, &size); 1298 new_size += size + 1; 1299 } 1300 new_text_list = 1301 (char **) XtMalloc((Cardinal) sizeof(char *) * (unsigned) *text_list_count); 1302 new_text_list[0] = tmp = XtMalloc((Cardinal) new_size); 1303 for (i = 0; i < (*text_list_count); ++i) { 1304 data = (Char *) (*text_list)[i]; 1305 size = strlen((*text_list)[i]) + 1; 1306 data = UTF8toLatin1(screen, data, size, &size); 1307 memcpy(tmp, data, size + 1); 1308 new_text_list[i] = tmp; 1309 tmp += size + 1; 1310 } 1311 XFreeStringList((*text_list)); 1312 *text_list = new_text_list; 1313 } else { 1314 rc = -1; 1315 } 1316 } 1317 return rc; 1318} 1319#endif /* OPT_WIDE_CHARS */ 1320 1321static char * 1322parseItem(char *value, char *nextc) 1323{ 1324 char *nextp = value; 1325 while (*nextp != '\0' && *nextp != ',') { 1326 *nextp = x_toupper(*nextp); 1327 ++nextp; 1328 } 1329 *nextc = *nextp; 1330 *nextp = '\0'; 1331 x_strtrim(value); 1332 1333 return nextp; 1334} 1335 1336/* 1337 * All of the wanted strings are unique in the first character, so we can 1338 * use simple abbreviations. 1339 */ 1340static Bool 1341sameItem(const char *actual, const char *wanted) 1342{ 1343 Bool result = False; 1344 size_t have = strlen(actual); 1345 size_t need = strlen(wanted); 1346 1347 if (have != 0 && have <= need) { 1348 if (!strncmp(actual, wanted, have)) { 1349 TRACE(("...matched \"%s\"\n", wanted)); 1350 result = True; 1351 } 1352 } 1353 1354 return result; 1355} 1356 1357/* 1358 * Handle the eightBitSelectTypes or utf8SelectTypes resource values. 1359 */ 1360static Bool 1361overrideTargets(Widget w, String value, Atom ** resultp) 1362{ 1363 Bool override = False; 1364 XtermWidget xw; 1365 1366 if ((xw = getXtermWidget(w)) != 0) { 1367 TScreen *screen = TScreenOf(xw); 1368 1369 if (!IsEmpty(value)) { 1370 char *copied = x_strdup(value); 1371 if (copied != 0) { 1372 Atom *result = 0; 1373 Cardinal count = 1; 1374 int n; 1375 1376 TRACE(("decoding SelectTypes \"%s\"\n", value)); 1377 for (n = 0; copied[n] != '\0'; ++n) { 1378 if (copied[n] == ',') 1379 ++count; 1380 } 1381 result = (Atom *) XtMalloc(((2 * count) + 1) 1382 * (Cardinal) sizeof(Atom)); 1383 if (result == NULL) { 1384 TRACE(("Couldn't allocate selection types\n")); 1385 } else { 1386 char nextc = '?'; 1387 char *listp = (char *) copied; 1388 count = 0; 1389 do { 1390 char *nextp = parseItem(listp, &nextc); 1391 size_t len = strlen(listp); 1392 1393 if (len == 0) { 1394 ; 1395 } 1396#if OPT_WIDE_CHARS 1397 else if (sameItem(listp, "UTF8")) { 1398 result[count++] = XA_UTF8_STRING(XtDisplay(w)); 1399 } 1400#endif 1401 else if (sameItem(listp, "I18N")) { 1402 if (screen->i18nSelections) { 1403 result[count++] = XA_TEXT(XtDisplay(w)); 1404 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1405 } 1406 } else if (sameItem(listp, "TEXT")) { 1407 result[count++] = XA_TEXT(XtDisplay(w)); 1408 } else if (sameItem(listp, "COMPOUND_TEXT")) { 1409 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1410 } else if (sameItem(listp, "STRING")) { 1411 result[count++] = XA_STRING; 1412 } 1413 *nextp++ = nextc; 1414 listp = nextp; 1415 } while (nextc != '\0'); 1416 if (count) { 1417 result[count] = None; 1418 override = True; 1419 *resultp = result; 1420 } else { 1421 XtFree((char *) result); 1422 } 1423 } 1424 } else { 1425 TRACE(("Couldn't allocate copy of selection types\n")); 1426 } 1427 } 1428 } 1429 return override; 1430} 1431 1432#if OPT_WIDE_CHARS 1433static Atom * 1434allocUtf8Targets(Widget w, TScreen * screen) 1435{ 1436 Atom **resultp = &(screen->selection_targets_utf8); 1437 1438 if (*resultp == 0) { 1439 Atom *result; 1440 1441 if (!overrideTargets(w, screen->utf8_select_types, &result)) { 1442 result = (Atom *) XtMalloc((Cardinal) (5 * sizeof(Atom))); 1443 if (result == NULL) { 1444 TRACE(("Couldn't allocate utf-8 selection targets\n")); 1445 } else { 1446 int n = 0; 1447 1448 result[n++] = XA_UTF8_STRING(XtDisplay(w)); 1449#ifdef X_HAVE_UTF8_STRING 1450 if (screen->i18nSelections) { 1451 result[n++] = XA_TEXT(XtDisplay(w)); 1452 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1453 } 1454#endif 1455 result[n++] = XA_STRING; 1456 result[n] = None; 1457 } 1458 } 1459 1460 *resultp = result; 1461 } 1462 1463 return *resultp; 1464} 1465#endif 1466 1467static Atom * 1468alloc8bitTargets(Widget w, TScreen * screen) 1469{ 1470 Atom **resultp = &(screen->selection_targets_8bit); 1471 1472 if (*resultp == 0) { 1473 Atom *result = 0; 1474 1475 if (!overrideTargets(w, screen->eightbit_select_types, &result)) { 1476 result = (Atom *) XtMalloc((Cardinal) (5 * sizeof(Atom))); 1477 if (result == NULL) { 1478 TRACE(("Couldn't allocate 8bit selection targets\n")); 1479 } else { 1480 int n = 0; 1481 1482#ifdef X_HAVE_UTF8_STRING 1483 result[n++] = XA_UTF8_STRING(XtDisplay(w)); 1484#endif 1485 if (screen->i18nSelections) { 1486 result[n++] = XA_TEXT(XtDisplay(w)); 1487 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w)); 1488 } 1489 result[n++] = XA_STRING; 1490 result[n] = None; 1491 } 1492 } 1493 1494 *resultp = result; 1495 } 1496 1497 return *resultp; 1498} 1499 1500static Atom * 1501_SelectionTargets(Widget w) 1502{ 1503 Atom *result; 1504 TScreen *screen; 1505 XtermWidget xw; 1506 1507 if ((xw = getXtermWidget(w)) == 0) { 1508 result = NULL; 1509 } else { 1510 screen = TScreenOf(xw); 1511 1512#if OPT_WIDE_CHARS 1513 if (screen->wide_chars) { 1514 result = allocUtf8Targets(w, screen); 1515 } else 1516#endif 1517 { 1518 /* not screen->wide_chars */ 1519 result = alloc8bitTargets(w, screen); 1520 } 1521 } 1522 1523 return result; 1524} 1525 1526#define isSELECT(value) (!strcmp(value, "SELECT")) 1527 1528static void 1529UnmapSelections(XtermWidget xw) 1530{ 1531 TScreen *screen = TScreenOf(xw); 1532 Cardinal n; 1533 1534 if (screen->mappedSelect) { 1535 for (n = 0; screen->mappedSelect[n] != 0; ++n) 1536 free((void *) screen->mappedSelect[n]); 1537 free(screen->mappedSelect); 1538 screen->mappedSelect = 0; 1539 } 1540} 1541 1542/* 1543 * xterm generally uses the primary selection. Some applications prefer 1544 * (or are limited to) the clipboard. Since the translations resource is 1545 * complicated, users seldom change the way it affects selection. But it 1546 * is simple to remap the choice between primary and clipboard before the 1547 * call to XmuInternStrings(). 1548 */ 1549static String * 1550MapSelections(XtermWidget xw, String * params, Cardinal num_params) 1551{ 1552 String *result = params; 1553 1554 if (num_params > 0) { 1555 Cardinal j; 1556 Boolean map = False; 1557 1558 for (j = 0; j < num_params; ++j) { 1559 TRACE(("param[%d]:%s\n", j, params[j])); 1560 if (isSELECT(params[j])) { 1561 map = True; 1562 break; 1563 } 1564 } 1565 if (map) { 1566 TScreen *screen = TScreenOf(xw); 1567 const char *mapTo = (screen->selectToClipboard 1568 ? "CLIPBOARD" 1569 : "PRIMARY"); 1570 1571 UnmapSelections(xw); 1572 if ((result = TypeMallocN(String, num_params + 1)) != 0) { 1573 result[num_params] = 0; 1574 for (j = 0; j < num_params; ++j) { 1575 result[j] = x_strdup((isSELECT(params[j]) 1576 ? mapTo 1577 : params[j])); 1578 if (result[j] == 0) { 1579 UnmapSelections(xw); 1580 result = 0; 1581 break; 1582 } 1583 } 1584 screen->mappedSelect = result; 1585 } 1586 } 1587 } 1588 return result; 1589} 1590 1591/* 1592 * Lookup the cut-buffer number, which will be in the range 0-7. 1593 * If it is not a cut-buffer, it is the primary selection (-1). 1594 */ 1595static int 1596CutBuffer(Atom code) 1597{ 1598 int cutbuffer; 1599 switch ((unsigned) code) { 1600 case XA_CUT_BUFFER0: 1601 cutbuffer = 0; 1602 break; 1603 case XA_CUT_BUFFER1: 1604 cutbuffer = 1; 1605 break; 1606 case XA_CUT_BUFFER2: 1607 cutbuffer = 2; 1608 break; 1609 case XA_CUT_BUFFER3: 1610 cutbuffer = 3; 1611 break; 1612 case XA_CUT_BUFFER4: 1613 cutbuffer = 4; 1614 break; 1615 case XA_CUT_BUFFER5: 1616 cutbuffer = 5; 1617 break; 1618 case XA_CUT_BUFFER6: 1619 cutbuffer = 6; 1620 break; 1621 case XA_CUT_BUFFER7: 1622 cutbuffer = 7; 1623 break; 1624 default: 1625 cutbuffer = -1; 1626 break; 1627 } 1628 return cutbuffer; 1629} 1630 1631#if OPT_PASTE64 1632static void 1633FinishPaste64(XtermWidget xw) 1634{ 1635 TScreen *screen = TScreenOf(xw); 1636 1637 TRACE(("FinishPaste64(%d)\n", screen->base64_paste)); 1638 if (screen->base64_paste) { 1639 screen->base64_paste = 0; 1640 unparseputc1(xw, screen->base64_final); 1641 unparse_end(xw); 1642 } 1643} 1644#endif 1645 1646#if !OPT_PASTE64 1647static 1648#endif 1649void 1650xtermGetSelection(Widget w, 1651 Time ev_time, 1652 String * params, /* selections in precedence order */ 1653 Cardinal num_params, 1654 Atom * targets) 1655{ 1656 Atom selection; 1657 int cutbuffer; 1658 Atom target; 1659 1660 XtermWidget xw; 1661 1662 if (num_params == 0) 1663 return; 1664 if ((xw = getXtermWidget(w)) == 0) 1665 return; 1666 1667 TRACE(("xtermGetSelection num_params %d\n", num_params)); 1668 params = MapSelections(xw, params, num_params); 1669 1670 XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection); 1671 cutbuffer = CutBuffer(selection); 1672 1673 TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer, 1674 (targets 1675 ? visibleSelectionTarget(XtDisplay(w), targets[0]) 1676 : "None"))); 1677 1678 if (cutbuffer >= 0) { 1679 int inbytes; 1680 unsigned long nbytes; 1681 int fmt8 = 8; 1682 Atom type = XA_STRING; 1683 char *line; 1684 1685 /* 'line' is freed in SelectionReceived */ 1686 line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer); 1687 nbytes = (unsigned long) inbytes; 1688 1689 if (nbytes > 0) 1690 SelectionReceived(w, NULL, &selection, &type, (XtPointer) line, 1691 &nbytes, &fmt8); 1692 else if (num_params > 1) { 1693 xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL); 1694 } 1695#if OPT_PASTE64 1696 else { 1697 FinishPaste64(xw); 1698 } 1699#endif 1700 return; 1701 } else { 1702 struct _SelectionList *list; 1703 1704 if (targets == NULL || targets[0] == None) { 1705 targets = _SelectionTargets(w); 1706 } 1707 1708 if (targets != 0) { 1709 target = targets[0]; 1710 1711 if (targets[1] == None) { /* last target in list */ 1712 params++; 1713 num_params--; 1714 targets = _SelectionTargets(w); 1715 } else { 1716 targets = &(targets[1]); 1717 } 1718 1719 if (num_params) { 1720 /* 'list' is freed in SelectionReceived */ 1721 list = XtNew(struct _SelectionList); 1722 if (list != 0) { 1723 list->params = params; 1724 list->count = num_params; 1725 list->targets = targets; 1726 list->time = ev_time; 1727 } 1728 } else { 1729 list = NULL; 1730 } 1731 1732 XtGetSelectionValue(w, selection, 1733 target, 1734 SelectionReceived, 1735 (XtPointer) list, ev_time); 1736 } 1737 } 1738} 1739 1740#if OPT_TRACE && OPT_WIDE_CHARS 1741static void 1742GettingSelection(Display * dpy, Atom type, Char * line, unsigned long len) 1743{ 1744 Char *cp; 1745 char *name; 1746 1747 name = XGetAtomName(dpy, type); 1748 1749 TRACE(("Getting %s (%ld)\n", name, (long int) type)); 1750 for (cp = line; cp < line + len; cp++) { 1751 TRACE(("[%d:%lu]", (int) (cp + 1 - line), len)); 1752 if (isprint(*cp)) { 1753 TRACE(("%c\n", *cp)); 1754 } else { 1755 TRACE(("\\x%02x\n", *cp)); 1756 } 1757 } 1758} 1759#else 1760#define GettingSelection(dpy,type,line,len) /* nothing */ 1761#endif 1762 1763#ifdef VMS 1764# define tty_vwrite(pty,lag,l) tt_write(lag,l) 1765#else /* !( VMS ) */ 1766# define tty_vwrite(pty,lag,l) v_write(pty,lag,l) 1767#endif /* defined VMS */ 1768 1769#if OPT_PASTE64 1770/* Return base64 code character given 6-bit number */ 1771static const char base64_code[] = "\ 1772ABCDEFGHIJKLMNOPQRSTUVWXYZ\ 1773abcdefghijklmnopqrstuvwxyz\ 17740123456789+/"; 1775static void 1776base64_flush(TScreen * screen) 1777{ 1778 Char x; 1779 switch (screen->base64_count) { 1780 case 0: 1781 break; 1782 case 2: 1783 x = CharOf(base64_code[screen->base64_accu << 4]); 1784 tty_vwrite(screen->respond, &x, 1); 1785 break; 1786 case 4: 1787 x = CharOf(base64_code[screen->base64_accu << 2]); 1788 tty_vwrite(screen->respond, &x, 1); 1789 break; 1790 } 1791 if (screen->base64_pad & 3) 1792 tty_vwrite(screen->respond, 1793 (const Char *) "===", 1794 (unsigned) (4 - (screen->base64_pad & 3))); 1795 screen->base64_count = 0; 1796 screen->base64_accu = 0; 1797 screen->base64_pad = 0; 1798} 1799#endif /* OPT_PASTE64 */ 1800 1801static void 1802_qWriteSelectionData(TScreen * screen, Char * lag, unsigned length) 1803{ 1804#if OPT_PASTE64 1805 if (screen->base64_paste) { 1806 /* Send data as base64 */ 1807 Char *p = lag; 1808 Char buf[64]; 1809 unsigned x = 0; 1810 while (length--) { 1811 switch (screen->base64_count) { 1812 case 0: 1813 buf[x++] = CharOf(base64_code[*p >> 2]); 1814 screen->base64_accu = (unsigned) (*p & 0x3); 1815 screen->base64_count = 2; 1816 ++p; 1817 break; 1818 case 2: 1819 buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) + 1820 (*p >> 4)]); 1821 screen->base64_accu = (unsigned) (*p & 0xF); 1822 screen->base64_count = 4; 1823 ++p; 1824 break; 1825 case 4: 1826 buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) + 1827 (*p >> 6)]); 1828 buf[x++] = CharOf(base64_code[*p & 0x3F]); 1829 screen->base64_accu = 0; 1830 screen->base64_count = 0; 1831 ++p; 1832 break; 1833 } 1834 if (x >= 63) { 1835 /* Write 63 or 64 characters */ 1836 screen->base64_pad += x; 1837 tty_vwrite(screen->respond, buf, x); 1838 x = 0; 1839 } 1840 } 1841 if (x != 0) { 1842 screen->base64_pad += x; 1843 tty_vwrite(screen->respond, buf, x); 1844 } 1845 } else 1846#endif /* OPT_PASTE64 */ 1847#if OPT_READLINE 1848 if (SCREEN_FLAG(screen, paste_quotes)) { 1849 while (length--) { 1850 tty_vwrite(screen->respond, (const Char *) "\026", 1); /* Control-V */ 1851 tty_vwrite(screen->respond, lag++, 1); 1852 } 1853 } else 1854#endif 1855 tty_vwrite(screen->respond, lag, length); 1856} 1857 1858static void 1859_WriteSelectionData(TScreen * screen, Char * line, size_t length) 1860{ 1861 /* Write data to pty a line at a time. */ 1862 /* Doing this one line at a time may no longer be necessary 1863 because v_write has been re-written. */ 1864 1865 Char *lag, *end; 1866 1867 /* in the VMS version, if tt_pasting isn't set to True then qio 1868 reads aren't blocked and an infinite loop is entered, where the 1869 pasted text shows up as new input, goes in again, shows up 1870 again, ad nauseum. */ 1871#ifdef VMS 1872 tt_pasting = True; 1873#endif 1874 1875 end = &line[length]; 1876 lag = line; 1877 1878#if OPT_PASTE64 1879 if (screen->base64_paste) { 1880 _qWriteSelectionData(screen, lag, (unsigned) (end - lag)); 1881 base64_flush(screen); 1882 } else 1883#endif 1884 { 1885 if (!SCREEN_FLAG(screen, paste_literal_nl)) { 1886 Char *cp; 1887 for (cp = line; cp != end; cp++) { 1888 if (*cp == '\n') { 1889 *cp = '\r'; 1890 _qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1)); 1891 lag = cp + 1; 1892 } 1893 } 1894 } 1895 1896 if (lag != end) { 1897 _qWriteSelectionData(screen, lag, (unsigned) (end - lag)); 1898 } 1899 } 1900#ifdef VMS 1901 tt_pasting = False; 1902 tt_start_read(); /* reenable reads or a character may be lost */ 1903#endif 1904} 1905 1906#if OPT_READLINE 1907static void 1908_WriteKey(TScreen * screen, const Char * in) 1909{ 1910 Char line[16]; 1911 unsigned count = 0; 1912 size_t length = strlen((const char *) in); 1913 1914 if (screen->control_eight_bits) { 1915 line[count++] = ANSI_CSI; 1916 } else { 1917 line[count++] = ANSI_ESC; 1918 line[count++] = '['; 1919 } 1920 while (length--) 1921 line[count++] = *in++; 1922 line[count++] = '~'; 1923 tty_vwrite(screen->respond, line, count); 1924} 1925#endif /* OPT_READLINE */ 1926 1927/* SelectionReceived: stuff received selection text into pty */ 1928 1929/* ARGSUSED */ 1930static void 1931SelectionReceived(Widget w, 1932 XtPointer client_data, 1933 Atom * selection GCC_UNUSED, 1934 Atom * type, 1935 XtPointer value, 1936 unsigned long *length, 1937 int *format) 1938{ 1939 char **text_list = NULL; 1940 int text_list_count; 1941 XTextProperty text_prop; 1942 TScreen *screen; 1943 Display *dpy; 1944#if OPT_TRACE && OPT_WIDE_CHARS 1945 Char *line = (Char *) value; 1946#endif 1947 1948 XtermWidget xw; 1949 1950 if ((xw = getXtermWidget(w)) == 0) 1951 return; 1952 1953 screen = TScreenOf(xw); 1954 dpy = XtDisplay(w); 1955 1956 if (*type == 0 /*XT_CONVERT_FAIL */ 1957 || *length == 0 1958 || value == NULL) 1959 goto fail; 1960 1961 text_prop.value = (unsigned char *) value; 1962 text_prop.encoding = *type; 1963 text_prop.format = *format; 1964 text_prop.nitems = *length; 1965 1966 TRACE(("SelectionReceived %s format %d, nitems %ld\n", 1967 visibleSelectionTarget(dpy, text_prop.encoding), 1968 text_prop.format, 1969 text_prop.nitems)); 1970 1971#if OPT_WIDE_CHARS 1972 if (screen->wide_chars) { 1973 if (*type == XA_UTF8_STRING(dpy) || 1974 *type == XA_STRING || 1975 *type == XA_COMPOUND_TEXT(dpy)) { 1976 GettingSelection(dpy, *type, line, *length); 1977 if (Xutf8TextPropertyToTextList(dpy, &text_prop, 1978 &text_list, 1979 &text_list_count) < 0) { 1980 TRACE(("Conversion failed\n")); 1981 text_list = NULL; 1982 } 1983 } 1984 } else 1985#endif /* OPT_WIDE_CHARS */ 1986 { 1987 /* Convert the selection to locale's multibyte encoding. */ 1988 1989 if (*type == XA_UTF8_STRING(dpy) || 1990 *type == XA_STRING || 1991 *type == XA_COMPOUND_TEXT(dpy)) { 1992 Status rc; 1993 1994 GettingSelection(dpy, *type, line, *length); 1995 1996#if OPT_WIDE_CHARS 1997 if (*type == XA_UTF8_STRING(dpy) && 1998 !(screen->wide_chars || screen->c1_printable)) { 1999 rc = xtermUtf8ToTextList(xw, &text_prop, 2000 &text_list, &text_list_count); 2001 } else 2002#endif 2003 if (*type == XA_STRING && screen->brokenSelections) { 2004 rc = XTextPropertyToStringList(&text_prop, 2005 &text_list, &text_list_count); 2006 } else { 2007 rc = XmbTextPropertyToTextList(dpy, &text_prop, 2008 &text_list, 2009 &text_list_count); 2010 } 2011 if (rc < 0) { 2012 TRACE(("Conversion failed\n")); 2013 text_list = NULL; 2014 } 2015 } 2016 } 2017 2018 if (text_list != NULL && text_list_count != 0) { 2019 int i; 2020 2021#if OPT_PASTE64 2022 if (screen->base64_paste) { 2023 ; 2024 } else 2025#endif 2026#if OPT_READLINE 2027 if (SCREEN_FLAG(screen, paste_brackets)) { 2028 _WriteKey(screen, (const Char *) "200"); 2029 } 2030#endif 2031 for (i = 0; i < text_list_count; i++) { 2032 size_t len = strlen(text_list[i]); 2033 _WriteSelectionData(screen, (Char *) text_list[i], len); 2034 } 2035#if OPT_PASTE64 2036 if (screen->base64_paste) { 2037 FinishPaste64(xw); 2038 } else 2039#endif 2040#if OPT_READLINE 2041 if (SCREEN_FLAG(screen, paste_brackets)) { 2042 _WriteKey(screen, (const Char *) "201"); 2043 } 2044#endif 2045 XFreeStringList(text_list); 2046 } else 2047 goto fail; 2048 2049 XtFree((char *) client_data); 2050 XtFree((char *) value); 2051 2052 return; 2053 2054 fail: 2055 if (client_data != 0) { 2056 struct _SelectionList *list = (struct _SelectionList *) client_data; 2057 2058 TRACE(("SelectionReceived ->xtermGetSelection\n")); 2059 xtermGetSelection(w, list->time, 2060 list->params, list->count, list->targets); 2061 XtFree((char *) client_data); 2062#if OPT_PASTE64 2063 } else { 2064 FinishPaste64(xw); 2065#endif 2066 } 2067 return; 2068} 2069 2070void 2071HandleInsertSelection(Widget w, 2072 XEvent * event, /* assumed to be XButtonEvent* */ 2073 String * params, /* selections in precedence order */ 2074 Cardinal *num_params) 2075{ 2076 XtermWidget xw; 2077 2078 if ((xw = getXtermWidget(w)) != 0) { 2079 if (!SendMousePosition(xw, event)) { 2080#if OPT_READLINE 2081 int ldelta; 2082 TScreen *screen = TScreenOf(xw); 2083 if (IsBtnEvent(event) 2084 /* Disable on Shift-mouse, including the application-mouse modes */ 2085 && !(KeyModifiers(event) & ShiftMask) 2086 && (screen->send_mouse_pos == MOUSE_OFF) 2087 && SCREEN_FLAG(screen, paste_moves) 2088 && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta)) 2089 ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta); 2090#endif /* OPT_READLINE */ 2091 2092 xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL); 2093 } 2094 } 2095} 2096 2097static SelectUnit 2098EvalSelectUnit(XtermWidget xw, 2099 Time buttonDownTime, 2100 SelectUnit defaultUnit, 2101 unsigned int button) 2102{ 2103 TScreen *screen = TScreenOf(xw); 2104 SelectUnit result; 2105 int delta; 2106 2107 if (button != screen->lastButton) { 2108 delta = screen->multiClickTime + 1; 2109 } else if (screen->lastButtonUpTime == (Time) 0) { 2110 /* first time and once in a blue moon */ 2111 delta = screen->multiClickTime + 1; 2112 } else if (buttonDownTime > screen->lastButtonUpTime) { 2113 /* most of the time */ 2114 delta = (int) (buttonDownTime - screen->lastButtonUpTime); 2115 } else { 2116 /* time has rolled over since lastButtonUpTime */ 2117 delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime); 2118 } 2119 2120 if (delta > screen->multiClickTime) { 2121 screen->numberOfClicks = 1; 2122 result = defaultUnit; 2123 } else { 2124 result = screen->selectMap[screen->numberOfClicks % screen->maxClicks]; 2125 screen->numberOfClicks += 1; 2126 } 2127 TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result)); 2128 return result; 2129} 2130 2131static void 2132do_select_start(XtermWidget xw, 2133 XEvent * event, /* must be XButtonEvent* */ 2134 CELL * cell) 2135{ 2136 TScreen *screen = TScreenOf(xw); 2137 2138 if (SendMousePosition(xw, event)) 2139 return; 2140 screen->selectUnit = EvalSelectUnit(xw, 2141 event->xbutton.time, 2142 Select_CHAR, 2143 event->xbutton.button); 2144 screen->replyToEmacs = False; 2145 2146#if OPT_READLINE 2147 lastButtonDownTime = event->xbutton.time; 2148#endif 2149 2150 StartSelect(xw, cell); 2151} 2152 2153/* ARGSUSED */ 2154void 2155HandleSelectStart(Widget w, 2156 XEvent * event, /* must be XButtonEvent* */ 2157 String * params GCC_UNUSED, 2158 Cardinal *num_params GCC_UNUSED) 2159{ 2160 XtermWidget xw; 2161 2162 if ((xw = getXtermWidget(w)) != 0) { 2163 TScreen *screen = TScreenOf(xw); 2164 CELL cell; 2165 2166 screen->firstValidRow = 0; 2167 screen->lastValidRow = screen->max_row; 2168 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell); 2169 2170#if OPT_READLINE 2171 ExtendingSelection = 0; 2172#endif 2173 2174 do_select_start(xw, event, &cell); 2175 } 2176} 2177 2178/* ARGSUSED */ 2179void 2180HandleKeyboardSelectStart(Widget w, 2181 XEvent * event, /* must be XButtonEvent* */ 2182 String * params GCC_UNUSED, 2183 Cardinal *num_params GCC_UNUSED) 2184{ 2185 XtermWidget xw; 2186 2187 if ((xw = getXtermWidget(w)) != 0) { 2188 TScreen *screen = TScreenOf(xw); 2189 do_select_start(xw, event, &screen->cursorp); 2190 } 2191} 2192 2193static void 2194TrackDown(XtermWidget xw, XButtonEvent * event) 2195{ 2196 TScreen *screen = TScreenOf(xw); 2197 CELL cell; 2198 2199 screen->selectUnit = EvalSelectUnit(xw, 2200 event->time, 2201 Select_CHAR, 2202 event->button); 2203 if (screen->numberOfClicks > 1) { 2204 PointToCELL(screen, event->y, event->x, &cell); 2205 screen->replyToEmacs = True; 2206 StartSelect(xw, &cell); 2207 } else { 2208 screen->waitingForTrackInfo = True; 2209 EditorButton(xw, event); 2210 } 2211} 2212 2213#define boundsCheck(x) if (x < 0) \ 2214 x = 0; \ 2215 else if (x >= screen->max_row) \ 2216 x = screen->max_row 2217 2218void 2219TrackMouse(XtermWidget xw, 2220 int func, 2221 CELL * start, 2222 int firstrow, 2223 int lastrow) 2224{ 2225 TScreen *screen = TScreenOf(xw); 2226 2227 if (screen->waitingForTrackInfo) { /* if Timed, ignore */ 2228 screen->waitingForTrackInfo = False; 2229 2230 if (func != 0) { 2231 CELL first = *start; 2232 2233 boundsCheck(first.row); 2234 boundsCheck(firstrow); 2235 boundsCheck(lastrow); 2236 screen->firstValidRow = firstrow; 2237 screen->lastValidRow = lastrow; 2238 screen->replyToEmacs = True; 2239 StartSelect(xw, &first); 2240 } 2241 } 2242} 2243 2244static void 2245StartSelect(XtermWidget xw, const CELL * cell) 2246{ 2247 TScreen *screen = TScreenOf(xw); 2248 2249 TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col)); 2250 if (screen->cursor_state) 2251 HideCursor(); 2252 if (screen->numberOfClicks == 1) { 2253 /* set start of selection */ 2254 screen->rawPos = *cell; 2255 } 2256 /* else use old values in rawPos */ 2257 screen->saveStartR = screen->startExt = screen->rawPos; 2258 screen->saveEndR = screen->endExt = screen->rawPos; 2259 if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) { 2260 screen->eventMode = LEFTEXTENSION; 2261 screen->startExt = *cell; 2262 } else { 2263 screen->eventMode = RIGHTEXTENSION; 2264 screen->endExt = *cell; 2265 } 2266 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False); 2267} 2268 2269static void 2270EndExtend(XtermWidget xw, 2271 XEvent * event, /* must be XButtonEvent */ 2272 String * params, /* selections */ 2273 Cardinal num_params, 2274 Bool use_cursor_loc) 2275{ 2276 CELL cell; 2277 unsigned count; 2278 TScreen *screen = TScreenOf(xw); 2279 Char line[20]; 2280 2281 if (use_cursor_loc) { 2282 cell = screen->cursorp; 2283 } else { 2284 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell); 2285 } 2286 ExtendExtend(xw, &cell); 2287 screen->lastButtonUpTime = event->xbutton.time; 2288 screen->lastButton = event->xbutton.button; 2289 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) { 2290 if (screen->replyToEmacs) { 2291 count = 0; 2292 if (screen->control_eight_bits) { 2293 line[count++] = ANSI_CSI; 2294 } else { 2295 line[count++] = ANSI_ESC; 2296 line[count++] = '['; 2297 } 2298 if (isSameCELL(&(screen->rawPos), &(screen->startSel)) 2299 && isSameCELL(&cell, &(screen->endSel))) { 2300 /* Use short-form emacs select */ 2301 line[count++] = 't'; 2302 count = EmitMousePosition(screen, line, count, screen->endSel.col); 2303 count = EmitMousePosition(screen, line, count, screen->endSel.row); 2304 } else { 2305 /* long-form, specify everything */ 2306 line[count++] = 'T'; 2307 count = EmitMousePosition(screen, line, count, screen->startSel.col); 2308 count = EmitMousePosition(screen, line, count, screen->startSel.row); 2309 count = EmitMousePosition(screen, line, count, screen->endSel.col); 2310 count = EmitMousePosition(screen, line, count, screen->endSel.row); 2311 count = EmitMousePosition(screen, line, count, cell.col); 2312 count = EmitMousePosition(screen, line, count, cell.row); 2313 } 2314 v_write(screen->respond, line, count); 2315 TrackText(xw, &zeroCELL, &zeroCELL); 2316 } 2317 } 2318 SelectSet(xw, event, params, num_params); 2319 screen->eventMode = NORMAL; 2320} 2321 2322void 2323HandleSelectSet(Widget w, 2324 XEvent * event, 2325 String * params, 2326 Cardinal *num_params) 2327{ 2328 XtermWidget xw; 2329 2330 if ((xw = getXtermWidget(w)) != 0) { 2331 SelectSet(xw, event, params, *num_params); 2332 } 2333} 2334 2335/* ARGSUSED */ 2336static void 2337SelectSet(XtermWidget xw, 2338 XEvent * event GCC_UNUSED, 2339 String * params, 2340 Cardinal num_params) 2341{ 2342 TScreen *screen = TScreenOf(xw); 2343 2344 TRACE(("SelectSet\n")); 2345 /* Only do select stuff if non-null select */ 2346 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) { 2347 SaltTextAway(xw, &(screen->startSel), &(screen->endSel), params, num_params); 2348 } else { 2349 DisownSelection(xw); 2350 } 2351} 2352 2353#define Abs(x) ((x) < 0 ? -(x) : (x)) 2354 2355/* ARGSUSED */ 2356static void 2357do_start_extend(XtermWidget xw, 2358 XEvent * event, /* must be XButtonEvent* */ 2359 String * params GCC_UNUSED, 2360 Cardinal *num_params GCC_UNUSED, 2361 Bool use_cursor_loc) 2362{ 2363 TScreen *screen = TScreenOf(xw); 2364 int coord; 2365 CELL cell; 2366 2367 if (SendMousePosition(xw, event)) 2368 return; 2369 2370 screen->firstValidRow = 0; 2371 screen->lastValidRow = screen->max_row; 2372#if OPT_READLINE 2373 if ((KeyModifiers(event) & ShiftMask) 2374 || event->xbutton.button != Button3 2375 || !(SCREEN_FLAG(screen, dclick3_deletes))) 2376#endif 2377 screen->selectUnit = EvalSelectUnit(xw, 2378 event->xbutton.time, 2379 screen->selectUnit, 2380 event->xbutton.button); 2381 screen->replyToEmacs = False; 2382 2383#if OPT_READLINE 2384 CheckSecondPress3(screen, event); 2385#endif 2386 2387 if (screen->numberOfClicks == 1 2388 || (SCREEN_FLAG(screen, dclick3_deletes) /* Dclick special */ 2389 &&!(KeyModifiers(event) & ShiftMask))) { 2390 /* Save existing selection so we can reestablish it if the guy 2391 extends past the other end of the selection */ 2392 screen->saveStartR = screen->startExt = screen->startRaw; 2393 screen->saveEndR = screen->endExt = screen->endRaw; 2394 } else { 2395 /* He just needed the selection mode changed, use old values. */ 2396 screen->startExt = screen->startRaw = screen->saveStartR; 2397 screen->endExt = screen->endRaw = screen->saveEndR; 2398 } 2399 if (use_cursor_loc) { 2400 cell = screen->cursorp; 2401 } else { 2402 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell); 2403 } 2404 coord = Coordinate(screen, &cell); 2405 2406 if (Abs(coord - Coordinate(screen, &(screen->startSel))) 2407 < Abs(coord - Coordinate(screen, &(screen->endSel))) 2408 || coord < Coordinate(screen, &(screen->startSel))) { 2409 /* point is close to left side of selection */ 2410 screen->eventMode = LEFTEXTENSION; 2411 screen->startExt = cell; 2412 } else { 2413 /* point is close to left side of selection */ 2414 screen->eventMode = RIGHTEXTENSION; 2415 screen->endExt = cell; 2416 } 2417 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True); 2418 2419#if OPT_READLINE 2420 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) 2421 ExtendingSelection = 1; 2422#endif 2423} 2424 2425static void 2426ExtendExtend(XtermWidget xw, const CELL * cell) 2427{ 2428 TScreen *screen = TScreenOf(xw); 2429 int coord = Coordinate(screen, cell); 2430 2431 TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col)); 2432 if (screen->eventMode == LEFTEXTENSION 2433 && ((coord + (screen->selectUnit != Select_CHAR)) 2434 > Coordinate(screen, &(screen->endSel)))) { 2435 /* Whoops, he's changed his mind. Do RIGHTEXTENSION */ 2436 screen->eventMode = RIGHTEXTENSION; 2437 screen->startExt = screen->saveStartR; 2438 } else if (screen->eventMode == RIGHTEXTENSION 2439 && coord < Coordinate(screen, &(screen->startSel))) { 2440 /* Whoops, he's changed his mind. Do LEFTEXTENSION */ 2441 screen->eventMode = LEFTEXTENSION; 2442 screen->endExt = screen->saveEndR; 2443 } 2444 if (screen->eventMode == LEFTEXTENSION) { 2445 screen->startExt = *cell; 2446 } else { 2447 screen->endExt = *cell; 2448 } 2449 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False); 2450 2451#if OPT_READLINE 2452 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) 2453 ExtendingSelection = 1; 2454#endif 2455} 2456 2457void 2458HandleStartExtend(Widget w, 2459 XEvent * event, /* must be XButtonEvent* */ 2460 String * params, /* unused */ 2461 Cardinal *num_params) /* unused */ 2462{ 2463 XtermWidget xw; 2464 2465 if ((xw = getXtermWidget(w)) != 0) { 2466 do_start_extend(xw, event, params, num_params, False); 2467 } 2468} 2469 2470void 2471HandleKeyboardStartExtend(Widget w, 2472 XEvent * event, /* must be XButtonEvent* */ 2473 String * params, /* unused */ 2474 Cardinal *num_params) /* unused */ 2475{ 2476 XtermWidget xw; 2477 2478 if ((xw = getXtermWidget(w)) != 0) { 2479 do_start_extend(xw, event, params, num_params, True); 2480 } 2481} 2482 2483void 2484ScrollSelection(TScreen * screen, int amount, Bool always) 2485{ 2486 int minrow = INX2ROW(screen, -screen->savedlines); 2487 int maxrow = INX2ROW(screen, screen->max_row); 2488 int maxcol = screen->max_col; 2489 2490#define scroll_update_one(cell) \ 2491 (cell)->row += amount; \ 2492 if ((cell)->row < minrow) { \ 2493 (cell)->row = minrow; \ 2494 (cell)->col = 0; \ 2495 } \ 2496 if ((cell)->row > maxrow) { \ 2497 (cell)->row = maxrow; \ 2498 (cell)->col = maxcol; \ 2499 } 2500 2501 scroll_update_one(&(screen->startRaw)); 2502 scroll_update_one(&(screen->endRaw)); 2503 scroll_update_one(&(screen->startSel)); 2504 scroll_update_one(&(screen->endSel)); 2505 2506 scroll_update_one(&(screen->rawPos)); 2507 2508 /* 2509 * If we are told to scroll the selection but it lies outside the scrolling 2510 * margins, then that could cause the selection to move (bad). It is not 2511 * simple to fix, because this function is called both for the scrollbar 2512 * actions as well as application scrolling. The 'always' flag is set in 2513 * the former case. The rest of the logic handles the latter. 2514 */ 2515 if (ScrnHaveSelection(screen)) { 2516 int adjust; 2517 2518 adjust = ROW2INX(screen, screen->startH.row); 2519 if (always 2520 || !ScrnHaveLineMargins(screen) 2521 || ScrnIsLineInMargins(screen, adjust)) { 2522 scroll_update_one(&screen->startH); 2523 } 2524 adjust = ROW2INX(screen, screen->endH.row); 2525 if (always 2526 || !ScrnHaveLineMargins(screen) 2527 || ScrnIsLineInMargins(screen, adjust)) { 2528 scroll_update_one(&screen->endH); 2529 } 2530 } 2531 2532 screen->startHCoord = Coordinate(screen, &screen->startH); 2533 screen->endHCoord = Coordinate(screen, &screen->endH); 2534} 2535 2536/*ARGSUSED*/ 2537void 2538ResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols) 2539{ 2540 rows--; /* decr to get 0-max */ 2541 cols--; 2542 2543 if (screen->startRaw.row > rows) 2544 screen->startRaw.row = rows; 2545 if (screen->startSel.row > rows) 2546 screen->startSel.row = rows; 2547 if (screen->endRaw.row > rows) 2548 screen->endRaw.row = rows; 2549 if (screen->endSel.row > rows) 2550 screen->endSel.row = rows; 2551 if (screen->rawPos.row > rows) 2552 screen->rawPos.row = rows; 2553 2554 if (screen->startRaw.col > cols) 2555 screen->startRaw.col = cols; 2556 if (screen->startSel.col > cols) 2557 screen->startSel.col = cols; 2558 if (screen->endRaw.col > cols) 2559 screen->endRaw.col = cols; 2560 if (screen->endSel.col > cols) 2561 screen->endSel.col = cols; 2562 if (screen->rawPos.col > cols) 2563 screen->rawPos.col = cols; 2564} 2565 2566#if OPT_WIDE_CHARS 2567Bool 2568iswide(int i) 2569{ 2570 return (i == HIDDEN_CHAR) || (WideCells(i) == 2); 2571} 2572 2573#define isWideCell(row, col) iswide((int)XTERM_CELL(row, col)) 2574#endif 2575 2576static void 2577PointToCELL(TScreen * screen, 2578 int y, 2579 int x, 2580 CELL * cell) 2581/* Convert pixel coordinates to character coordinates. 2582 Rows are clipped between firstValidRow and lastValidRow. 2583 Columns are clipped between to be 0 or greater, but are not clipped to some 2584 maximum value. */ 2585{ 2586 cell->row = (y - screen->border) / FontHeight(screen); 2587 if (cell->row < screen->firstValidRow) 2588 cell->row = screen->firstValidRow; 2589 else if (cell->row > screen->lastValidRow) 2590 cell->row = screen->lastValidRow; 2591 cell->col = (x - OriginX(screen)) / FontWidth(screen); 2592 if (cell->col < 0) 2593 cell->col = 0; 2594 else if (cell->col > MaxCols(screen)) { 2595 cell->col = MaxCols(screen); 2596 } 2597#if OPT_WIDE_CHARS 2598 /* 2599 * If we got a click on the right half of a doublewidth character, 2600 * pretend it happened on the left half. 2601 */ 2602 if (cell->col > 0 2603 && isWideCell(cell->row, cell->col - 1) 2604 && (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) { 2605 cell->col -= 1; 2606 } 2607#endif 2608} 2609 2610/* 2611 * Find the last column at which text was drawn on the given row. 2612 */ 2613static int 2614LastTextCol(TScreen * screen, LineData * ld, int row) 2615{ 2616 int i = -1; 2617 Char *ch; 2618 2619 if (ld != 0) { 2620 if (okScrnRow(screen, row)) { 2621 for (i = screen->max_col, 2622 ch = ld->attribs + i; 2623 i >= 0 && !(*ch & CHARDRAWN); 2624 ch--, i--) { 2625 ; 2626 } 2627#if OPT_DEC_CHRSET 2628 if (CSET_DOUBLE(GetLineDblCS(ld))) { 2629 i *= 2; 2630 } 2631#endif 2632 } 2633 } 2634 return (i); 2635} 2636 2637#if !OPT_WIDE_CHARS 2638/* 2639** double click table for cut and paste in 8 bits 2640** 2641** This table is divided in four parts : 2642** 2643** - control characters [0,0x1f] U [0x80,0x9f] 2644** - separators [0x20,0x3f] U [0xa0,0xb9] 2645** - binding characters [0x40,0x7f] U [0xc0,0xff] 2646** - exceptions 2647*/ 2648/* *INDENT-OFF* */ 2649static int charClass[256] = 2650{ 2651/* NUL SOH STX ETX EOT ENQ ACK BEL */ 2652 32, 1, 1, 1, 1, 1, 1, 1, 2653/* BS HT NL VT NP CR SO SI */ 2654 1, 32, 1, 1, 1, 1, 1, 1, 2655/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ 2656 1, 1, 1, 1, 1, 1, 1, 1, 2657/* CAN EM SUB ESC FS GS RS US */ 2658 1, 1, 1, 1, 1, 1, 1, 1, 2659/* SP ! " # $ % & ' */ 2660 32, 33, 34, 35, 36, 37, 38, 39, 2661/* ( ) * + , - . / */ 2662 40, 41, 42, 43, 44, 45, 46, 47, 2663/* 0 1 2 3 4 5 6 7 */ 2664 48, 48, 48, 48, 48, 48, 48, 48, 2665/* 8 9 : ; < = > ? */ 2666 48, 48, 58, 59, 60, 61, 62, 63, 2667/* @ A B C D E F G */ 2668 64, 48, 48, 48, 48, 48, 48, 48, 2669/* H I J K L M N O */ 2670 48, 48, 48, 48, 48, 48, 48, 48, 2671/* P Q R S T U V W */ 2672 48, 48, 48, 48, 48, 48, 48, 48, 2673/* X Y Z [ \ ] ^ _ */ 2674 48, 48, 48, 91, 92, 93, 94, 48, 2675/* ` a b c d e f g */ 2676 96, 48, 48, 48, 48, 48, 48, 48, 2677/* h i j k l m n o */ 2678 48, 48, 48, 48, 48, 48, 48, 48, 2679/* p q r s t u v w */ 2680 48, 48, 48, 48, 48, 48, 48, 48, 2681/* x y z { | } ~ DEL */ 2682 48, 48, 48, 123, 124, 125, 126, 1, 2683/* x80 x81 x82 x83 IND NEL SSA ESA */ 2684 1, 1, 1, 1, 1, 1, 1, 1, 2685/* HTS HTJ VTS PLD PLU RI SS2 SS3 */ 2686 1, 1, 1, 1, 1, 1, 1, 1, 2687/* DCS PU1 PU2 STS CCH MW SPA EPA */ 2688 1, 1, 1, 1, 1, 1, 1, 1, 2689/* x98 x99 x9A CSI ST OSC PM APC */ 2690 1, 1, 1, 1, 1, 1, 1, 1, 2691/* - i c/ L ox Y- | So */ 2692 160, 161, 162, 163, 164, 165, 166, 167, 2693/* .. c0 ip << _ R0 - */ 2694 168, 169, 170, 171, 172, 173, 174, 175, 2695/* o +- 2 3 ' u q| . */ 2696 176, 177, 178, 179, 180, 181, 182, 183, 2697/* , 1 2 >> 1/4 1/2 3/4 ? */ 2698 184, 185, 186, 187, 188, 189, 190, 191, 2699/* A` A' A^ A~ A: Ao AE C, */ 2700 48, 48, 48, 48, 48, 48, 48, 48, 2701/* E` E' E^ E: I` I' I^ I: */ 2702 48, 48, 48, 48, 48, 48, 48, 48, 2703/* D- N~ O` O' O^ O~ O: X */ 2704 48, 48, 48, 48, 48, 48, 48, 215, 2705/* O/ U` U' U^ U: Y' P B */ 2706 48, 48, 48, 48, 48, 48, 48, 48, 2707/* a` a' a^ a~ a: ao ae c, */ 2708 48, 48, 48, 48, 48, 48, 48, 48, 2709/* e` e' e^ e: i` i' i^ i: */ 2710 48, 48, 48, 48, 48, 48, 48, 48, 2711/* d n~ o` o' o^ o~ o: -: */ 2712 48, 48, 48, 48, 48, 48, 48, 247, 2713/* o/ u` u' u^ u: y' P y: */ 2714 48, 48, 48, 48, 48, 48, 48, 48}; 2715/* *INDENT-ON* */ 2716 2717int 2718SetCharacterClassRange(int low, /* in range of [0..255] */ 2719 int high, 2720 int value) /* arbitrary */ 2721{ 2722 2723 if (low < 0 || high > 255 || high < low) 2724 return (-1); 2725 2726 for (; low <= high; low++) 2727 charClass[low] = value; 2728 2729 return (0); 2730} 2731#endif 2732 2733static int 2734class_of(LineData * ld, CELL * cell) 2735{ 2736 CELL temp = *cell; 2737 2738#if OPT_DEC_CHRSET 2739 if (CSET_DOUBLE(GetLineDblCS(ld))) { 2740 temp.col /= 2; 2741 } 2742#endif 2743 2744 assert(temp.col < ld->lineSize); 2745 return CharacterClass((int) (ld->charData[temp.col])); 2746} 2747 2748#if OPT_WIDE_CHARS 2749#define CClassSelects(name, cclass) \ 2750 (CClassOf(name) == cclass \ 2751 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR) 2752#else 2753#define CClassSelects(name, cclass) \ 2754 (class_of(ld.name, &((screen->name))) == cclass) 2755#endif 2756 2757#define CClassOf(name) class_of(ld.name, &((screen->name))) 2758 2759/* 2760 * If the given column is past the end of text on the given row, bump to the 2761 * beginning of the next line. 2762 */ 2763static Boolean 2764okPosition(TScreen * screen, 2765 LineData ** ld, 2766 CELL * cell) 2767{ 2768 Boolean result = True; 2769 2770 if (cell->row > screen->max_row) { 2771 result = False; 2772 } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) { 2773 if (cell->row < screen->max_row) { 2774 cell->col = 0; 2775 *ld = GET_LINEDATA(screen, ++cell->row); 2776 result = False; 2777 } 2778 } 2779 return result; 2780} 2781 2782static void 2783trimLastLine(TScreen * screen, 2784 LineData ** ld, 2785 CELL * last) 2786{ 2787 if (screen->cutNewline && last->row < screen->max_row) { 2788 last->col = 0; 2789 *ld = GET_LINEDATA(screen, ++last->row); 2790 } else { 2791 last->col = LastTextCol(screen, *ld, last->row) + 1; 2792 } 2793} 2794 2795#if OPT_SELECT_REGEX 2796/* 2797 * Returns the first row of a wrapped line. 2798 */ 2799static int 2800firstRowOfLine(TScreen * screen, int row, Bool visible) 2801{ 2802 LineData *ld = 0; 2803 int limit = visible ? 0 : -screen->savedlines; 2804 2805 while (row > limit && 2806 (ld = GET_LINEDATA(screen, row - 1)) != 0 && 2807 LineTstWrapped(ld)) { 2808 --row; 2809 } 2810 return row; 2811} 2812 2813/* 2814 * Returns the last row of a wrapped line. 2815 */ 2816static int 2817lastRowOfLine(TScreen * screen, int row) 2818{ 2819 LineData *ld; 2820 2821 while (row < screen->max_row && 2822 (ld = GET_LINEDATA(screen, row)) != 0 && 2823 LineTstWrapped(ld)) { 2824 ++row; 2825 } 2826 return row; 2827} 2828 2829/* 2830 * Returns the number of cells on the range of rows. 2831 */ 2832static unsigned 2833lengthOfLines(TScreen * screen, int firstRow, int lastRow) 2834{ 2835 unsigned length = 0; 2836 int n; 2837 2838 for (n = firstRow; n <= lastRow; ++n) { 2839 LineData *ld = GET_LINEDATA(screen, n); 2840 int value = LastTextCol(screen, ld, n); 2841 if (value >= 0) 2842 length += (unsigned) (value + 1); 2843 } 2844 return length; 2845} 2846 2847/* 2848 * Make a copy of the wrapped-line which corresponds to the given row as a 2849 * string of bytes. Construct an index for the columns from the beginning of 2850 * the line. 2851 */ 2852static char * 2853make_indexed_text(TScreen * screen, int row, unsigned length, int *indexed) 2854{ 2855 Char *result = 0; 2856 size_t need = (length + 1); 2857 2858 /* 2859 * Get a quick upper bound to the number of bytes needed, if the whole 2860 * string were UTF-8. 2861 */ 2862 if_OPT_WIDE_CHARS(screen, { 2863 need *= ((screen->lineExtra + 1) * 6); 2864 }); 2865 2866 if ((result = TypeCallocN(Char, need + 1)) != 0) { 2867 LineData *ld = GET_LINEDATA(screen, row); 2868 unsigned used = 0; 2869 Char *last = result; 2870 2871 do { 2872 int col = 0; 2873 int limit = LastTextCol(screen, ld, row); 2874 2875 while (col <= limit) { 2876 Char *next = last; 2877 unsigned data = ld->charData[col]; 2878 2879 assert(col < ld->lineSize); 2880 /* some internal points may not be drawn */ 2881 if (data == 0) 2882 data = ' '; 2883 2884 if_WIDE_OR_NARROW(screen, { 2885 next = convertToUTF8(last, data); 2886 } 2887 , { 2888 *next++ = CharOf(data); 2889 }); 2890 2891 if_OPT_WIDE_CHARS(screen, { 2892 size_t off; 2893 for_each_combData(off, ld) { 2894 data = ld->combData[off][col]; 2895 if (data == 0) 2896 break; 2897 next = convertToUTF8(next, data); 2898 } 2899 }); 2900 2901 indexed[used] = (int) (last - result); 2902 *next = 0; 2903 /* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */ 2904 last = next; 2905 ++used; 2906 ++col; 2907 indexed[used] = (int) (next - result); 2908 } 2909 } while (used < length && 2910 LineTstWrapped(ld) && 2911 (ld = GET_LINEDATA(screen, ++row)) != 0 && 2912 row < screen->max_row); 2913 } 2914 /* TRACE(("result:%s\n", result)); */ 2915 return (char *) result; 2916} 2917 2918/* 2919 * Find the column given an offset into the character string by using the 2920 * index constructed in make_indexed_text(). 2921 */ 2922static int 2923indexToCol(int *indexed, int len, int off) 2924{ 2925 int col = 0; 2926 while (indexed[col] < len) { 2927 if (indexed[col] >= off) 2928 break; 2929 ++col; 2930 } 2931 return col; 2932} 2933 2934/* 2935 * Given a row number, and a column offset from that (which may be wrapped), 2936 * set the cell to the actual row/column values. 2937 */ 2938static void 2939columnToCell(TScreen * screen, int row, int col, CELL * cell) 2940{ 2941 while (row < screen->max_row) { 2942 LineData *ld = GET_LINEDATA(screen, row); 2943 int last = LastTextCol(screen, ld, row); 2944 2945 /* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */ 2946 if (col <= last) { 2947 break; 2948 } 2949 /* 2950 * Stop if the current row does not wrap (does not continue the current 2951 * line). 2952 */ 2953 if (!LineTstWrapped(ld)) { 2954 col = last + 1; 2955 break; 2956 } 2957 col -= (last + 1); 2958 ++row; 2959 } 2960 if (col < 0) 2961 col = 0; 2962 cell->row = row; 2963 cell->col = col; 2964} 2965 2966/* 2967 * Given a cell, find the corresponding column offset. 2968 */ 2969static int 2970cellToColumn(TScreen * screen, CELL * cell) 2971{ 2972 LineData *ld = 0; 2973 int col = cell->col; 2974 int row = firstRowOfLine(screen, cell->row, False); 2975 while (row < cell->row) { 2976 ld = GET_LINEDATA(screen, row); 2977 col += LastTextCol(screen, ld, row++); 2978 } 2979#if OPT_DEC_CHRSET 2980 if (ld == 0) 2981 ld = GET_LINEDATA(screen, row); 2982 if (CSET_DOUBLE(GetLineDblCS(ld))) 2983 col /= 2; 2984#endif 2985 return col; 2986} 2987 2988static void 2989do_select_regex(TScreen * screen, CELL * startc, CELL * endc) 2990{ 2991 LineData *ld = GET_LINEDATA(screen, startc->row); 2992 int inx = ((screen->numberOfClicks - 1) % screen->maxClicks); 2993 char *expr = screen->selectExpr[inx]; 2994 regex_t preg; 2995 regmatch_t match; 2996 char *search; 2997 int *indexed; 2998 2999 TRACE(("Select_REGEX:%s\n", NonNull(expr))); 3000 if (okPosition(screen, &ld, startc) && expr != 0) { 3001 if (regcomp(&preg, expr, REG_EXTENDED) == 0) { 3002 int firstRow = firstRowOfLine(screen, startc->row, True); 3003 int lastRow = lastRowOfLine(screen, firstRow); 3004 unsigned size = lengthOfLines(screen, firstRow, lastRow); 3005 int actual = cellToColumn(screen, startc); 3006 3007 TRACE(("regcomp ok rows %d..%d bytes %d\n", 3008 firstRow, lastRow, size)); 3009 3010 if ((indexed = TypeCallocN(int, size + 1)) != 0) { 3011 if ((search = make_indexed_text(screen, 3012 firstRow, 3013 size, 3014 indexed)) != 0) { 3015 int len = (int) strlen(search); 3016 int col; 3017 int best_col = -1; 3018 int best_len = -1; 3019 3020 for (col = 0; indexed[col] < len; ++col) { 3021 if (regexec(&preg, 3022 search + indexed[col], 3023 (size_t) 1, &match, 0) == 0) { 3024 int start_inx = match.rm_so + indexed[col]; 3025 int finis_inx = match.rm_eo + indexed[col]; 3026 int start_col = indexToCol(indexed, len, start_inx); 3027 int finis_col = indexToCol(indexed, len, finis_inx); 3028 3029 if (start_col <= actual && 3030 actual < finis_col) { 3031 int test = finis_col - start_col; 3032 if (best_len < test) { 3033 best_len = test; 3034 best_col = start_col; 3035 TRACE(("match column %d len %d\n", 3036 best_col, 3037 best_len)); 3038 } 3039 } 3040 } 3041 } 3042 if (best_col >= 0) { 3043 int best_nxt = best_col + best_len; 3044 columnToCell(screen, firstRow, best_col, startc); 3045 columnToCell(screen, firstRow, best_nxt, endc); 3046 TRACE(("search::%s\n", search)); 3047 TRACE(("indexed:%d..%d -> %d..%d\n", 3048 best_col, best_nxt, 3049 indexed[best_col], 3050 indexed[best_nxt])); 3051 TRACE(("matched:%d:%s\n", 3052 indexed[best_nxt] + 1 - 3053 indexed[best_col], 3054 visibleChars((Char *) (search + indexed[best_col]), 3055 (unsigned) (indexed[best_nxt] + 3056 1 - 3057 indexed[best_col])))); 3058 } 3059 free(search); 3060 } 3061 free(indexed); 3062#if OPT_DEC_CHRSET 3063 if ((ld = GET_LINEDATA(screen, startc->row)) != 0) { 3064 if (CSET_DOUBLE(GetLineDblCS(ld))) 3065 startc->col *= 2; 3066 } 3067 if ((ld = GET_LINEDATA(screen, endc->row)) != 0) { 3068 if (CSET_DOUBLE(GetLineDblCS(ld))) 3069 endc->col *= 2; 3070 } 3071#endif 3072 } 3073 regfree(&preg); 3074 } 3075 } 3076} 3077#endif /* OPT_SELECT_REGEX */ 3078 3079#define InitRow(name) \ 3080 ld.name = GET_LINEDATA(screen, screen->name.row) 3081 3082#define NextRow(name) \ 3083 ld.name = GET_LINEDATA(screen, ++screen->name.row) 3084 3085#define PrevRow(name) \ 3086 ld.name = GET_LINEDATA(screen, --screen->name.row) 3087 3088#define MoreRows(name) \ 3089 (screen->name.row < screen->max_row) 3090 3091#define isPrevWrapped(name) \ 3092 (screen->name.row > 0 \ 3093 && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \ 3094 && LineTstWrapped(ltmp)) 3095 3096/* 3097 * sets startSel endSel 3098 * ensuring that they have legal values 3099 */ 3100static void 3101ComputeSelect(XtermWidget xw, 3102 CELL * startc, 3103 CELL * endc, 3104 Bool extend) 3105{ 3106 TScreen *screen = TScreenOf(xw); 3107 3108 int length; 3109 int cclass; 3110 CELL first = *startc; 3111 CELL last = *endc; 3112 Boolean ignored = False; 3113 3114 struct { 3115 LineData *startSel; 3116 LineData *endSel; 3117 } ld; 3118 LineData *ltmp; 3119 3120 TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n", 3121 first.row, first.col, 3122 last.row, last.col, 3123 extend ? "" : "no")); 3124 3125#if OPT_WIDE_CHARS 3126 if (first.col > 1 3127 && isWideCell(first.row, first.col - 1) 3128 && XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) { 3129 TRACE(("Adjusting start. Changing downwards from %i.\n", first.col)); 3130 first.col -= 1; 3131 if (last.col == (first.col + 1)) 3132 last.col--; 3133 } 3134 3135 if (last.col > 1 3136 && isWideCell(last.row, last.col - 1) 3137 && XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) { 3138 last.col += 1; 3139 } 3140#endif 3141 3142 if (Coordinate(screen, &first) <= Coordinate(screen, &last)) { 3143 screen->startSel = screen->startRaw = first; 3144 screen->endSel = screen->endRaw = last; 3145 } else { /* Swap them */ 3146 screen->startSel = screen->startRaw = last; 3147 screen->endSel = screen->endRaw = first; 3148 } 3149 3150 InitRow(startSel); 3151 InitRow(endSel); 3152 3153 switch (screen->selectUnit) { 3154 case Select_CHAR: 3155 (void) okPosition(screen, &(ld.startSel), &(screen->startSel)); 3156 (void) okPosition(screen, &(ld.endSel), &(screen->endSel)); 3157 break; 3158 3159 case Select_WORD: 3160 TRACE(("Select_WORD\n")); 3161 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) { 3162 cclass = CClassOf(startSel); 3163 do { 3164 --screen->startSel.col; 3165 if (screen->startSel.col < 0 3166 && isPrevWrapped(startSel)) { 3167 PrevRow(startSel); 3168 screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row); 3169 } 3170 } while (screen->startSel.col >= 0 3171 && CClassSelects(startSel, cclass)); 3172 ++screen->startSel.col; 3173 } 3174#if OPT_WIDE_CHARS 3175 if (screen->startSel.col 3176 && XTERM_CELL(screen->startSel.row, 3177 screen->startSel.col) == HIDDEN_CHAR) 3178 screen->startSel.col++; 3179#endif 3180 3181 if (okPosition(screen, &(ld.endSel), &(screen->endSel))) { 3182 length = LastTextCol(screen, ld.endSel, screen->endSel.row); 3183 cclass = CClassOf(endSel); 3184 do { 3185 ++screen->endSel.col; 3186 if (screen->endSel.col > length 3187 && LineTstWrapped(ld.endSel)) { 3188 if (!MoreRows(endSel)) 3189 break; 3190 screen->endSel.col = 0; 3191 NextRow(endSel); 3192 length = LastTextCol(screen, ld.endSel, screen->endSel.row); 3193 } 3194 } while (screen->endSel.col <= length 3195 && CClassSelects(endSel, cclass)); 3196 /* Word-select selects if pointing to any char in "word", 3197 * especially note that it includes the last character in a word. 3198 * So we do no --endSel.col and do special eol handling. 3199 */ 3200 if (screen->endSel.col > length + 1 3201 && MoreRows(endSel)) { 3202 screen->endSel.col = 0; 3203 NextRow(endSel); 3204 } 3205 } 3206#if OPT_WIDE_CHARS 3207 if (screen->endSel.col 3208 && XTERM_CELL(screen->endSel.row, 3209 screen->endSel.col) == HIDDEN_CHAR) 3210 screen->endSel.col++; 3211#endif 3212 3213 screen->saveStartW = screen->startSel; 3214 break; 3215 3216 case Select_LINE: 3217 TRACE(("Select_LINE\n")); 3218 while (LineTstWrapped(ld.endSel) 3219 && MoreRows(endSel)) { 3220 NextRow(endSel); 3221 } 3222 if (screen->cutToBeginningOfLine 3223 || screen->startSel.row < screen->saveStartW.row) { 3224 screen->startSel.col = 0; 3225 while (isPrevWrapped(startSel)) { 3226 PrevRow(startSel); 3227 } 3228 } else if (!extend) { 3229 if ((first.row < screen->saveStartW.row) 3230 || (isSameRow(&first, &(screen->saveStartW)) 3231 && first.col < screen->saveStartW.col)) { 3232 screen->startSel.col = 0; 3233 while (isPrevWrapped(startSel)) { 3234 PrevRow(startSel); 3235 } 3236 } else { 3237 screen->startSel = screen->saveStartW; 3238 } 3239 } 3240 trimLastLine(screen, &(ld.endSel), &(screen->endSel)); 3241 break; 3242 3243 case Select_GROUP: /* paragraph */ 3244 TRACE(("Select_GROUP\n")); 3245 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) { 3246 /* scan backward for beginning of group */ 3247 while (screen->startSel.row > 0 && 3248 (LastTextCol(screen, ld.startSel, screen->startSel.row - 3249 1) > 0 || 3250 isPrevWrapped(startSel))) { 3251 PrevRow(startSel); 3252 } 3253 screen->startSel.col = 0; 3254 /* scan forward for end of group */ 3255 while (MoreRows(endSel) && 3256 (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) > 3257 0 || 3258 LineTstWrapped(ld.endSel))) { 3259 NextRow(endSel); 3260 } 3261 trimLastLine(screen, &(ld.endSel), &(screen->endSel)); 3262 } 3263 break; 3264 3265 case Select_PAGE: /* everything one can see */ 3266 TRACE(("Select_PAGE\n")); 3267 screen->startSel.row = 0; 3268 screen->startSel.col = 0; 3269 screen->endSel.row = MaxRows(screen); 3270 screen->endSel.col = 0; 3271 break; 3272 3273 case Select_ALL: /* counts scrollback if in normal screen */ 3274 TRACE(("Select_ALL\n")); 3275 screen->startSel.row = -screen->savedlines; 3276 screen->startSel.col = 0; 3277 screen->endSel.row = MaxRows(screen); 3278 screen->endSel.col = 0; 3279 break; 3280 3281#if OPT_SELECT_REGEX 3282 case Select_REGEX: 3283 do_select_regex(screen, &(screen->startSel), &(screen->endSel)); 3284 break; 3285#endif 3286 3287 case NSELECTUNITS: /* always ignore */ 3288 ignored = True; 3289 break; 3290 } 3291 3292 if (!ignored) { 3293 /* check boundaries */ 3294 ScrollSelection(screen, 0, False); 3295 TrackText(xw, &(screen->startSel), &(screen->endSel)); 3296 } 3297 3298 return; 3299} 3300 3301/* Guaranteed (first.row, first.col) <= (last.row, last.col) */ 3302static void 3303TrackText(XtermWidget xw, 3304 const CELL * firstp, 3305 const CELL * lastp) 3306{ 3307 TScreen *screen = TScreenOf(xw); 3308 int from, to; 3309 CELL old_start, old_end; 3310 CELL first = *firstp; 3311 CELL last = *lastp; 3312 3313 TRACE(("TrackText(first=%d,%d, last=%d,%d)\n", 3314 first.row, first.col, last.row, last.col)); 3315 3316 old_start = screen->startH; 3317 old_end = screen->endH; 3318 if (isSameCELL(&first, &old_start) && 3319 isSameCELL(&last, &old_end)) 3320 return; 3321 screen->startH = first; 3322 screen->endH = last; 3323 from = Coordinate(screen, &screen->startH); 3324 to = Coordinate(screen, &screen->endH); 3325 if (to <= screen->startHCoord || from > screen->endHCoord) { 3326 /* No overlap whatsoever between old and new hilite */ 3327 ReHiliteText(xw, &old_start, &old_end); 3328 ReHiliteText(xw, &first, &last); 3329 } else { 3330 if (from < screen->startHCoord) { 3331 /* Extend left end */ 3332 ReHiliteText(xw, &first, &old_start); 3333 } else if (from > screen->startHCoord) { 3334 /* Shorten left end */ 3335 ReHiliteText(xw, &old_start, &first); 3336 } 3337 if (to > screen->endHCoord) { 3338 /* Extend right end */ 3339 ReHiliteText(xw, &old_end, &last); 3340 } else if (to < screen->endHCoord) { 3341 /* Shorten right end */ 3342 ReHiliteText(xw, &last, &old_end); 3343 } 3344 } 3345 screen->startHCoord = from; 3346 screen->endHCoord = to; 3347} 3348 3349/* Guaranteed that (first->row, first->col) <= (last->row, last->col) */ 3350static void 3351ReHiliteText(XtermWidget xw, 3352 CELL * firstp, 3353 CELL * lastp) 3354{ 3355 TScreen *screen = TScreenOf(xw); 3356 int i; 3357 CELL first = *firstp; 3358 CELL last = *lastp; 3359 3360 TRACE(("ReHiliteText from %d.%d to %d.%d\n", 3361 first.row, first.col, last.row, last.col)); 3362 3363 if (first.row < 0) 3364 first.row = first.col = 0; 3365 else if (first.row > screen->max_row) 3366 return; /* nothing to do, since last.row >= first.row */ 3367 3368 if (last.row < 0) 3369 return; /* nothing to do, since first.row <= last.row */ 3370 else if (last.row > screen->max_row) { 3371 last.row = screen->max_row; 3372 last.col = MaxCols(screen); 3373 } 3374 if (isSameCELL(&first, &last)) 3375 return; 3376 3377 if (!isSameRow(&first, &last)) { /* do multiple rows */ 3378 if ((i = screen->max_col - first.col + 1) > 0) { /* first row */ 3379 ScrnRefresh(xw, first.row, first.col, 1, i, True); 3380 } 3381 if ((i = last.row - first.row - 1) > 0) { /* middle rows */ 3382 ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True); 3383 } 3384 if (last.col > 0 && last.row <= screen->max_row) { /* last row */ 3385 ScrnRefresh(xw, last.row, 0, 1, last.col, True); 3386 } 3387 } else { /* do single row */ 3388 ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True); 3389 } 3390} 3391 3392/* 3393 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col), and that both points are valid 3394 * (may have cell->row = screen->max_row+1, cell->col = 0). 3395 */ 3396static void 3397SaltTextAway(XtermWidget xw, 3398 CELL * cellc, 3399 CELL * cell, 3400 String * params, /* selections */ 3401 Cardinal num_params) 3402{ 3403 TScreen *screen = TScreenOf(xw); 3404 int i, j = 0; 3405 int eol; 3406 int tmp; 3407 Char *line; 3408 Char *lp; 3409 CELL first = *cellc; 3410 CELL last = *cell; 3411 3412 if (isSameRow(&first, &last) && first.col > last.col) { 3413 EXCHANGE(first.col, last.col, tmp); 3414 } 3415 3416 --last.col; 3417 /* first we need to know how long the string is before we can save it */ 3418 3419 if (isSameRow(&last, &first)) { 3420 j = Length(screen, first.row, first.col, last.col); 3421 } else { /* two cases, cut is on same line, cut spans multiple lines */ 3422 j += Length(screen, first.row, first.col, screen->max_col) + 1; 3423 for (i = first.row + 1; i < last.row; i++) 3424 j += Length(screen, i, 0, screen->max_col) + 1; 3425 if (last.col >= 0) 3426 j += Length(screen, last.row, 0, last.col); 3427 } 3428 3429 /* UTF-8 may require more space */ 3430 if_OPT_WIDE_CHARS(screen, { 3431 j *= 4; 3432 }); 3433 3434 /* now get some memory to save it in */ 3435 3436 if (screen->selection_size <= j) { 3437 if ((line = (Char *) malloc((size_t) j + 1)) == 0) 3438 SysError(ERROR_BMALLOC2); 3439 XtFree((char *) screen->selection_data); 3440 screen->selection_data = line; 3441 screen->selection_size = j + 1; 3442 } else { 3443 line = screen->selection_data; 3444 } 3445 3446 if ((line == 0) 3447 || (j < 0)) 3448 return; 3449 3450 line[j] = '\0'; /* make sure it is null terminated */ 3451 lp = line; /* lp points to where to save the text */ 3452 if (isSameRow(&last, &first)) { 3453 lp = SaveText(screen, last.row, first.col, last.col, lp, &eol); 3454 } else { 3455 lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol); 3456 if (eol) 3457 *lp++ = '\n'; /* put in newline at end of line */ 3458 for (i = first.row + 1; i < last.row; i++) { 3459 lp = SaveText(screen, i, 0, screen->max_col, lp, &eol); 3460 if (eol) 3461 *lp++ = '\n'; 3462 } 3463 if (last.col >= 0) 3464 lp = SaveText(screen, last.row, 0, last.col, lp, &eol); 3465 } 3466 *lp = '\0'; /* make sure we have end marked */ 3467 3468 TRACE(("Salted TEXT:%d:%s\n", (int) (lp - line), 3469 visibleChars(line, (unsigned) (lp - line)))); 3470 3471 screen->selection_length = (unsigned long) (lp - line); 3472 _OwnSelection(xw, params, num_params); 3473} 3474 3475#if OPT_PASTE64 3476void 3477ClearSelectionBuffer(TScreen * screen) 3478{ 3479 screen->selection_length = 0; 3480 screen->base64_count = 0; 3481} 3482 3483static void 3484AppendStrToSelectionBuffer(TScreen * screen, Char * text, size_t len) 3485{ 3486 if (len != 0) { 3487 int j = (int) (screen->selection_length + len); /* New length */ 3488 int k = j + (j >> 2) + 80; /* New size if we grow buffer: grow by ~50% */ 3489 if (j + 1 >= screen->selection_size) { 3490 if (!screen->selection_length) { 3491 /* New buffer */ 3492 Char *line; 3493 if ((line = (Char *) malloc((size_t) k)) == 0) 3494 SysError(ERROR_BMALLOC2); 3495 XtFree((char *) screen->selection_data); 3496 screen->selection_data = line; 3497 } else { 3498 /* Realloc buffer */ 3499 screen->selection_data = (Char *) 3500 realloc(screen->selection_data, 3501 (size_t) k); 3502 if (screen->selection_data == 0) 3503 SysError(ERROR_BMALLOC2); 3504 } 3505 screen->selection_size = k; 3506 } 3507 if (screen->selection_data != 0) { 3508 memcpy(screen->selection_data + screen->selection_length, text, len); 3509 screen->selection_length += len; 3510 screen->selection_data[screen->selection_length] = 0; 3511 } 3512 } 3513} 3514 3515void 3516AppendToSelectionBuffer(TScreen * screen, unsigned c) 3517{ 3518 unsigned six; 3519 Char ch; 3520 3521 /* Decode base64 character */ 3522 if (c >= 'A' && c <= 'Z') 3523 six = c - 'A'; 3524 else if (c >= 'a' && c <= 'z') 3525 six = c - 'a' + 26; 3526 else if (c >= '0' && c <= '9') 3527 six = c - '0' + 52; 3528 else if (c == '+') 3529 six = 62; 3530 else if (c == '/') 3531 six = 63; 3532 else 3533 return; 3534 3535 /* Accumulate bytes */ 3536 switch (screen->base64_count) { 3537 case 0: 3538 screen->base64_accu = six; 3539 screen->base64_count = 6; 3540 break; 3541 3542 case 2: 3543 ch = CharOf((screen->base64_accu << 6) + six); 3544 screen->base64_count = 0; 3545 AppendStrToSelectionBuffer(screen, &ch, (size_t) 1); 3546 break; 3547 3548 case 4: 3549 ch = CharOf((screen->base64_accu << 4) + (six >> 2)); 3550 screen->base64_accu = (six & 0x3); 3551 screen->base64_count = 2; 3552 AppendStrToSelectionBuffer(screen, &ch, (size_t) 1); 3553 break; 3554 3555 case 6: 3556 ch = CharOf((screen->base64_accu << 2) + (six >> 4)); 3557 screen->base64_accu = (six & 0xF); 3558 screen->base64_count = 4; 3559 AppendStrToSelectionBuffer(screen, &ch, (size_t) 1); 3560 break; 3561 } 3562} 3563 3564void 3565CompleteSelection(XtermWidget xw, String * args, Cardinal len) 3566{ 3567 TScreen *screen = TScreenOf(xw); 3568 3569 screen->base64_count = 0; 3570 screen->base64_accu = 0; 3571 _OwnSelection(xw, args, len); 3572} 3573#endif /* OPT_PASTE64 */ 3574 3575static Bool 3576_ConvertSelectionHelper(Widget w, 3577 Atom * type, 3578 XtPointer *value, 3579 unsigned long *length, 3580 int *format, 3581 int (*conversion_function) (Display *, 3582 char **, int, 3583 XICCEncodingStyle, 3584 XTextProperty *), 3585 XICCEncodingStyle conversion_style) 3586{ 3587 XtermWidget xw; 3588 3589 if ((xw = getXtermWidget(w)) != 0) { 3590 TScreen *screen = TScreenOf(xw); 3591 Display *dpy = XtDisplay(w); 3592 XTextProperty textprop; 3593 char *the_data = (char *) screen->selection_data; 3594 3595 if (conversion_function(dpy, &the_data, 1, 3596 conversion_style, 3597 &textprop) >= Success) { 3598 *value = (XtPointer) textprop.value; 3599 *length = textprop.nitems; 3600 *type = textprop.encoding; 3601 *format = textprop.format; 3602 return True; 3603 } 3604 } 3605 return False; 3606} 3607 3608static Boolean 3609SaveConvertedLength(XtPointer *target, unsigned long source) 3610{ 3611 Boolean result = False; 3612 3613 *target = XtMalloc(4); 3614 if (*target != 0) { 3615 result = True; 3616 if (sizeof(unsigned long) == 4) { 3617 *(unsigned long *) *target = source; 3618 } else if (sizeof(unsigned) == 4) { 3619 *(unsigned *) *target = (unsigned) source; 3620 } else if (sizeof(unsigned short) == 4) { 3621 *(unsigned short *) *target = (unsigned short) source; 3622 } else { 3623 /* FIXME - does this depend on byte-order? */ 3624 unsigned long temp = source; 3625 memcpy((char *) *target, 3626 ((char *) &temp) + sizeof(temp) - 4, 3627 (size_t) 4); 3628 } 3629 } 3630 return result; 3631} 3632 3633static Boolean 3634ConvertSelection(Widget w, 3635 Atom * selection, 3636 Atom * target, 3637 Atom * type, 3638 XtPointer *value, 3639 unsigned long *length, 3640 int *format) 3641{ 3642 Display *dpy = XtDisplay(w); 3643 TScreen *screen; 3644 Bool result = False; 3645 3646 XtermWidget xw; 3647 3648 if ((xw = getXtermWidget(w)) == 0) 3649 return False; 3650 3651 screen = TScreenOf(xw); 3652 3653 if (screen->selection_data == NULL) 3654 return False; /* can this happen? */ 3655 3656 TRACE(("ConvertSelection %s\n", 3657 visibleSelectionTarget(dpy, *target))); 3658 3659 if (*target == XA_TARGETS(dpy)) { 3660 Atom *allocP; 3661 Atom *targetP; 3662 Atom *std_targets; 3663 XPointer std_return = 0; 3664 unsigned long std_length; 3665 3666 if (XmuConvertStandardSelection(w, screen->selection_time, selection, 3667 target, type, &std_return, 3668 &std_length, format)) { 3669 Atom *my_targets = _SelectionTargets(w); 3670 3671 TRACE(("XmuConvertStandardSelection - success\n")); 3672 std_targets = (Atom *) (std_return); 3673 *length = std_length + 6; 3674 3675 targetP = (Atom *) XtMalloc((Cardinal) (sizeof(Atom) * (*length))); 3676 allocP = targetP; 3677 3678 *value = (XtPointer) targetP; 3679 3680 while (*my_targets != None) { 3681 *targetP++ = *my_targets++; 3682 } 3683 *targetP++ = XA_LENGTH(dpy); 3684 *targetP++ = XA_LIST_LENGTH(dpy); 3685 3686 *length = std_length + (unsigned long) (targetP - allocP); 3687 3688 memcpy(targetP, std_targets, sizeof(Atom) * std_length); 3689 XtFree((char *) std_targets); 3690 *type = XA_ATOM; 3691 *format = 32; 3692 result = True; 3693 } else { 3694 TRACE(("XmuConvertStandardSelection - failed\n")); 3695 } 3696 } 3697#if OPT_WIDE_CHARS 3698 else if (screen->wide_chars && *target == XA_STRING) { 3699 result = 3700 _ConvertSelectionHelper(w, 3701 type, value, length, format, 3702 Xutf8TextListToTextProperty, 3703 XStringStyle); 3704 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 3705 } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) { 3706 result = 3707 _ConvertSelectionHelper(w, 3708 type, value, length, format, 3709 Xutf8TextListToTextProperty, 3710 XUTF8StringStyle); 3711 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 3712 } else if (screen->wide_chars && *target == XA_TEXT(dpy)) { 3713 result = 3714 _ConvertSelectionHelper(w, 3715 type, value, length, format, 3716 Xutf8TextListToTextProperty, 3717 XStdICCTextStyle); 3718 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 3719 } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) { 3720 result = 3721 _ConvertSelectionHelper(w, 3722 type, value, length, format, 3723 Xutf8TextListToTextProperty, 3724 XCompoundTextStyle); 3725 TRACE(("...Xutf8TextListToTextProperty:%d\n", result)); 3726 } 3727#endif 3728 3729 else if (*target == XA_STRING) { /* not wide_chars */ 3730 /* We can only reach this point if the selection requestor 3731 requested STRING before any of TEXT, COMPOUND_TEXT or 3732 UTF8_STRING. We therefore assume that the requestor is not 3733 properly internationalised, and dump raw eight-bit data 3734 with no conversion into the selection. Yes, this breaks 3735 the ICCCM in non-Latin-1 locales. */ 3736 *type = XA_STRING; 3737 *value = (XtPointer) screen->selection_data; 3738 *length = screen->selection_length; 3739 *format = 8; 3740 result = True; 3741 TRACE(("...raw 8-bit data:%d\n", result)); 3742 } else if (*target == XA_TEXT(dpy)) { /* not wide_chars */ 3743 result = 3744 _ConvertSelectionHelper(w, 3745 type, value, length, format, 3746 XmbTextListToTextProperty, 3747 XStdICCTextStyle); 3748 TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result)); 3749 } else if (*target == XA_COMPOUND_TEXT(dpy)) { /* not wide_chars */ 3750 result = 3751 _ConvertSelectionHelper(w, 3752 type, value, length, format, 3753 XmbTextListToTextProperty, 3754 XCompoundTextStyle); 3755 TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result)); 3756 } 3757#ifdef X_HAVE_UTF8_STRING 3758 else if (*target == XA_UTF8_STRING(dpy)) { /* not wide_chars */ 3759 result = 3760 _ConvertSelectionHelper(w, 3761 type, value, length, format, 3762 XmbTextListToTextProperty, 3763 XUTF8StringStyle); 3764 TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result)); 3765 } 3766#endif 3767 else if (*target == XA_LIST_LENGTH(dpy)) { 3768 result = SaveConvertedLength(value, (unsigned long) 1); 3769 *type = XA_INTEGER; 3770 *length = 1; 3771 *format = 32; 3772 TRACE(("...list of values:%d\n", result)); 3773 } else if (*target == XA_LENGTH(dpy)) { 3774 /* This value is wrong if we have UTF-8 text */ 3775 result = SaveConvertedLength(value, screen->selection_length); 3776 *type = XA_INTEGER; 3777 *length = 1; 3778 *format = 32; 3779 TRACE(("...list of values:%d\n", result)); 3780 } else if (XmuConvertStandardSelection(w, 3781 screen->selection_time, selection, 3782 target, type, (XPointer *) value, 3783 length, format)) { 3784 result = True; 3785 TRACE(("...XmuConvertStandardSelection:%d\n", result)); 3786 } 3787 3788 /* else */ 3789 return (Boolean) result; 3790} 3791 3792static void 3793LoseSelection(Widget w, Atom * selection) 3794{ 3795 TScreen *screen; 3796 Atom *atomP; 3797 Cardinal i; 3798 3799 XtermWidget xw; 3800 3801 if ((xw = getXtermWidget(w)) == 0) 3802 return; 3803 3804 screen = TScreenOf(xw); 3805 for (i = 0, atomP = screen->selection_atoms; 3806 i < screen->selection_count; i++, atomP++) { 3807 if (*selection == *atomP) 3808 *atomP = (Atom) 0; 3809 if (CutBuffer(*atomP) >= 0) { 3810 *atomP = (Atom) 0; 3811 } 3812 } 3813 3814 for (i = screen->selection_count; i; i--) { 3815 if (screen->selection_atoms[i - 1] != 0) 3816 break; 3817 } 3818 screen->selection_count = i; 3819 3820 for (i = 0, atomP = screen->selection_atoms; 3821 i < screen->selection_count; i++, atomP++) { 3822 if (*atomP == (Atom) 0) { 3823 *atomP = screen->selection_atoms[--screen->selection_count]; 3824 } 3825 } 3826 3827 if (screen->selection_count == 0) 3828 TrackText(xw, &zeroCELL, &zeroCELL); 3829} 3830 3831/* ARGSUSED */ 3832static void 3833SelectionDone(Widget w GCC_UNUSED, 3834 Atom * selection GCC_UNUSED, 3835 Atom * target GCC_UNUSED) 3836{ 3837 /* empty proc so Intrinsics know we want to keep storage */ 3838} 3839 3840static void 3841_OwnSelection(XtermWidget xw, 3842 String * selections, 3843 Cardinal count) 3844{ 3845 TScreen *screen = TScreenOf(xw); 3846 Atom *atoms = screen->selection_atoms; 3847 Cardinal i; 3848 Bool have_selection = False; 3849 3850 if (count == 0) 3851 return; 3852 if (screen->selection_length == 0) 3853 return; 3854 3855 TRACE(("_OwnSelection count %d\n", count)); 3856 selections = MapSelections(xw, selections, count); 3857 3858 if (count > screen->sel_atoms_size) { 3859 XtFree((char *) atoms); 3860 atoms = (Atom *) XtMalloc((Cardinal) (count * sizeof(Atom))); 3861 screen->selection_atoms = atoms; 3862 screen->sel_atoms_size = count; 3863 } 3864 XmuInternStrings(XtDisplay((Widget) xw), selections, count, atoms); 3865 for (i = 0; i < count; i++) { 3866 int cutbuffer = CutBuffer(atoms[i]); 3867 if (cutbuffer >= 0) { 3868 unsigned long limit = 3869 (unsigned long) (4 * XMaxRequestSize(XtDisplay((Widget) xw)) - 32); 3870 if (screen->selection_length > limit) { 3871 TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n", 3872 screen->selection_length, cutbuffer)); 3873 fprintf(stderr, 3874 "%s: selection too big (%lu bytes), not storing in CUT_BUFFER%d\n", 3875 xterm_name, screen->selection_length, cutbuffer); 3876 } else { 3877 /* This used to just use the UTF-8 data, which was totally 3878 * broken as not even the corresponding paste code in Xterm 3879 * understood this! So now it converts to Latin1 first. 3880 * Robert Brady, 2000-09-05 3881 */ 3882 unsigned long length = screen->selection_length; 3883 Char *data = screen->selection_data; 3884 if_OPT_WIDE_CHARS((screen), { 3885 data = UTF8toLatin1(screen, data, length, &length); 3886 }); 3887 TRACE(("XStoreBuffer(%d)\n", cutbuffer)); 3888 XStoreBuffer(XtDisplay((Widget) xw), 3889 (char *) data, 3890 (int) length, 3891 cutbuffer); 3892 } 3893 } else if (!screen->replyToEmacs) { 3894 have_selection |= 3895 XtOwnSelection((Widget) xw, atoms[i], 3896 screen->selection_time, 3897 ConvertSelection, LoseSelection, SelectionDone); 3898 } 3899 } 3900 if (!screen->replyToEmacs) 3901 screen->selection_count = count; 3902 if (!have_selection) 3903 TrackText(xw, &zeroCELL, &zeroCELL); 3904} 3905 3906static void 3907ResetSelectionState(TScreen * screen) 3908{ 3909 screen->selection_count = 0; 3910 screen->startH = zeroCELL; 3911 screen->endH = zeroCELL; 3912} 3913 3914void 3915DisownSelection(XtermWidget xw) 3916{ 3917 TScreen *screen = TScreenOf(xw); 3918 Atom *atoms = screen->selection_atoms; 3919 Cardinal count = screen->selection_count; 3920 Cardinal i; 3921 3922 TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n", 3923 count, 3924 screen->startH.row, 3925 screen->startH.col, 3926 screen->endH.row, 3927 screen->endH.col)); 3928 3929 for (i = 0; i < count; i++) { 3930 int cutbuffer = CutBuffer(atoms[i]); 3931 if (cutbuffer < 0) { 3932 XtDisownSelection((Widget) xw, atoms[i], 3933 screen->selection_time); 3934 } 3935 } 3936 /* 3937 * If none of the callbacks via XtDisownSelection() reset highlighting 3938 * do it now. 3939 */ 3940 if (ScrnHaveSelection(screen)) { 3941 /* save data which will be reset */ 3942 CELL first = screen->startH; 3943 CELL last = screen->endH; 3944 3945 ResetSelectionState(screen); 3946 ReHiliteText(xw, &first, &last); 3947 } else { 3948 ResetSelectionState(screen); 3949 } 3950} 3951 3952void 3953UnhiliteSelection(XtermWidget xw) 3954{ 3955 TScreen *screen = TScreenOf(xw); 3956 3957 if (ScrnHaveSelection(screen)) { 3958 CELL first = screen->startH; 3959 CELL last = screen->endH; 3960 3961 screen->startH = zeroCELL; 3962 screen->endH = zeroCELL; 3963 ReHiliteText(xw, &first, &last); 3964 } 3965} 3966 3967/* returns number of chars in line from scol to ecol out */ 3968/* ARGSUSED */ 3969static int 3970Length(TScreen * screen, 3971 int row, 3972 int scol, 3973 int ecol) 3974{ 3975 LineData *ld = GET_LINEDATA(screen, row); 3976 int lastcol = LastTextCol(screen, ld, row); 3977 3978 if (ecol > lastcol) 3979 ecol = lastcol; 3980 return (ecol - scol + 1); 3981} 3982 3983/* copies text into line, preallocated */ 3984static Char * 3985SaveText(TScreen * screen, 3986 int row, 3987 int scol, 3988 int ecol, 3989 Char * lp, /* pointer to where to put the text */ 3990 int *eol) 3991{ 3992 LineData *ld; 3993 int i = 0; 3994 unsigned c; 3995 Char *result = lp; 3996#if OPT_WIDE_CHARS 3997 unsigned previous = 0; 3998#endif 3999 4000 ld = GET_LINEDATA(screen, row); 4001 i = Length(screen, row, scol, ecol); 4002 ecol = scol + i; 4003#if OPT_DEC_CHRSET 4004 if (CSET_DOUBLE(GetLineDblCS(ld))) { 4005 scol = (scol + 0) / 2; 4006 ecol = (ecol + 1) / 2; 4007 } 4008#endif 4009 *eol = !LineTstWrapped(ld); 4010 for (i = scol; i < ecol; i++) { 4011 assert(i < ld->lineSize); 4012 c = E2A(ld->charData[i]); 4013#if OPT_WIDE_CHARS 4014 /* We want to strip out every occurrence of HIDDEN_CHAR AFTER a 4015 * wide character. 4016 */ 4017 if (c == HIDDEN_CHAR && isWide((int) previous)) { 4018 previous = c; 4019 /* Combining characters attached to double-width characters 4020 are in memory attached to the HIDDEN_CHAR */ 4021 if_OPT_WIDE_CHARS(screen, { 4022 if (screen->utf8_mode != uFalse) { 4023 unsigned ch; 4024 size_t off; 4025 for_each_combData(off, ld) { 4026 ch = ld->combData[off][i]; 4027 if (ch == 0) 4028 break; 4029 lp = convertToUTF8(lp, ch); 4030 } 4031 } 4032 }); 4033 continue; 4034 } 4035 previous = c; 4036 if (screen->utf8_mode != uFalse) { 4037 lp = convertToUTF8(lp, (c != 0) ? c : ' '); 4038 if_OPT_WIDE_CHARS(screen, { 4039 unsigned ch; 4040 size_t off; 4041 for_each_combData(off, ld) { 4042 ch = ld->combData[off][i]; 4043 if (ch == 0) 4044 break; 4045 lp = convertToUTF8(lp, ch); 4046 } 4047 }); 4048 } else 4049#endif 4050 { 4051 if (c == 0) { 4052 c = E2A(' '); 4053 } else if (c < E2A(' ')) { 4054 c = DECtoASCII(c); 4055 } else if (c == 0x7f) { 4056 c = 0x5f; 4057 } 4058 *lp++ = CharOf(A2E(c)); 4059 } 4060 if (c != E2A(' ')) 4061 result = lp; 4062 } 4063 4064 /* 4065 * If requested, trim trailing blanks from selected lines. Do not do this 4066 * if the line is wrapped. 4067 */ 4068 if (!*eol || !screen->trim_selection) 4069 result = lp; 4070 4071 return (result); 4072} 4073 4074/* 32 + following 7-bit word: 4075 4076 1:0 Button no: 0, 1, 2. 3=release. 4077 2 shift 4078 3 meta 4079 4 ctrl 4080 5 set for motion notify 4081 6 set for wheel 4082*/ 4083 4084/* Position: 32 - 255. */ 4085 4086static Char 4087BtnCode(XButtonEvent * event, int button) 4088{ 4089 int result = (int) (32 + (KeyState(event->state) << 2)); 4090 4091 if (button < 0 || button > 5) { 4092 result += 3; 4093 } else { 4094 if (button > 3) 4095 result += (64 - 4); 4096 if (event->type == MotionNotify) 4097 result += 32; 4098 result += button; 4099 } 4100 return CharOf(result); 4101} 4102 4103static void 4104EditorButton(XtermWidget xw, XButtonEvent * event) 4105{ 4106 TScreen *screen = TScreenOf(xw); 4107 int pty = screen->respond; 4108 int mouse_limit = screen->ext_mode_mouse ? EXT_MOUSE_LIMIT : MOUSE_LIMIT; 4109 Char line[10]; 4110 int row, col; 4111 int button; 4112 unsigned count = 0; 4113 Boolean changed = True; 4114 4115 /* If button event, get button # adjusted for DEC compatibility */ 4116 button = (int) (event->button - 1); 4117 if (button >= 3) 4118 button++; 4119 4120 /* Compute character position of mouse pointer */ 4121 row = (event->y - screen->border) / FontHeight(screen); 4122 col = (event->x - OriginX(screen)) / FontWidth(screen); 4123 4124 /* Limit to screen dimensions */ 4125 if (row < 0) 4126 row = 0; 4127 else if (row > screen->max_row) 4128 row = screen->max_row; 4129 4130 if (col < 0) 4131 col = 0; 4132 else if (col > screen->max_col) 4133 col = screen->max_col; 4134 4135 /* Limit to representable mouse dimensions */ 4136 if (row > mouse_limit) 4137 row = mouse_limit; 4138 if (col > mouse_limit) 4139 col = mouse_limit; 4140 4141 /* Build key sequence starting with \E[M */ 4142 if (screen->control_eight_bits) { 4143 line[count++] = ANSI_CSI; 4144 } else { 4145 line[count++] = ANSI_ESC; 4146 line[count++] = '['; 4147 } 4148#if OPT_SCO_FUNC_KEYS 4149 if (xw->keyboard.type == keyboardIsSCO) { 4150 /* 4151 * SCO function key F1 is \E[M, which would conflict with xterm's 4152 * normal kmous. 4153 */ 4154 line[count++] = '>'; 4155 } 4156#endif 4157 line[count++] = 'M'; 4158 4159 /* Add event code to key sequence */ 4160 if (screen->send_mouse_pos == X10_MOUSE) { 4161 line[count++] = CharOf(' ' + button); 4162 } else { 4163 /* Button-Motion events */ 4164 switch (event->type) { 4165 case ButtonPress: 4166 line[count++] = BtnCode(event, screen->mouse_button = button); 4167 break; 4168 case ButtonRelease: 4169 /* 4170 * Wheel mouse interface generates release-events for buttons 4171 * 4 and 5, coded here as 3 and 4 respectively. We change the 4172 * release for buttons 1..3 to a -1. 4173 */ 4174 if (button < 3) 4175 button = -1; 4176 line[count++] = BtnCode(event, screen->mouse_button = button); 4177 break; 4178 case MotionNotify: 4179 /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion 4180 * events only if character cell has changed. 4181 */ 4182 if ((row == screen->mouse_row) 4183 && (col == screen->mouse_col)) { 4184 changed = False; 4185 } else { 4186 line[count++] = BtnCode(event, screen->mouse_button); 4187 } 4188 break; 4189 default: 4190 changed = False; 4191 break; 4192 } 4193 } 4194 4195 if (changed) { 4196 screen->mouse_row = row; 4197 screen->mouse_col = col; 4198 4199 TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1])); 4200 4201 /* Add pointer position to key sequence */ 4202 count = EmitMousePosition(screen, line, count, col); 4203 count = EmitMousePosition(screen, line, count, row); 4204 4205 /* Transmit key sequence to process running under xterm */ 4206 v_write(pty, line, count); 4207 } 4208 return; 4209} 4210 4211#if OPT_FOCUS_EVENT 4212void 4213SendFocusButton(XtermWidget xw, XFocusChangeEvent * event) 4214{ 4215 TScreen *screen = TScreenOf(xw); 4216 4217 if (screen->send_focus_pos) { 4218 ANSI reply; 4219 4220 memset(&reply, 0, sizeof(reply)); 4221 reply.a_type = ANSI_CSI; 4222 4223#if OPT_SCO_FUNC_KEYS 4224 if (xw->keyboard.type == keyboardIsSCO) { 4225 reply.a_pintro = '>'; 4226 } 4227#endif 4228 reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O'); 4229 unparseseq(xw, &reply); 4230 } 4231 return; 4232} 4233#endif /* OPT_FOCUS_EVENT */ 4234