1 /* $XTermId: button.c,v 1.637 2021/08/12 00:33:06 tom Exp $ */
2
3 /*
4 * Copyright 1999-2020,2021 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 /*
56 button.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 <ctype.h>
66 #include <assert.h>
67
68 #include <X11/Xatom.h>
69 #include <X11/Xmu/Atoms.h>
70 #include <X11/Xmu/StdSel.h>
71
72 #include <xutf8.h>
73 #include <fontutils.h>
74
75 #include <data.h>
76 #include <error.h>
77 #include <menu.h>
78 #include <charclass.h>
79 #include <xstrings.h>
80
81 #if OPT_SELECT_REGEX
82 #if defined(HAVE_PCRE2POSIX_H)
83 #include <pcre2posix.h>
84
85 /* pcre2 used to provide its "POSIX" entrypoints using the same names as the
86 * standard ones in the C runtime, but that never worked because the linker
87 * would use the C runtime. Debian patched the library to fix this symbol
88 * conflict, but overlooked the header file, and Debian's patch was made
89 * obsolete when pcre2 was changed early in 2019 to provide different names.
90 *
91 * Here is a workaround to make the older version of Debian's package work.
92 */
93 #if !defined(PCRE2regcomp) && defined(HAVE_PCRE2REGCOMP)
94
95 #undef regcomp
96 #undef regexec
97 #undef regfree
98
99 #ifdef __cplusplus
100 extern "C" {
101 #endif
102 PCRE2POSIX_EXP_DECL int PCRE2regcomp(regex_t *, const char *, int);
103 PCRE2POSIX_EXP_DECL int PCRE2regexec(const regex_t *, const char *, size_t,
104 regmatch_t *, int);
105 PCRE2POSIX_EXP_DECL void PCRE2regfree(regex_t *);
106 #ifdef __cplusplus
107 } /* extern "C" */
108 #endif
109 #define regcomp(r,s,n) PCRE2regcomp(r,s,n)
110 #define regexec(r,s,n,m,x) PCRE2regexec(r,s,n,m,x)
111 #define regfree(r) PCRE2regfree(r)
112 #endif
113 /* end workaround... */
114 #elif defined(HAVE_PCREPOSIX_H)
115 #include <pcreposix.h>
116 #else /* POSIX regex.h */
117 #include <sys/types.h>
118 #include <regex.h>
119 #endif
120 #endif /* OPT_SELECT_REGEX */
121
122 #ifdef HAVE_X11_TRANSLATEI_H
123 #include <X11/ConvertI.h>
124 #include <X11/TranslateI.h>
125 #else
126 extern String _XtPrintXlations(Widget w,
127 XtTranslations xlations,
128 Widget accelWidget,
129 _XtBoolean includeRHS);
130 #endif
131
132 #define PRIMARY_NAME "PRIMARY"
133 #define CLIPBOARD_NAME "CLIPBOARD"
134 #define SECONDARY_NAME "SECONDARY"
135
136 #define AtomToSelection(d,n) \
137 (((n) == XA_CLIPBOARD(d)) \
138 ? CLIPBOARD_CODE \
139 : (((n) == XA_SECONDARY) \
140 ? SECONDARY_CODE \
141 : PRIMARY_CODE))
142
143 #define isSelectionCode(n) ((n) >= PRIMARY_CODE)
144 #define CutBufferToCode(n) ((n) + MAX_SELECTION_CODES)
145 #define okSelectionCode(n) (isSelectionCode(n) ? (n) : PRIMARY_CODE)
146
147 #if OPT_WIDE_CHARS
148 #include <ctype.h>
149 #include <wcwidth.h>
150 #else
151 #define CharacterClass(value) \
152 charClass[(value) & (int)((sizeof(charClass)/sizeof(charClass[0]))-1)]
153 #endif
154
155 /*
156 * We'll generally map rows to indices when doing selection.
157 * Simplify that with a macro.
158 *
159 * Note that ROW2INX() is safe to use with auto increment/decrement for
160 * the row expression since that is evaluated once.
161 */
162 #define GET_LINEDATA(screen, row) \
163 getLineData(screen, ROW2INX(screen, row))
164
165 #define MaxMouseBtn 5
166
167 #define IsBtnEvent(event) ((event)->type == ButtonPress || (event)->type == ButtonRelease)
168 #define IsKeyEvent(event) ((event)->type == KeyPress || (event)->type == KeyRelease)
169
170 #define Coordinate(s,c) ((c)->row * MaxCols(s) + (c)->col)
171
172 static const CELL zeroCELL =
173 {0, 0};
174
175 #if OPT_DEC_LOCATOR
176 static Bool SendLocatorPosition(XtermWidget xw, XButtonEvent *event);
177 static void CheckLocatorPosition(XtermWidget xw, XButtonEvent *event);
178 #endif /* OPT_DEC_LOCATOR */
179
180 /* Multi-click handling */
181 #if OPT_READLINE
182 static Time lastButtonDownTime = 0;
183 static int ExtendingSelection = 0;
184 static Time lastButton3UpTime = 0;
185 static Time lastButton3DoubleDownTime = 0;
186 static CELL lastButton3; /* At the release time */
187 #endif /* OPT_READLINE */
188
189 static Char *SaveText(TScreen *screen, int row, int scol, int ecol,
190 Char *lp, int *eol);
191 static int Length(TScreen *screen, int row, int scol, int ecol);
192 static void ComputeSelect(XtermWidget xw, CELL *startc, CELL *endc, Bool
193 extend, Bool normal);
194 static void EditorButton(XtermWidget xw, XButtonEvent *event);
195 static void EndExtend(XtermWidget w, XEvent *event, String *params, Cardinal
196 num_params, Bool use_cursor_loc);
197 static void ExtendExtend(XtermWidget xw, const CELL *cell);
198 static void PointToCELL(TScreen *screen, int y, int x, CELL *cell);
199 static void ReHiliteText(XtermWidget xw, CELL *first, CELL *last);
200 static void SaltTextAway(XtermWidget xw, int which, CELL *cellc, CELL *cell);
201 static void SelectSet(XtermWidget xw, XEvent *event, String *params, Cardinal num_params);
202 static void SelectionReceived PROTO_XT_SEL_CB_ARGS;
203 static void StartSelect(XtermWidget xw, const CELL *cell);
204 static void TrackDown(XtermWidget xw, XButtonEvent *event);
205 static void TrackText(XtermWidget xw, const CELL *first, const CELL *last);
206 static void UnHiliteText(XtermWidget xw);
207 static void _OwnSelection(XtermWidget xw, String *selections, Cardinal count);
208 static void do_select_end(XtermWidget xw, XEvent *event, String *params,
209 Cardinal *num_params, Bool use_cursor_loc);
210
211 #define MOUSE_LIMIT (255 - 32)
212
213 /* Send SET_EXT_SIZE_MOUSE to enable offsets up to EXT_MOUSE_LIMIT */
214 #define EXT_MOUSE_LIMIT (2047 - 32)
215 #define EXT_MOUSE_START (127 - 32)
216
217 static int
MouseLimit(TScreen * screen)218 MouseLimit(TScreen *screen)
219 {
220 int mouse_limit;
221
222 switch (screen->extend_coords) {
223 default:
224 mouse_limit = MOUSE_LIMIT;
225 break;
226 case SET_EXT_MODE_MOUSE:
227 mouse_limit = EXT_MOUSE_LIMIT;
228 break;
229 case SET_SGR_EXT_MODE_MOUSE:
230 case SET_URXVT_EXT_MODE_MOUSE:
231 case SET_PIXEL_POSITION_MOUSE:
232 mouse_limit = -1;
233 break;
234 }
235 return mouse_limit;
236 }
237
238 static unsigned
EmitMousePosition(TScreen * screen,Char line[],unsigned count,int value)239 EmitMousePosition(TScreen *screen, Char line[], unsigned count, int value)
240 {
241 int mouse_limit = MouseLimit(screen);
242
243 /*
244 * Add pointer position to key sequence
245 *
246 * In extended mode we encode large positions as two-byte UTF-8.
247 *
248 * NOTE: historically, it was possible to emit 256, which became
249 * zero by truncation to 8 bits. While this was arguably a bug,
250 * it's also somewhat useful as a past-end marker. We preserve
251 * this behavior for both normal and extended mouse modes.
252 */
253 switch (screen->extend_coords) {
254 default:
255 if (value == mouse_limit) {
256 line[count++] = CharOf(0);
257 } else {
258 line[count++] = CharOf(' ' + value + 1);
259 }
260 break;
261 case SET_EXT_MODE_MOUSE:
262 if (value == mouse_limit) {
263 line[count++] = CharOf(0);
264 } else if (value < EXT_MOUSE_START) {
265 line[count++] = CharOf(' ' + value + 1);
266 } else {
267 value += ' ' + 1;
268 line[count++] = CharOf(0xC0 + (value >> 6));
269 line[count++] = CharOf(0x80 + (value & 0x3F));
270 }
271 break;
272 case SET_SGR_EXT_MODE_MOUSE:
273 case SET_URXVT_EXT_MODE_MOUSE:
274 case SET_PIXEL_POSITION_MOUSE:
275 count += (unsigned) sprintf((char *) line + count, "%d", value + 1);
276 break;
277 }
278 return count;
279 }
280
281 static unsigned
EmitMousePositionSeparator(TScreen * screen,Char line[],unsigned count)282 EmitMousePositionSeparator(TScreen *screen, Char line[], unsigned count)
283 {
284 switch (screen->extend_coords) {
285 case SET_SGR_EXT_MODE_MOUSE:
286 case SET_URXVT_EXT_MODE_MOUSE:
287 case SET_PIXEL_POSITION_MOUSE:
288 line[count++] = ';';
289 break;
290 }
291 return count;
292 }
293
294 enum {
295 scanMods,
296 scanKey,
297 scanColon,
298 scanFunc,
299 scanArgs
300 };
301
302 #if OPT_TRACE > 1
303 static const char *
visibleScan(int mode)304 visibleScan(int mode)
305 {
306 const char *result = "?";
307 #define DATA(name) case name: result = #name; break
308 switch (mode) {
309 DATA(scanMods);
310 DATA(scanKey);
311 DATA(scanColon);
312 DATA(scanFunc);
313 DATA(scanArgs);
314 }
315 #undef DATA
316 return result;
317 }
318 #endif
319
320 #define L_BRACK '<'
321 #define R_BRACK '>'
322 #define L_PAREN '('
323 #define R_PAREN ')'
324
325 static char *
scanTrans(char * source,int * this_is,int * next_is,unsigned * first,unsigned * last)326 scanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
327 {
328 char ch;
329 char *target = source;
330
331 *first = *last = 0;
332 if (IsEmpty(target)) {
333 target = 0;
334 } else {
335 do {
336 while (IsSpace(*target))
337 target++;
338 *first = (unsigned) (target - source);
339 switch (*this_is = *next_is) {
340 case scanMods:
341 while ((ch = *target)) {
342 if (IsSpace(ch)) {
343 break;
344 } else if (ch == L_BRACK) {
345 *next_is = scanKey;
346 break;
347 } else if (ch == ':') {
348 *next_is = scanColon;
349 break;
350 } else if (ch == '~' && target != source) {
351 break;
352 }
353 target++;
354 }
355 break;
356 case scanKey:
357 while ((ch = *target)) {
358 if (IsSpace(ch)) {
359 break;
360 } else if (ch == ':') {
361 *next_is = scanColon;
362 break;
363 }
364 target++;
365 if (ch == R_BRACK)
366 break;
367 }
368 break;
369 case scanColon:
370 *next_is = scanFunc;
371 target++;
372 break;
373 case scanFunc:
374 while ((ch = *target)) {
375 if (IsSpace(ch)) {
376 break;
377 } else if (ch == L_PAREN) {
378 *next_is = scanArgs;
379 break;
380 }
381 target++;
382 }
383 break;
384 case scanArgs:
385 while ((ch = *target)) {
386 if (ch == R_PAREN) {
387 target++;
388 *next_is = scanFunc;
389 break;
390 }
391 target++;
392 }
393 break;
394 }
395 *last = (unsigned) (target - source);
396 if (*target == '\n') {
397 *next_is = scanMods;
398 target++;
399 }
400 } while (*first == *last);
401 }
402 return target;
403 }
404
405 void
xtermButtonInit(XtermWidget xw)406 xtermButtonInit(XtermWidget xw)
407 {
408 Widget w = (Widget) xw;
409 XErrorHandler save = XSetErrorHandler(ignore_x11_error);
410 XtTranslations xlations;
411 Widget xcelerat;
412 String result;
413
414 XtVaGetValues(w,
415 XtNtranslations, &xlations,
416 XtNaccelerators, &xcelerat,
417 (XtPointer) 0);
418 result = _XtPrintXlations(w, xlations, xcelerat, True);
419 if (result) {
420 static const char *table[] =
421 {
422 "insert-selection",
423 "select-end",
424 "select-extend",
425 "select-start",
426 "start-extend",
427 };
428 char *data = x_strdup(result);
429 char *next;
430 int state = scanMods;
431 int state2 = scanMods;
432 unsigned first;
433 unsigned last;
434 int have_button = -1;
435 Bool want_button = False;
436 Bool have_shift = False;
437 unsigned allowed = 0;
438 unsigned disallow = 0;
439
440 TRACE(("xtermButtonInit length %ld\n", (long) strlen(result)));
441 xw->keyboard.print_translations = data;
442 while ((next = scanTrans(data, &state, &state2, &first, &last)) != 0) {
443 unsigned len = (last - first);
444 TRACE2(("parse %s:%d..%d '%.*s'\n",
445 visibleScan(state), first, last,
446 len, data + first));
447 if (state == scanMods) {
448 if (len > 1 && data[first] == '~') {
449 len--;
450 first++;
451 }
452 if (len == 7 && !x_strncasecmp(data + first, "button", len - 1)) {
453 have_button = data[first + 6] - '0';
454 } else if (len == 5 && !x_strncasecmp(data + first, "shift", len)) {
455 have_shift = True;
456 }
457 } else if (state == scanKey) {
458 if (!x_strncasecmp(data + first, "<buttonpress>", len) ||
459 !x_strncasecmp(data + first, "<buttonrelease>", len)) {
460 want_button = True;
461 } else if (want_button) {
462 have_button = data[first] - '0';
463 want_button = False;
464 }
465 } else if (state == scanFunc && have_button > 0) {
466 Cardinal n;
467 unsigned bmask = 1U << (have_button - 1);
468 for (n = 0; n < XtNumber(table); ++n) {
469 if (!x_strncasecmp(table[n], data + first, len)) {
470 TRACE(("...button %d: %s%s\n",
471 have_button, table[n],
472 have_shift ? " (disallow)" : ""));
473 if (have_shift)
474 disallow |= bmask;
475 else
476 allowed |= bmask;
477 break;
478 }
479 }
480 }
481 if (state2 == scanMods && state >= scanColon) {
482 have_button = -1;
483 want_button = False;
484 have_shift = False;
485 }
486 state = state2;
487 data = next;
488 }
489 XFree((char *) result);
490 xw->keyboard.shift_buttons = allowed & ~disallow;
491 #if OPT_TRACE
492 if (xw->keyboard.shift_buttons) {
493 int button = 0;
494 unsigned mask = xw->keyboard.shift_buttons;
495 TRACE(("...Buttons used for selection that can be overridden:"));
496 while (mask != 0) {
497 ++button;
498 if ((mask & 1) != 0)
499 TRACE((" %d", button));
500 mask >>= 1;
501 }
502 TRACE(("\n"));
503 } else {
504 TRACE(("...No buttons used with selection can be overridden\n"));
505 }
506 #endif
507 }
508 XSetErrorHandler(save);
509 }
510
511 /*
512 * Shift and control are regular X11 modifiers, but meta is not:
513 * + X10 (which had no xmodmap utility) had a meta mask, but X11 did not.
514 * + X11R1 introduced xmodmap, along with the current set of modifier masks.
515 * The meta key has been assumed to be mod1 since X11R1.
516 * The initial xterm logic in X11 was different, but gave the same result.
517 * + X11R2 modified xterm was to eliminate the X10 table which provided part of
518 * the meta logic.
519 * + X11R3 modified Xt, making Meta_L and Meta_R assignable via xmodmap, and
520 * equating Alt with Meta. Neither Alt/Meta are modifiers, but Alt is more
521 * likely to be on the keyboard. This release also added keymap tables for
522 * the server; Meta was used frequently in HP keymaps, which were the most
523 * extensive set of keymaps.
524 * + X11R4 mentions Meta in the ICCCM, stating that if Meta_L or Meta_R are
525 * found in the keysyms for a given modifier, that the client should use
526 * that modifier.
527 *
528 * This function follows the ICCCM, picking the modifier which contains the
529 * Meta_L/Meta_R keysyms (if available), falling back to the Alt_L/Alt_R
530 * (as per X11R3), and ultimately to mod1 (per X11R1).
531 */
532 static unsigned
MetaMask(XtermWidget xw)533 MetaMask(XtermWidget xw)
534 {
535 #if OPT_NUM_LOCK
536 unsigned meta = xw->work.meta_mods;
537 if (meta == 0)
538 meta = xw->work.alt_mods;
539 if (meta == 0)
540 meta = Mod1Mask;
541 #else
542 unsigned meta = Mod1Mask;
543 (void) xw;
544 #endif
545 return meta;
546 }
547
548 /*
549 * Returns a mask of the modifiers we may use for modifying the mouse protocol
550 * response strings.
551 */
552 static unsigned
OurModifiers(XtermWidget xw)553 OurModifiers(XtermWidget xw)
554 {
555 return (ShiftMask
556 | ControlMask
557 | MetaMask(xw));
558 }
559
560 /*
561 * The actual check for the shift-mask, to see if it should tell xterm to
562 * override mouse-protocol in favor of select/paste actions depends upon
563 * whether the shiftEscape resource is set to true/always vs false/never.
564 */
565 static Boolean
ShiftOverride(XtermWidget xw,unsigned state,int button)566 ShiftOverride(XtermWidget xw, unsigned state, int button)
567 {
568 unsigned check = (state & OurModifiers(xw));
569 Boolean result = False;
570
571 if (check & ShiftMask) {
572 if (xw->keyboard.shift_escape == ssFalse ||
573 xw->keyboard.shift_escape == ssNever) {
574 result = True;
575 } else if (xw->keyboard.shift_escape == ssTrue) {
576 /*
577 * Check if the button is one that we found does not directly use
578 * the shift-modifier in its bindings to select/copy actions.
579 */
580 if (button > 0 && button <= MaxMouseBtn) {
581 if (xw->keyboard.shift_buttons & (1U << (button - 1))) {
582 result = True;
583 }
584 } else {
585 result = True; /* unlikely, and we don't care */
586 }
587 }
588 }
589 TRACE2(("ShiftOverride ( %#x -> %#x ) %d\n", state, check, result));
590 return result;
591 }
592
593 /*
594 * Normally xterm treats the shift-modifier specially when the mouse protocol
595 * is active. The translations resource binds otherwise unmodified button
596 * for these mouse-related events:
597 *
598 * ~Meta <Btn1Down>:select-start() \n\
599 * ~Meta <Btn1Motion>:select-extend() \n\
600 * ~Ctrl ~Meta <Btn2Up>:insert-selection(SELECT, CUT_BUFFER0) \n\
601 * ~Ctrl ~Meta <Btn3Down>:start-extend() \n\
602 * ~Meta <Btn3Motion>:select-extend() \n\
603 * <BtnUp>:select-end(SELECT, CUT_BUFFER0) \n\
604 *
605 * There is no API in the X libraries which would tell us if a given mouse
606 * button is bound to one of these actions. These functions make the choice
607 * configurable.
608 */
609 static Bool
InterpretButton(XtermWidget xw,XButtonEvent * event)610 InterpretButton(XtermWidget xw, XButtonEvent *event)
611 {
612 Bool result = False;
613
614 if (ShiftOverride(xw, event->state, (int) event->button)) {
615 TRACE(("...shift-button #%d overrides mouse-protocol\n", event->button));
616 result = True;
617 }
618 return result;
619 }
620
621 #define Button1Index 8 /* X.h should have done this */
622
623 static int
MotionButton(unsigned state)624 MotionButton(unsigned state)
625 {
626 unsigned bmask = state >> Button1Index;
627 int result = 1;
628
629 if (bmask != 0) {
630 while (!(bmask & 1)) {
631 ++result;
632 bmask >>= 1;
633 }
634 }
635 return result;
636 }
637
638 static Bool
InterpretEvent(XtermWidget xw,XEvent * event)639 InterpretEvent(XtermWidget xw, XEvent *event)
640 {
641 Bool result = False; /* if not a button, is motion */
642
643 if (IsBtnEvent(event)) {
644 result = InterpretButton(xw, (XButtonEvent *) event);
645 } else if (event->type == MotionNotify) {
646 unsigned state = event->xmotion.state;
647 int button = MotionButton(state);
648
649 if (ShiftOverride(xw, state, button)) {
650 TRACE(("...shift-motion #%d (%d,%d) overrides mouse-protocol\n",
651 button,
652 event->xmotion.y,
653 event->xmotion.x));
654 result = True;
655 }
656 }
657 return result;
658 }
659
660 #define OverrideEvent(event) InterpretEvent(xw, event)
661 #define OverrideButton(event) InterpretButton(xw, event)
662
663 /*
664 * Returns true if we handled the event here, and nothing more is needed.
665 */
666 Bool
SendMousePosition(XtermWidget xw,XEvent * event)667 SendMousePosition(XtermWidget xw, XEvent *event)
668 {
669 XButtonEvent *my_event = (XButtonEvent *) event;
670 Bool result = False;
671
672 switch (okSendMousePos(xw)) {
673 case MOUSE_OFF:
674 /* If send_mouse_pos mode isn't on, we shouldn't be here */
675 break;
676
677 case BTN_EVENT_MOUSE:
678 case ANY_EVENT_MOUSE:
679 if (!OverrideEvent(event)) {
680 /* xterm extension for motion reporting. June 1998 */
681 /* EditorButton() will distinguish between the modes */
682 switch (event->type) {
683 case MotionNotify:
684 my_event->button = 0;
685 /* FALLTHRU */
686 case ButtonPress:
687 /* FALLTHRU */
688 case ButtonRelease:
689 EditorButton(xw, my_event);
690 result = True;
691 break;
692 }
693 }
694 break;
695
696 case X10_MOUSE: /* X10 compatibility sequences */
697 if (IsBtnEvent(event)) {
698 if (!OverrideButton(my_event)) {
699 if (my_event->type == ButtonPress)
700 EditorButton(xw, my_event);
701 result = True;
702 }
703 }
704 break;
705
706 case VT200_HIGHLIGHT_MOUSE: /* DEC vt200 hilite tracking */
707 if (IsBtnEvent(event)) {
708 if (!OverrideButton(my_event)) {
709 if (my_event->type == ButtonPress &&
710 my_event->button == Button1) {
711 TrackDown(xw, my_event);
712 } else {
713 EditorButton(xw, my_event);
714 }
715 result = True;
716 }
717 }
718 break;
719
720 case VT200_MOUSE: /* DEC vt200 compatible */
721 if (IsBtnEvent(event)) {
722 if (!OverrideButton(my_event)) {
723 EditorButton(xw, my_event);
724 result = True;
725 }
726 }
727 break;
728
729 case DEC_LOCATOR:
730 #if OPT_DEC_LOCATOR
731 if (IsBtnEvent(event) || event->type == MotionNotify) {
732 result = SendLocatorPosition(xw, my_event);
733 }
734 #endif /* OPT_DEC_LOCATOR */
735 break;
736 }
737 return result;
738 }
739
740 #if OPT_DEC_LOCATOR
741
742 #define LocatorCoords( row, col, x, y, oor ) \
743 if( screen->locator_pixels ) { \
744 (oor)=False; (row) = (y)+1; (col) = (x)+1; \
745 /* Limit to screen dimensions */ \
746 if ((row) < 1) (row) = 1,(oor)=True; \
747 else if ((row) > screen->border*2+Height(screen)) \
748 (row) = screen->border*2+Height(screen),(oor)=True; \
749 if ((col) < 1) (col) = 1,(oor)=True; \
750 else if ((col) > OriginX(screen)*2+Width(screen)) \
751 (col) = OriginX(screen)*2+Width(screen),(oor)=True; \
752 } else { \
753 (oor)=False; \
754 /* Compute character position of mouse pointer */ \
755 (row) = ((y) - screen->border) / FontHeight(screen); \
756 (col) = ((x) - OriginX(screen)) / FontWidth(screen); \
757 /* Limit to screen dimensions */ \
758 if ((row) < 0) (row) = 0,(oor)=True; \
759 else if ((row) > screen->max_row) \
760 (row) = screen->max_row,(oor)=True; \
761 if ((col) < 0) (col) = 0,(oor)=True; \
762 else if ((col) > screen->max_col) \
763 (col) = screen->max_col,(oor)=True; \
764 (row)++; (col)++; \
765 }
766
767 static Bool
SendLocatorPosition(XtermWidget xw,XButtonEvent * event)768 SendLocatorPosition(XtermWidget xw, XButtonEvent *event)
769 {
770 ANSI reply;
771 TScreen *screen = TScreenOf(xw);
772 int row, col;
773 Bool oor;
774 int button;
775 unsigned state;
776
777 /* Make sure the event is an appropriate type */
778 if (IsBtnEvent(event)) {
779 if (OverrideButton(event))
780 return (False);
781 } else {
782 if (!screen->loc_filter)
783 return (False);
784 }
785
786 if ((event->type == ButtonPress &&
787 !(screen->locator_events & LOC_BTNS_DN)) ||
788 (event->type == ButtonRelease &&
789 !(screen->locator_events & LOC_BTNS_UP)))
790 return (True);
791
792 if (event->type == MotionNotify) {
793 CheckLocatorPosition(xw, event);
794 return (True);
795 }
796
797 /* get button # */
798 button = (int) event->button - 1;
799
800 LocatorCoords(row, col, event->x, event->y, oor);
801
802 /*
803 * DECterm mouse:
804 *
805 * ESCAPE '[' event ; mask ; row ; column '&' 'w'
806 */
807 memset(&reply, 0, sizeof(reply));
808 reply.a_type = ANSI_CSI;
809
810 if (oor) {
811 reply.a_nparam = 1;
812 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
813 reply.a_inters = '&';
814 reply.a_final = 'w';
815 unparseseq(xw, &reply);
816
817 if (screen->locator_reset) {
818 MotionOff(screen, xw);
819 screen->send_mouse_pos = MOUSE_OFF;
820 }
821 return (True);
822 }
823
824 /*
825 * event:
826 * 1 no buttons
827 * 2 left button down
828 * 3 left button up
829 * 4 middle button down
830 * 5 middle button up
831 * 6 right button down
832 * 7 right button up
833 * 8 M4 down
834 * 9 M4 up
835 */
836 reply.a_nparam = 4;
837 switch (event->type) {
838 case ButtonPress:
839 reply.a_param[0] = (ParmType) (2 + (button << 1));
840 break;
841 case ButtonRelease:
842 reply.a_param[0] = (ParmType) (3 + (button << 1));
843 break;
844 default:
845 return (True);
846 }
847 /*
848 * mask:
849 * bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
850 * M4 down left down middle down right down
851 *
852 * Notice that Button1 (left) and Button3 (right) are swapped in the mask.
853 * Also, mask should be the state after the button press/release,
854 * X provides the state not including the button press/release.
855 */
856 state = (event->state
857 & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8;
858 /* update mask to "after" state */
859 state ^= ((unsigned) (1 << button));
860 /* swap Button1 & Button3 */
861 state = ((state & (unsigned) ~(4 | 1))
862 | ((state & 1) ? 4 : 0)
863 | ((state & 4) ? 1 : 0));
864
865 reply.a_param[1] = (ParmType) state;
866 reply.a_param[2] = (ParmType) row;
867 reply.a_param[3] = (ParmType) col;
868 reply.a_inters = '&';
869 reply.a_final = 'w';
870
871 unparseseq(xw, &reply);
872
873 if (screen->locator_reset) {
874 MotionOff(screen, xw);
875 screen->send_mouse_pos = MOUSE_OFF;
876 }
877
878 /*
879 * DECterm turns the Locator off if a button is pressed while a filter
880 * rectangle is active. This might be a bug, but I don't know, so I'll
881 * emulate it anyway.
882 */
883 if (screen->loc_filter) {
884 screen->send_mouse_pos = MOUSE_OFF;
885 screen->loc_filter = False;
886 screen->locator_events = 0;
887 MotionOff(screen, xw);
888 }
889
890 return (True);
891 }
892
893 /*
894 * mask:
895 * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
896 * M4 down left down middle down right down
897 *
898 * Button1 (left) and Button3 (right) are swapped in the mask relative to X.
899 */
900 #define ButtonState(state, mask) \
901 { int stemp = (int) (((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8); \
902 /* swap Button1 & Button3 */ \
903 (state) = (stemp & ~(4|1)) | ((stemp & 1) ? 4 : 0) | ((stemp & 4) ? 1 : 0); \
904 }
905
906 void
GetLocatorPosition(XtermWidget xw)907 GetLocatorPosition(XtermWidget xw)
908 {
909 ANSI reply;
910 TScreen *screen = TScreenOf(xw);
911 Window root, child;
912 int rx, ry, x, y;
913 unsigned int mask = 0;
914 int row = 0, col = 0;
915 Bool oor = False;
916 Bool ret = False;
917 int state;
918
919 /*
920 * DECterm turns the Locator off if the position is requested while a
921 * filter rectangle is active. This might be a bug, but I don't know, so
922 * I'll emulate it anyways.
923 */
924 if (screen->loc_filter) {
925 screen->send_mouse_pos = MOUSE_OFF;
926 screen->loc_filter = False;
927 screen->locator_events = 0;
928 MotionOff(screen, xw);
929 }
930
931 memset(&reply, 0, sizeof(reply));
932 reply.a_type = ANSI_CSI;
933
934 if (okSendMousePos(xw) == DEC_LOCATOR) {
935 ret = XQueryPointer(screen->display, VWindow(screen), &root,
936 &child, &rx, &ry, &x, &y, &mask);
937 if (ret) {
938 LocatorCoords(row, col, x, y, oor);
939 }
940 }
941 if (ret == False || oor) {
942 reply.a_nparam = 1;
943 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
944 reply.a_inters = '&';
945 reply.a_final = 'w';
946 unparseseq(xw, &reply);
947
948 if (screen->locator_reset) {
949 MotionOff(screen, xw);
950 screen->send_mouse_pos = MOUSE_OFF;
951 }
952 return;
953 }
954
955 ButtonState(state, mask);
956
957 reply.a_nparam = 4;
958 reply.a_param[0] = 1; /* Event - 1 = response to locator request */
959 reply.a_param[1] = (ParmType) state;
960 reply.a_param[2] = (ParmType) row;
961 reply.a_param[3] = (ParmType) col;
962 reply.a_inters = '&';
963 reply.a_final = 'w';
964 unparseseq(xw, &reply);
965
966 if (screen->locator_reset) {
967 MotionOff(screen, xw);
968 screen->send_mouse_pos = MOUSE_OFF;
969 }
970 }
971
972 void
InitLocatorFilter(XtermWidget xw)973 InitLocatorFilter(XtermWidget xw)
974 {
975 ANSI reply;
976 TScreen *screen = TScreenOf(xw);
977 Window root, child;
978 int rx, ry, x, y;
979 unsigned int mask;
980 int row = 0, col = 0;
981 Bool oor = 0;
982 Bool ret;
983
984 ret = XQueryPointer(screen->display, VWindow(screen),
985 &root, &child, &rx, &ry, &x, &y, &mask);
986 if (ret) {
987 LocatorCoords(row, col, x, y, oor);
988 }
989 if (ret == False || oor) {
990 /* Locator is unavailable */
991
992 if (screen->loc_filter_top != LOC_FILTER_POS ||
993 screen->loc_filter_left != LOC_FILTER_POS ||
994 screen->loc_filter_bottom != LOC_FILTER_POS ||
995 screen->loc_filter_right != LOC_FILTER_POS) {
996 /*
997 * If any explicit coordinates were received,
998 * report immediately with no coordinates.
999 */
1000 memset(&reply, 0, sizeof(reply));
1001 reply.a_type = ANSI_CSI;
1002 reply.a_nparam = 1;
1003 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
1004 reply.a_inters = '&';
1005 reply.a_final = 'w';
1006 unparseseq(xw, &reply);
1007
1008 if (screen->locator_reset) {
1009 MotionOff(screen, xw);
1010 screen->send_mouse_pos = MOUSE_OFF;
1011 }
1012 } else {
1013 /*
1014 * No explicit coordinates were received, and the pointer is
1015 * unavailable. Report when the pointer re-enters the window.
1016 */
1017 screen->loc_filter = True;
1018 MotionOn(screen, xw);
1019 }
1020 return;
1021 }
1022
1023 /*
1024 * Adjust rectangle coordinates:
1025 * 1. Replace "LOC_FILTER_POS" with current coordinates
1026 * 2. Limit coordinates to screen size
1027 * 3. make sure top and left are less than bottom and right, resp.
1028 */
1029 if (screen->locator_pixels) {
1030 rx = OriginX(screen) * 2 + Width(screen);
1031 ry = screen->border * 2 + Height(screen);
1032 } else {
1033 rx = screen->max_col;
1034 ry = screen->max_row;
1035 }
1036
1037 #define Adjust( coord, def, max ) \
1038 if( (coord) == LOC_FILTER_POS ) (coord) = (def); \
1039 else if ((coord) < 1) (coord) = 1; \
1040 else if ((coord) > (max)) (coord) = (max)
1041
1042 Adjust(screen->loc_filter_top, row, ry);
1043 Adjust(screen->loc_filter_left, col, rx);
1044 Adjust(screen->loc_filter_bottom, row, ry);
1045 Adjust(screen->loc_filter_right, col, rx);
1046
1047 if (screen->loc_filter_top > screen->loc_filter_bottom) {
1048 ry = screen->loc_filter_top;
1049 screen->loc_filter_top = screen->loc_filter_bottom;
1050 screen->loc_filter_bottom = ry;
1051 }
1052
1053 if (screen->loc_filter_left > screen->loc_filter_right) {
1054 rx = screen->loc_filter_left;
1055 screen->loc_filter_left = screen->loc_filter_right;
1056 screen->loc_filter_right = rx;
1057 }
1058
1059 if ((col < screen->loc_filter_left) ||
1060 (col > screen->loc_filter_right) ||
1061 (row < screen->loc_filter_top) ||
1062 (row > screen->loc_filter_bottom)) {
1063 int state;
1064
1065 /* Pointer is already outside the rectangle - report immediately */
1066 ButtonState(state, mask);
1067
1068 memset(&reply, 0, sizeof(reply));
1069 reply.a_type = ANSI_CSI;
1070 reply.a_nparam = 4;
1071 reply.a_param[0] = 10; /* Event - 10 = locator outside filter */
1072 reply.a_param[1] = (ParmType) state;
1073 reply.a_param[2] = (ParmType) row;
1074 reply.a_param[3] = (ParmType) col;
1075 reply.a_inters = '&';
1076 reply.a_final = 'w';
1077 unparseseq(xw, &reply);
1078
1079 if (screen->locator_reset) {
1080 MotionOff(screen, xw);
1081 screen->send_mouse_pos = MOUSE_OFF;
1082 }
1083 return;
1084 }
1085
1086 /*
1087 * Rectangle is set up. Allow pointer tracking
1088 * to detect if the mouse leaves the rectangle.
1089 */
1090 screen->loc_filter = True;
1091 MotionOn(screen, xw);
1092 }
1093
1094 static void
CheckLocatorPosition(XtermWidget xw,XButtonEvent * event)1095 CheckLocatorPosition(XtermWidget xw, XButtonEvent *event)
1096 {
1097 ANSI reply;
1098 TScreen *screen = TScreenOf(xw);
1099 int row, col;
1100 Bool oor;
1101
1102 LocatorCoords(row, col, event->x, event->y, oor);
1103
1104 /*
1105 * Send report if the pointer left the filter rectangle, if
1106 * the pointer left the window, or if the filter rectangle
1107 * had no coordinates and the pointer re-entered the window.
1108 */
1109 if (oor || (screen->loc_filter_top == LOC_FILTER_POS) ||
1110 (col < screen->loc_filter_left) ||
1111 (col > screen->loc_filter_right) ||
1112 (row < screen->loc_filter_top) ||
1113 (row > screen->loc_filter_bottom)) {
1114 /* Filter triggered - disable it */
1115 screen->loc_filter = False;
1116 MotionOff(screen, xw);
1117
1118 memset(&reply, 0, sizeof(reply));
1119 reply.a_type = ANSI_CSI;
1120 if (oor) {
1121 reply.a_nparam = 1;
1122 reply.a_param[0] = 0; /* Event - 0 = locator unavailable */
1123 } else {
1124 int state;
1125
1126 ButtonState(state, event->state);
1127
1128 reply.a_nparam = 4;
1129 reply.a_param[0] = 10; /* Event - 10 = locator outside filter */
1130 reply.a_param[1] = (ParmType) state;
1131 reply.a_param[2] = (ParmType) row;
1132 reply.a_param[3] = (ParmType) col;
1133 }
1134
1135 reply.a_inters = '&';
1136 reply.a_final = 'w';
1137 unparseseq(xw, &reply);
1138
1139 if (screen->locator_reset) {
1140 MotionOff(screen, xw);
1141 screen->send_mouse_pos = MOUSE_OFF;
1142 }
1143 }
1144 }
1145 #endif /* OPT_DEC_LOCATOR */
1146
1147 #if OPT_READLINE
1148 static int
isClick1_clean(XtermWidget xw,XButtonEvent * event)1149 isClick1_clean(XtermWidget xw, XButtonEvent *event)
1150 {
1151 TScreen *screen = TScreenOf(xw);
1152 int delta;
1153
1154 /* Disable on Shift-Click-1, including the application-mouse modes */
1155 if (OverrideButton(event)
1156 || (okSendMousePos(xw) != MOUSE_OFF)
1157 || ExtendingSelection) /* Was moved */
1158 return 0;
1159
1160 if (event->type != ButtonRelease)
1161 return 0;
1162
1163 if (lastButtonDownTime == (Time) 0) {
1164 /* first time or once in a blue moon */
1165 delta = screen->multiClickTime + 1;
1166 } else if (event->time > lastButtonDownTime) {
1167 /* most of the time */
1168 delta = (int) (event->time - lastButtonDownTime);
1169 } else {
1170 /* time has rolled over since lastButtonUpTime */
1171 delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->time);
1172 }
1173
1174 return delta <= screen->multiClickTime;
1175 }
1176
1177 static int
isDoubleClick3(XtermWidget xw,TScreen * screen,XButtonEvent * event)1178 isDoubleClick3(XtermWidget xw, TScreen *screen, XButtonEvent *event)
1179 {
1180 int delta;
1181
1182 if (event->type != ButtonRelease
1183 || OverrideButton(event)
1184 || event->button != Button3) {
1185 lastButton3UpTime = 0; /* Disable the cached info */
1186 return 0;
1187 }
1188 /* Process Btn3Release. */
1189 if (lastButton3DoubleDownTime == (Time) 0) {
1190 /* No previous click or once in a blue moon */
1191 delta = screen->multiClickTime + 1;
1192 } else if (event->time > lastButton3DoubleDownTime) {
1193 /* most of the time */
1194 delta = (int) (event->time - lastButton3DoubleDownTime);
1195 } else {
1196 /* time has rolled over since lastButton3DoubleDownTime */
1197 delta = (int) ((((Time) ~ 0) - lastButton3DoubleDownTime) + event->time);
1198 }
1199 if (delta <= screen->multiClickTime) {
1200 /* Double click */
1201 CELL cell;
1202
1203 /* Cannot check ExtendingSelection, since mouse-3 always sets it */
1204 PointToCELL(screen, event->y, event->x, &cell);
1205 if (isSameCELL(&cell, &lastButton3)) {
1206 lastButton3DoubleDownTime = 0; /* Disable the third click */
1207 return 1;
1208 }
1209 }
1210 /* Not a double click, memorize for future check. */
1211 lastButton3UpTime = event->time;
1212 PointToCELL(screen, event->y, event->x, &lastButton3);
1213 return 0;
1214 }
1215
1216 static int
CheckSecondPress3(XtermWidget xw,TScreen * screen,XEvent * event)1217 CheckSecondPress3(XtermWidget xw, TScreen *screen, XEvent *event)
1218 {
1219 int delta;
1220
1221 if (event->type != ButtonPress
1222 || OverrideEvent(event)
1223 || event->xbutton.button != Button3) {
1224 lastButton3DoubleDownTime = 0; /* Disable the cached info */
1225 return 0;
1226 }
1227 /* Process Btn3Press. */
1228 if (lastButton3UpTime == (Time) 0) {
1229 /* No previous click or once in a blue moon */
1230 delta = screen->multiClickTime + 1;
1231 } else if (event->xbutton.time > lastButton3UpTime) {
1232 /* most of the time */
1233 delta = (int) (event->xbutton.time - lastButton3UpTime);
1234 } else {
1235 /* time has rolled over since lastButton3UpTime */
1236 delta = (int) ((((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time);
1237 }
1238 if (delta <= screen->multiClickTime) {
1239 CELL cell;
1240
1241 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
1242 if (isSameCELL(&cell, &lastButton3)) {
1243 /* A candidate for a double-click */
1244 lastButton3DoubleDownTime = event->xbutton.time;
1245 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &lastButton3);
1246 return 1;
1247 }
1248 lastButton3UpTime = 0; /* Disable the info about the previous click */
1249 }
1250 /* Either too long, or moved, disable. */
1251 lastButton3DoubleDownTime = 0;
1252 return 0;
1253 }
1254
1255 static int
rowOnCurrentLine(TScreen * screen,int line,int * deltap)1256 rowOnCurrentLine(TScreen *screen,
1257 int line,
1258 int *deltap) /* must be XButtonEvent */
1259 {
1260 int result = 1;
1261
1262 *deltap = 0;
1263
1264 if (line != screen->cur_row) {
1265 int l1, l2;
1266
1267 if (line < screen->cur_row) {
1268 l1 = line;
1269 l2 = screen->cur_row;
1270 } else {
1271 l2 = line;
1272 l1 = screen->cur_row;
1273 }
1274 l1--;
1275 while (++l1 < l2) {
1276 LineData *ld = GET_LINEDATA(screen, l1);
1277 if (!LineTstWrapped(ld)) {
1278 result = 0;
1279 break;
1280 }
1281 }
1282 if (result) {
1283 /* Everything is on one "wrapped line" now */
1284 *deltap = line - screen->cur_row;
1285 }
1286 }
1287 return result;
1288 }
1289
1290 static int
eventRow(TScreen * screen,XEvent * event)1291 eventRow(TScreen *screen, XEvent *event) /* must be XButtonEvent */
1292 {
1293 return (event->xbutton.y - screen->border) / FontHeight(screen);
1294 }
1295
1296 static int
eventColBetween(TScreen * screen,XEvent * event)1297 eventColBetween(TScreen *screen, XEvent *event) /* must be XButtonEvent */
1298 {
1299 /* Correct by half a width - we are acting on a boundary, not on a cell. */
1300 return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2)
1301 / FontWidth(screen));
1302 }
1303
1304 static int
ReadLineMovePoint(TScreen * screen,int col,int ldelta)1305 ReadLineMovePoint(TScreen *screen, int col, int ldelta)
1306 {
1307 Char line[6];
1308 unsigned count = 0;
1309
1310 col += ldelta * MaxCols(screen) - screen->cur_col;
1311 if (col == 0)
1312 return 0;
1313 if (screen->control_eight_bits) {
1314 line[count++] = ANSI_CSI;
1315 } else {
1316 line[count++] = ANSI_ESC;
1317 line[count++] = '['; /* XXX maybe sometimes O is better? */
1318 }
1319 line[count] = CharOf(col > 0 ? 'C' : 'D');
1320 if (col < 0)
1321 col = -col;
1322 while (col--)
1323 v_write(screen->respond, line, 3);
1324 return 1;
1325 }
1326
1327 static int
ReadLineDelete(TScreen * screen,CELL * cell1,CELL * cell2)1328 ReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
1329 {
1330 int del;
1331
1332 del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
1333 if (del <= 0) /* Just in case... */
1334 return 0;
1335 while (del--)
1336 v_write(screen->respond, (const Char *) "\177", 1);
1337 return 1;
1338 }
1339
1340 static void
readlineExtend(XtermWidget xw,XEvent * event)1341 readlineExtend(XtermWidget xw, XEvent *event)
1342 {
1343 TScreen *screen = TScreenOf(xw);
1344 int ldelta1, ldelta2;
1345
1346 if (IsBtnEvent(event)) {
1347 XButtonEvent *my_event = (XButtonEvent *) event;
1348 if (isClick1_clean(xw, my_event)
1349 && SCREEN_FLAG(screen, click1_moves)
1350 && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
1351 ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta1);
1352 }
1353 if (isDoubleClick3(xw, screen, my_event)
1354 && SCREEN_FLAG(screen, dclick3_deletes)
1355 && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1356 && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
1357 ReadLineMovePoint(screen, screen->endSel.col, ldelta2);
1358 ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1359 }
1360 }
1361 }
1362 #endif /* OPT_READLINE */
1363
1364 /* ^XM-G<line+' '><col+' '> */
1365 void
DiredButton(Widget w,XEvent * event,String * params GCC_UNUSED,Cardinal * num_params GCC_UNUSED)1366 DiredButton(Widget w,
1367 XEvent *event, /* must be XButtonEvent */
1368 String *params GCC_UNUSED, /* selections */
1369 Cardinal *num_params GCC_UNUSED)
1370 {
1371 XtermWidget xw;
1372
1373 if ((xw = getXtermWidget(w)) != 0) {
1374 TScreen *screen = TScreenOf(xw);
1375
1376 if (IsBtnEvent(event)
1377 && (event->xbutton.y >= screen->border)
1378 && (event->xbutton.x >= OriginX(screen))) {
1379 Char Line[6];
1380 unsigned line, col;
1381
1382 line = (unsigned) ((event->xbutton.y - screen->border)
1383 / FontHeight(screen));
1384 col = (unsigned) ((event->xbutton.x - OriginX(screen))
1385 / FontWidth(screen));
1386 Line[0] = CONTROL('X');
1387 Line[1] = ANSI_ESC;
1388 Line[2] = 'G';
1389 Line[3] = CharOf(' ' + col);
1390 Line[4] = CharOf(' ' + line);
1391 v_write(screen->respond, Line, 5);
1392 }
1393 }
1394 }
1395
1396 #if OPT_READLINE
1397 void
ReadLineButton(Widget w,XEvent * event,String * params,Cardinal * num_params)1398 ReadLineButton(Widget w,
1399 XEvent *event, /* must be XButtonEvent */
1400 String *params, /* selections */
1401 Cardinal *num_params)
1402 {
1403 XtermWidget xw;
1404
1405 if ((xw = getXtermWidget(w)) != 0) {
1406 TScreen *screen = TScreenOf(xw);
1407 Char Line[6];
1408 int line, col, ldelta = 0;
1409
1410 if (!IsBtnEvent(event)
1411 || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
1412 goto finish;
1413 if (event->type == ButtonRelease) {
1414 int delta;
1415
1416 if (lastButtonDownTime == (Time) 0) {
1417 /* first time and once in a blue moon */
1418 delta = screen->multiClickTime + 1;
1419 } else if (event->xbutton.time > lastButtonDownTime) {
1420 /* most of the time */
1421 delta = (int) (event->xbutton.time - lastButtonDownTime);
1422 } else {
1423 /* time has rolled over since lastButtonUpTime */
1424 delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1425 }
1426 if (delta > screen->multiClickTime)
1427 goto finish; /* All this work for this... */
1428 }
1429 line = (event->xbutton.y - screen->border) / FontHeight(screen);
1430 if (!rowOnCurrentLine(screen, line, &ldelta))
1431 goto finish;
1432 /* Correct by half a width - we are acting on a boundary, not on a cell. */
1433 col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1434 / 2)
1435 / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1436 if (col == 0)
1437 goto finish;
1438 Line[0] = ANSI_ESC;
1439 /* XXX: sometimes it is better to send '['? */
1440 Line[1] = 'O';
1441 Line[2] = CharOf(col > 0 ? 'C' : 'D');
1442 if (col < 0)
1443 col = -col;
1444 while (col--)
1445 v_write(screen->respond, Line, 3);
1446 finish:
1447 if (event->type == ButtonRelease)
1448 do_select_end(xw, event, params, num_params, False);
1449 }
1450 }
1451 #endif /* OPT_READLINE */
1452
1453 /* repeats <ESC>n or <ESC>p */
1454 void
ViButton(Widget w,XEvent * event,String * params GCC_UNUSED,Cardinal * num_params GCC_UNUSED)1455 ViButton(Widget w,
1456 XEvent *event, /* must be XButtonEvent */
1457 String *params GCC_UNUSED, /* selections */
1458 Cardinal *num_params GCC_UNUSED)
1459 {
1460 XtermWidget xw;
1461
1462 if ((xw = getXtermWidget(w)) != 0) {
1463 TScreen *screen = TScreenOf(xw);
1464 int pty = screen->respond;
1465
1466 if (IsBtnEvent(event)) {
1467 int line;
1468
1469 line = screen->cur_row -
1470 ((event->xbutton.y - screen->border) / FontHeight(screen));
1471
1472 if (line != 0) {
1473 Char Line[6];
1474
1475 Line[0] = ANSI_ESC; /* force an exit from insert-mode */
1476 v_write(pty, Line, 1);
1477
1478 if (line < 0) {
1479 line = -line;
1480 Line[0] = CONTROL('n');
1481 } else {
1482 Line[0] = CONTROL('p');
1483 }
1484 while (--line >= 0)
1485 v_write(pty, Line, 1);
1486 }
1487 }
1488 }
1489 }
1490
1491 /*
1492 * This function handles button-motion events
1493 */
1494 /*ARGSUSED*/
1495 void
HandleSelectExtend(Widget w,XEvent * event,String * params GCC_UNUSED,Cardinal * num_params GCC_UNUSED)1496 HandleSelectExtend(Widget w,
1497 XEvent *event, /* must be XMotionEvent */
1498 String *params GCC_UNUSED,
1499 Cardinal *num_params GCC_UNUSED)
1500 {
1501 XtermWidget xw;
1502
1503 if ((xw = getXtermWidget(w)) != 0) {
1504 TScreen *screen = TScreenOf(xw);
1505 CELL cell;
1506
1507 TRACE_EVENT("HandleSelectExtend", event, params, num_params);
1508
1509 screen->selection_time = event->xmotion.time;
1510 switch (screen->eventMode) {
1511 /* If not in one of the DEC mouse-reporting modes */
1512 case LEFTEXTENSION:
1513 case RIGHTEXTENSION:
1514 PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1515 ExtendExtend(xw, &cell);
1516 break;
1517
1518 /* If in motion reporting mode, send mouse position to
1519 character process as a key sequence \E[M... */
1520 case NORMAL:
1521 /* will get here if send_mouse_pos != MOUSE_OFF */
1522 if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1523 || okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1524 (void) SendMousePosition(xw, event);
1525 }
1526 break;
1527 }
1528 }
1529 }
1530
1531 void
HandleKeyboardSelectExtend(Widget w,XEvent * event GCC_UNUSED,String * params GCC_UNUSED,Cardinal * num_params GCC_UNUSED)1532 HandleKeyboardSelectExtend(Widget w,
1533 XEvent *event GCC_UNUSED, /* must be XButtonEvent */
1534 String *params GCC_UNUSED,
1535 Cardinal *num_params GCC_UNUSED)
1536 {
1537 XtermWidget xw;
1538
1539 if ((xw = getXtermWidget(w)) != 0) {
1540 TScreen *screen = TScreenOf(xw);
1541
1542 TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
1543 ExtendExtend(xw, &screen->cursorp);
1544 }
1545 }
1546
1547 static void
do_select_end(XtermWidget xw,XEvent * event,String * params,Cardinal * num_params,Bool use_cursor_loc)1548 do_select_end(XtermWidget xw,
1549 XEvent *event, /* must be XButtonEvent */
1550 String *params, /* selections */
1551 Cardinal *num_params,
1552 Bool use_cursor_loc)
1553 {
1554 TScreen *screen = TScreenOf(xw);
1555
1556 screen->selection_time = event->xbutton.time;
1557
1558 TRACE(("do_select_end %s @%ld\n",
1559 visibleEventMode(screen->eventMode),
1560 screen->selection_time));
1561
1562 switch (screen->eventMode) {
1563 case NORMAL:
1564 (void) SendMousePosition(xw, event);
1565 break;
1566 case LEFTEXTENSION:
1567 case RIGHTEXTENSION:
1568 EndExtend(xw, event, params, *num_params, use_cursor_loc);
1569 #if OPT_READLINE
1570 readlineExtend(xw, event);
1571 #endif /* OPT_READLINE */
1572 break;
1573 }
1574 }
1575
1576 void
HandleSelectEnd(Widget w,XEvent * event,String * params,Cardinal * num_params)1577 HandleSelectEnd(Widget w,
1578 XEvent *event, /* must be XButtonEvent */
1579 String *params, /* selections */
1580 Cardinal *num_params)
1581 {
1582 XtermWidget xw;
1583
1584 if ((xw = getXtermWidget(w)) != 0) {
1585 TRACE(("HandleSelectEnd\n"));
1586 do_select_end(xw, event, params, num_params, False);
1587 }
1588 }
1589
1590 void
HandleKeyboardSelectEnd(Widget w,XEvent * event,String * params,Cardinal * num_params)1591 HandleKeyboardSelectEnd(Widget w,
1592 XEvent *event, /* must be XButtonEvent */
1593 String *params, /* selections */
1594 Cardinal *num_params)
1595 {
1596 XtermWidget xw;
1597
1598 if ((xw = getXtermWidget(w)) != 0) {
1599 TRACE(("HandleKeyboardSelectEnd\n"));
1600 do_select_end(xw, event, params, num_params, True);
1601 }
1602 }
1603
1604 void
HandlePointerMotion(Widget w,XEvent * event,String * params,Cardinal * num_params)1605 HandlePointerMotion(Widget w,
1606 XEvent *event,
1607 String *params, /* selections */
1608 Cardinal *num_params)
1609 {
1610 XtermWidget xw;
1611
1612 (void) params;
1613 (void) num_params;
1614 if ((xw = getXtermWidget(w)) != 0) {
1615 TRACE(("HandlePointerMotion\n"));
1616 if (event->type == MotionNotify)
1617 (void) SendMousePosition(xw, event);
1618 }
1619 }
1620
1621 void
HandlePointerButton(Widget w,XEvent * event,String * params,Cardinal * num_params)1622 HandlePointerButton(Widget w,
1623 XEvent *event,
1624 String *params, /* selections */
1625 Cardinal *num_params)
1626 {
1627 XtermWidget xw;
1628
1629 (void) params;
1630 (void) num_params;
1631 if ((xw = getXtermWidget(w)) != 0) {
1632 TRACE(("HandlePointerButton\n"));
1633 if (IsBtnEvent(event))
1634 (void) SendMousePosition(xw, event);
1635 }
1636 }
1637
1638 /*
1639 * Copy the selection data to the given target(s).
1640 */
1641 void
HandleCopySelection(Widget w,XEvent * event,String * params,Cardinal * num_params)1642 HandleCopySelection(Widget w,
1643 XEvent *event,
1644 String *params, /* list of targets */
1645 Cardinal *num_params)
1646 {
1647 XtermWidget xw;
1648
1649 if ((xw = getXtermWidget(w)) != 0) {
1650 TRACE_EVENT("HandleCopySelection", event, params, num_params);
1651 SelectSet(xw, event, params, *num_params);
1652 }
1653 }
1654
1655 struct _SelectionList {
1656 String *params;
1657 Cardinal count;
1658 Atom *targets;
1659 Time time;
1660 };
1661
1662 static unsigned
DECtoASCII(unsigned ch)1663 DECtoASCII(unsigned ch)
1664 {
1665 if (xtermIsDecGraphic(ch)) {
1666 ch = CharOf("###########+++++##-##++++|######"[ch]);
1667 /* 01234567890123456789012345678901 */
1668 }
1669 return ch;
1670 }
1671
1672 #if OPT_WIDE_CHARS
1673 static Cardinal
addXtermChar(Char ** buffer,Cardinal * used,Cardinal offset,unsigned value)1674 addXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
1675 {
1676 if (offset + 1 >= *used) {
1677 *used = 1 + (2 * (offset + 1));
1678 allocXtermChars(buffer, *used);
1679 }
1680 (*buffer)[offset++] = (Char) value;
1681 return offset;
1682 }
1683 #define AddChar(buffer, used, offset, value) \
1684 offset = addXtermChar(buffer, used, offset, (unsigned) value)
1685
1686 /*
1687 * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1688 * or ASCII/Latin-1 equivalents for special cases.
1689 */
1690 static Char *
UTF8toLatin1(TScreen * screen,Char * s,unsigned long len,unsigned long * result)1691 UTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1692 {
1693 static Char *buffer;
1694 static Cardinal used;
1695
1696 Cardinal offset = 0;
1697
1698 if (len != 0) {
1699 PtyData data;
1700
1701 fakePtyData(&data, s, s + len);
1702 while (decodeUtf8(screen, &data)) {
1703 Bool fails = False;
1704 Bool extra = False;
1705 IChar value;
1706 skipPtyData(&data, value);
1707 if (value == UCS_REPL) {
1708 fails = True;
1709 } else if (value < 256) {
1710 AddChar(&buffer, &used, offset, CharOf(value));
1711 } else {
1712 unsigned eqv = ucs2dec(screen, value);
1713 if (xtermIsDecGraphic(eqv)) {
1714 AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1715 } else {
1716 eqv = AsciiEquivs(value);
1717 if (eqv == value) {
1718 fails = True;
1719 } else {
1720 AddChar(&buffer, &used, offset, eqv);
1721 }
1722 if (isWide((wchar_t) value))
1723 extra = True;
1724 }
1725 }
1726
1727 /*
1728 * If we're not able to plug in a single-byte result, insert the
1729 * defaultString (which normally is a single "#", but could be
1730 * whatever the user wants).
1731 */
1732 if (fails) {
1733 const Char *p;
1734
1735 for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
1736 AddChar(&buffer, &used, offset, *p);
1737 }
1738 }
1739 if (extra)
1740 AddChar(&buffer, &used, offset, ' ');
1741 }
1742 AddChar(&buffer, &used, offset, '\0');
1743 *result = (unsigned long) (offset - 1);
1744 } else {
1745 *result = 0;
1746 }
1747 return buffer;
1748 }
1749
1750 int
xtermUtf8ToTextList(XtermWidget xw,XTextProperty * text_prop,char *** text_list,int * text_list_count)1751 xtermUtf8ToTextList(XtermWidget xw,
1752 XTextProperty * text_prop,
1753 char ***text_list,
1754 int *text_list_count)
1755 {
1756 TScreen *screen = TScreenOf(xw);
1757 Display *dpy = screen->display;
1758 int rc = -1;
1759
1760 if (text_prop->format == 8
1761 && (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
1762 text_list,
1763 text_list_count)) >= 0) {
1764 if (*text_list != NULL && *text_list_count != 0) {
1765 int i;
1766 Char *data;
1767 char **new_text_list, *tmp;
1768 unsigned long size, new_size;
1769
1770 TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
1771
1772 /*
1773 * XLib StringList actually uses only two pointers, one for the
1774 * list itself, and one for the data. Pointer to the data is the
1775 * first element of the list, the rest (if any) list elements point
1776 * to the same memory block as the first element
1777 */
1778 new_size = 0;
1779 for (i = 0; i < *text_list_count; ++i) {
1780 data = (Char *) (*text_list)[i];
1781 size = strlen((*text_list)[i]) + 1;
1782 (void) UTF8toLatin1(screen, data, size, &size);
1783 new_size += size + 1;
1784 }
1785 new_text_list = TypeXtMallocN(char *, *text_list_count);
1786 new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
1787 for (i = 0; i < (*text_list_count); ++i) {
1788 data = (Char *) (*text_list)[i];
1789 size = strlen((*text_list)[i]) + 1;
1790 if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
1791 memcpy(tmp, data, size + 1);
1792 new_text_list[i] = tmp;
1793 tmp += size + 1;
1794 }
1795 }
1796 XFreeStringList((*text_list));
1797 *text_list = new_text_list;
1798 } else {
1799 rc = -1;
1800 }
1801 }
1802 return rc;
1803 }
1804 #endif /* OPT_WIDE_CHARS */
1805
1806 static char *
parseItem(char * value,char * nextc)1807 parseItem(char *value, char *nextc)
1808 {
1809 char *nextp = value;
1810 while (*nextp != '\0' && *nextp != ',') {
1811 *nextp = x_toupper(*nextp);
1812 ++nextp;
1813 }
1814 *nextc = *nextp;
1815 *nextp = '\0';
1816
1817 return nextp;
1818 }
1819
1820 /*
1821 * All of the wanted strings are unique in the first character, so we can
1822 * use simple abbreviations.
1823 */
1824 static Bool
sameItem(const char * actual,const char * wanted)1825 sameItem(const char *actual, const char *wanted)
1826 {
1827 Bool result = False;
1828 size_t have = strlen(actual);
1829 size_t need = strlen(wanted);
1830
1831 if (have != 0 && have <= need) {
1832 if (!strncmp(actual, wanted, have)) {
1833 TRACE(("...matched \"%s\"\n", wanted));
1834 result = True;
1835 }
1836 }
1837
1838 return result;
1839 }
1840
1841 /*
1842 * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1843 */
1844 static Bool
overrideTargets(Widget w,String value,Atom ** resultp)1845 overrideTargets(Widget w, String value, Atom **resultp)
1846 {
1847 Bool override = False;
1848 XtermWidget xw;
1849
1850 if ((xw = getXtermWidget(w)) != 0) {
1851 TScreen *screen = TScreenOf(xw);
1852
1853 if (!IsEmpty(value)) {
1854 char *copied = x_strdup(value);
1855 if (copied != 0) {
1856 Atom *result = 0;
1857 Cardinal count = 1;
1858 int n;
1859
1860 TRACE(("decoding SelectTypes \"%s\"\n", value));
1861 for (n = 0; copied[n] != '\0'; ++n) {
1862 if (copied[n] == ',')
1863 ++count;
1864 }
1865 result = TypeXtMallocN(Atom, (2 * count) + 1);
1866 if (result == NULL) {
1867 TRACE(("Couldn't allocate selection types\n"));
1868 } else {
1869 char nextc = '?';
1870 char *listp = (char *) copied;
1871 count = 0;
1872 do {
1873 char *nextp = parseItem(listp, &nextc);
1874 char *item = x_strtrim(listp);
1875 size_t len = (item ? strlen(item) : 0);
1876
1877 if (len == 0) {
1878 /* EMPTY */ ;
1879 }
1880 #if OPT_WIDE_CHARS
1881 else if (sameItem(item, "UTF8")) {
1882 result[count++] = XA_UTF8_STRING(XtDisplay(w));
1883 }
1884 #endif
1885 else if (sameItem(item, "I18N")) {
1886 if (screen->i18nSelections) {
1887 result[count++] = XA_TEXT(XtDisplay(w));
1888 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1889 }
1890 } else if (sameItem(item, "TEXT")) {
1891 result[count++] = XA_TEXT(XtDisplay(w));
1892 } else if (sameItem(item, "COMPOUND_TEXT")) {
1893 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1894 } else if (sameItem(item, "STRING")) {
1895 result[count++] = XA_STRING;
1896 }
1897 *nextp++ = nextc;
1898 listp = nextp;
1899 free(item);
1900 } while (nextc != '\0');
1901 if (count) {
1902 result[count] = None;
1903 override = True;
1904 *resultp = result;
1905 } else {
1906 XtFree((char *) result);
1907 }
1908 }
1909 free(copied);
1910 } else {
1911 TRACE(("Couldn't allocate copy of selection types\n"));
1912 }
1913 }
1914 }
1915 return override;
1916 }
1917
1918 #if OPT_WIDE_CHARS
1919 static Atom *
allocUtf8Targets(Widget w,TScreen * screen)1920 allocUtf8Targets(Widget w, TScreen *screen)
1921 {
1922 Atom **resultp = &(screen->selection_targets_utf8);
1923
1924 if (*resultp == 0) {
1925 Atom *result;
1926
1927 if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1928 result = TypeXtMallocN(Atom, 5);
1929 if (result == NULL) {
1930 TRACE(("Couldn't allocate utf-8 selection targets\n"));
1931 } else {
1932 int n = 0;
1933
1934 if (XSupportsLocale()) {
1935 result[n++] = XA_UTF8_STRING(XtDisplay(w));
1936 #ifdef X_HAVE_UTF8_STRING
1937 if (screen->i18nSelections) {
1938 result[n++] = XA_TEXT(XtDisplay(w));
1939 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1940 }
1941 #endif
1942 }
1943 result[n++] = XA_STRING;
1944 result[n] = None;
1945 }
1946 }
1947
1948 *resultp = result;
1949 }
1950
1951 return *resultp;
1952 }
1953 #endif
1954
1955 static Atom *
alloc8bitTargets(Widget w,TScreen * screen)1956 alloc8bitTargets(Widget w, TScreen *screen)
1957 {
1958 Atom **resultp = &(screen->selection_targets_8bit);
1959
1960 if (*resultp == 0) {
1961 Atom *result = 0;
1962
1963 if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1964 result = TypeXtMallocN(Atom, 5);
1965 if (result == NULL) {
1966 TRACE(("Couldn't allocate 8bit selection targets\n"));
1967 } else {
1968 int n = 0;
1969
1970 if (XSupportsLocale()) {
1971 #ifdef X_HAVE_UTF8_STRING
1972 result[n++] = XA_UTF8_STRING(XtDisplay(w));
1973 #endif
1974 if (screen->i18nSelections) {
1975 result[n++] = XA_TEXT(XtDisplay(w));
1976 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1977 }
1978 }
1979 result[n++] = XA_STRING;
1980 result[n] = None;
1981 }
1982 }
1983
1984 *resultp = result;
1985 }
1986
1987 return *resultp;
1988 }
1989
1990 static Atom *
_SelectionTargets(Widget w)1991 _SelectionTargets(Widget w)
1992 {
1993 Atom *result;
1994 XtermWidget xw;
1995
1996 if ((xw = getXtermWidget(w)) == 0) {
1997 result = NULL;
1998 } else {
1999 TScreen *screen = TScreenOf(xw);
2000
2001 #if OPT_WIDE_CHARS
2002 if (screen->wide_chars) {
2003 result = allocUtf8Targets(w, screen);
2004 } else
2005 #endif
2006 {
2007 /* not screen->wide_chars */
2008 result = alloc8bitTargets(w, screen);
2009 }
2010 }
2011
2012 return result;
2013 }
2014
2015 #define isSELECT(value) (!strcmp(value, "SELECT"))
2016
2017 static int
DefaultSelection(TScreen * screen)2018 DefaultSelection(TScreen *screen)
2019 {
2020 return (screen->selectToClipboard ? 1 : 0);
2021 }
2022
2023 static int
TargetToSelection(TScreen * screen,String name)2024 TargetToSelection(TScreen *screen, String name)
2025 {
2026 int result = -1;
2027 int cutb;
2028
2029 if (isSELECT(name)) {
2030 result = DefaultSelection(screen);
2031 } else if (!strcmp(name, PRIMARY_NAME)) {
2032 result = PRIMARY_CODE;
2033 } else if (!strcmp(name, CLIPBOARD_NAME)) {
2034 result = CLIPBOARD_CODE;
2035 } else if (!strcmp(name, SECONDARY_NAME)) {
2036 result = SECONDARY_CODE;
2037 } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
2038 if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
2039 result = CutBufferToCode(cutb);
2040 } else {
2041 xtermWarning("unexpected cut-buffer code: %d\n", cutb);
2042 }
2043 } else {
2044 xtermWarning("unexpected selection target: %s\n", name);
2045 }
2046 TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
2047 return result;
2048 }
2049
2050 void
UnmapSelections(XtermWidget xw)2051 UnmapSelections(XtermWidget xw)
2052 {
2053 TScreen *screen = TScreenOf(xw);
2054 Cardinal n;
2055
2056 if (screen->mappedSelect) {
2057 for (n = 0; screen->mappedSelect[n] != 0; ++n)
2058 free((void *) screen->mappedSelect[n]);
2059 FreeAndNull(screen->mappedSelect);
2060 }
2061 }
2062
2063 /*
2064 * xterm generally uses the primary selection. Some applications prefer
2065 * (or are limited to) the clipboard. Since the translations resource is
2066 * complicated, users seldom change the way it affects selection. But it
2067 * is simple to remap the choice between primary and clipboard before the
2068 * call to XmuInternStrings().
2069 */
2070 static String *
MapSelections(XtermWidget xw,String * params,Cardinal num_params)2071 MapSelections(XtermWidget xw, String *params, Cardinal num_params)
2072 {
2073 String *result = params;
2074
2075 if (params != 0 && num_params > 0) {
2076 Cardinal j;
2077 Boolean map = False;
2078
2079 for (j = 0; j < num_params; ++j) {
2080 TRACE(("param[%d]:%s\n", j, params[j]));
2081 if (isSELECT(params[j])) {
2082 map = True;
2083 break;
2084 }
2085 }
2086 if (map) {
2087 TScreen *screen = TScreenOf(xw);
2088 const char *mapTo = (screen->selectToClipboard
2089 ? CLIPBOARD_NAME
2090 : PRIMARY_NAME);
2091
2092 UnmapSelections(xw);
2093 if ((result = TypeMallocN(String, num_params + 1)) != 0) {
2094 result[num_params] = 0;
2095 for (j = 0; j < num_params; ++j) {
2096 result[j] = x_strdup((isSELECT(params[j])
2097 ? mapTo
2098 : params[j]));
2099 if (result[j] == 0) {
2100 UnmapSelections(xw);
2101 while (j != 0) {
2102 free((void *) result[--j]);
2103 }
2104 FreeAndNull(result);
2105 break;
2106 }
2107 }
2108 screen->mappedSelect = result;
2109 }
2110 }
2111 }
2112 return result;
2113 }
2114
2115 /*
2116 * Lookup the cut-buffer number, which will be in the range 0-7.
2117 * If it is not a cut-buffer, it is a type of selection, e.g., primary.
2118 */
2119 static int
CutBuffer(Atom code)2120 CutBuffer(Atom code)
2121 {
2122 int cutbuffer;
2123 switch ((unsigned) code) {
2124 case XA_CUT_BUFFER0:
2125 cutbuffer = 0;
2126 break;
2127 case XA_CUT_BUFFER1:
2128 cutbuffer = 1;
2129 break;
2130 case XA_CUT_BUFFER2:
2131 cutbuffer = 2;
2132 break;
2133 case XA_CUT_BUFFER3:
2134 cutbuffer = 3;
2135 break;
2136 case XA_CUT_BUFFER4:
2137 cutbuffer = 4;
2138 break;
2139 case XA_CUT_BUFFER5:
2140 cutbuffer = 5;
2141 break;
2142 case XA_CUT_BUFFER6:
2143 cutbuffer = 6;
2144 break;
2145 case XA_CUT_BUFFER7:
2146 cutbuffer = 7;
2147 break;
2148 default:
2149 cutbuffer = -1;
2150 break;
2151 }
2152 TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
2153 return cutbuffer;
2154 }
2155
2156 #if OPT_PASTE64
2157 static void
FinishPaste64(XtermWidget xw)2158 FinishPaste64(XtermWidget xw)
2159 {
2160 TScreen *screen = TScreenOf(xw);
2161
2162 TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
2163 if (screen->base64_paste) {
2164 screen->base64_paste = 0;
2165 unparseputc1(xw, screen->base64_final);
2166 unparse_end(xw);
2167 }
2168 }
2169 #endif
2170
2171 #if !OPT_PASTE64
2172 static
2173 #endif
2174 void
xtermGetSelection(Widget w,Time ev_time,String * params,Cardinal num_params,Atom * targets)2175 xtermGetSelection(Widget w,
2176 Time ev_time,
2177 String *params, /* selections in precedence order */
2178 Cardinal num_params,
2179 Atom *targets)
2180 {
2181 Atom selection;
2182 int cutbuffer;
2183 Atom target;
2184
2185 XtermWidget xw;
2186
2187 if (num_params == 0)
2188 return;
2189 if ((xw = getXtermWidget(w)) == 0)
2190 return;
2191
2192 TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
2193 params = MapSelections(xw, params, num_params);
2194
2195 XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
2196 cutbuffer = CutBuffer(selection);
2197
2198 TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
2199 (targets
2200 ? visibleSelectionTarget(XtDisplay(w), targets[0])
2201 : "None")));
2202
2203 if (cutbuffer >= 0) {
2204 int inbytes;
2205 unsigned long nbytes;
2206 int fmt8 = 8;
2207 Atom type = XA_STRING;
2208 char *line;
2209
2210 /* 'line' is freed in SelectionReceived */
2211 line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
2212 nbytes = (unsigned long) inbytes;
2213
2214 if (nbytes > 0) {
2215 SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
2216 &nbytes, &fmt8);
2217 } else if (num_params > 1) {
2218 xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
2219 }
2220 #if OPT_PASTE64
2221 else {
2222 FinishPaste64(xw);
2223 }
2224 #endif
2225 } else {
2226
2227 if (targets == NULL || targets[0] == None) {
2228 targets = _SelectionTargets(w);
2229 }
2230
2231 if (targets != 0) {
2232 struct _SelectionList *list;
2233
2234 target = targets[0];
2235
2236 if (targets[1] == None) { /* last target in list */
2237 params++;
2238 num_params--;
2239 targets = _SelectionTargets(w);
2240 } else {
2241 targets = &(targets[1]);
2242 }
2243
2244 if (num_params) {
2245 /* 'list' is freed in SelectionReceived */
2246 list = TypeXtMalloc(struct _SelectionList);
2247 if (list != 0) {
2248 list->params = params;
2249 list->count = num_params;
2250 list->targets = targets;
2251 list->time = ev_time;
2252 }
2253 } else {
2254 list = NULL;
2255 }
2256
2257 XtGetSelectionValue(w, selection,
2258 target,
2259 SelectionReceived,
2260 (XtPointer) list, ev_time);
2261 }
2262 }
2263 }
2264
2265 #if OPT_TRACE && OPT_WIDE_CHARS
2266 static void
GettingSelection(Display * dpy,Atom type,Char * line,unsigned long len)2267 GettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
2268 {
2269 Char *cp;
2270 const char *name = TraceAtomName(dpy, type);
2271
2272 TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
2273 for (cp = line; cp < line + len; cp++) {
2274 TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
2275 if (isprint(*cp)) {
2276 TRACE(("%c\n", *cp));
2277 } else {
2278 TRACE(("\\x%02x\n", *cp));
2279 }
2280 }
2281 }
2282 #else
2283 #define GettingSelection(dpy,type,line,len) /* nothing */
2284 #endif
2285
2286 #ifdef VMS
2287 # define tty_vwrite(pty,lag,l) tt_write(lag,l)
2288 #else /* !( VMS ) */
2289 # define tty_vwrite(pty,lag,l) v_write(pty,lag,l)
2290 #endif /* defined VMS */
2291
2292 #if OPT_PASTE64
2293 /* Return base64 code character given 6-bit number */
2294 static const char base64_code[] = "\
2295 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
2296 abcdefghijklmnopqrstuvwxyz\
2297 0123456789+/";
2298 static void
base64_flush(TScreen * screen)2299 base64_flush(TScreen *screen)
2300 {
2301 Char x;
2302
2303 TRACE(("base64_flush count %d, pad %d (%d)\n",
2304 screen->base64_count,
2305 screen->base64_pad,
2306 screen->base64_pad & 3));
2307
2308 switch (screen->base64_count) {
2309 case 0:
2310 break;
2311 case 2:
2312 x = CharOf(base64_code[screen->base64_accu << 4]);
2313 tty_vwrite(screen->respond, &x, 1);
2314 break;
2315 case 4:
2316 x = CharOf(base64_code[screen->base64_accu << 2]);
2317 tty_vwrite(screen->respond, &x, 1);
2318 break;
2319 }
2320 if (screen->base64_pad & 3) {
2321 tty_vwrite(screen->respond,
2322 (const Char *) "===",
2323 (unsigned) (3 - (screen->base64_pad & 3)));
2324 }
2325 screen->base64_count = 0;
2326 screen->base64_accu = 0;
2327 screen->base64_pad = 0;
2328 }
2329 #endif /* OPT_PASTE64 */
2330
2331 /*
2332 * Translate ISO-8859-1 or UTF-8 data to NRCS.
2333 */
2334 static void
ToNational(XtermWidget xw,Char * buffer,unsigned * length)2335 ToNational(XtermWidget xw, Char *buffer, unsigned *length)
2336 {
2337 TScreen *screen = TScreenOf(xw);
2338 DECNRCM_codes gsetL = screen->gsets[screen->curgl];
2339 DECNRCM_codes gsetR = screen->gsets[screen->curgr];
2340
2341 #if OPT_WIDE_CHARS
2342 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
2343 Char *p;
2344 PtyData *data = TypeXtMallocX(PtyData, *length);
2345
2346 memset(data, 0, sizeof(*data));
2347 data->next = data->buffer;
2348 data->last = data->buffer + *length;
2349 memcpy(data->buffer, buffer, (size_t) *length);
2350 p = buffer;
2351 while (data->next < data->last) {
2352 unsigned chr, out, gl, gr;
2353
2354 if (!decodeUtf8(screen, data)) {
2355 data->utf_size = 1;
2356 data->utf_data = data->next[0];
2357 }
2358 data->next += data->utf_size;
2359 chr = data->utf_data;
2360 out = chr;
2361 if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2362 out = gl;
2363 } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2364 out = gr;
2365 }
2366 *p++ = (Char) ((out < 256) ? out : ' ');
2367 }
2368 *length = (unsigned) (p - buffer);
2369 free(data);
2370 } else
2371 #endif
2372 {
2373 Char *p;
2374
2375 for (p = buffer; (int) (p - buffer) < (int) *length; ++p) {
2376 unsigned gl, gr;
2377 unsigned chr = *p;
2378 unsigned out = chr;
2379 if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2380 out = gl;
2381 } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2382 out = gr;
2383 }
2384 *p = (Char) out;
2385 }
2386 }
2387 }
2388
2389 static void
_qWriteSelectionData(XtermWidget xw,Char * lag,unsigned length)2390 _qWriteSelectionData(XtermWidget xw, Char *lag, unsigned length)
2391 {
2392 TScreen *screen = TScreenOf(xw);
2393
2394 /*
2395 * If we are pasting into a window which is using NRCS, we want to map
2396 * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
2397 * that an application would use to write characters with NRCS.
2398 *
2399 * TODO: handle conversion from UTF-8, and adjust length. This can be done
2400 * in the same buffer because the target is always 8-bit.
2401 */
2402 if ((xw->flags & NATIONAL) && (length != 0)) {
2403 ToNational(xw, lag, &length);
2404 }
2405 #if OPT_PASTE64
2406 if (screen->base64_paste) {
2407 /* Send data as base64 */
2408 Char *p = lag;
2409 Char buf[64];
2410 unsigned x = 0;
2411
2412 TRACE(("convert to base64 %d:%s\n", length, visibleChars(p, length)));
2413
2414 /*
2415 * Handle the case where the selection is from _this_ xterm, which
2416 * puts part of the reply in the buffer before the selection callback
2417 * happens.
2418 */
2419 if (screen->base64_paste && screen->unparse_len) {
2420 unparse_end(xw);
2421 }
2422 while (length--) {
2423 switch (screen->base64_count) {
2424 case 0:
2425 buf[x++] = CharOf(base64_code[*p >> 2]);
2426 screen->base64_accu = (unsigned) (*p & 0x3);
2427 screen->base64_count = 2;
2428 ++p;
2429 break;
2430 case 2:
2431 buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
2432 (*p >> 4)]);
2433 screen->base64_accu = (unsigned) (*p & 0xF);
2434 screen->base64_count = 4;
2435 ++p;
2436 break;
2437 case 4:
2438 buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
2439 (*p >> 6)]);
2440 buf[x++] = CharOf(base64_code[*p & 0x3F]);
2441 screen->base64_accu = 0;
2442 screen->base64_count = 0;
2443 ++p;
2444 break;
2445 }
2446 if (x >= 63) {
2447 /* Write 63 or 64 characters */
2448 screen->base64_pad += x;
2449 TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
2450 tty_vwrite(screen->respond, buf, x);
2451 x = 0;
2452 }
2453 }
2454 if (x != 0) {
2455 screen->base64_pad += x;
2456 TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
2457 tty_vwrite(screen->respond, buf, x);
2458 }
2459 } else
2460 #endif /* OPT_PASTE64 */
2461 #if OPT_READLINE
2462 if (SCREEN_FLAG(screen, paste_quotes)) {
2463 while (length--) {
2464 tty_vwrite(screen->respond, (const Char *) "\026", 1); /* Control-V */
2465 tty_vwrite(screen->respond, lag++, 1);
2466 }
2467 } else
2468 #endif
2469 {
2470 TRACE(("writing base64 padding %s\n", visibleChars(lag, length)));
2471 tty_vwrite(screen->respond, lag, length);
2472 }
2473 }
2474
2475 static void
_WriteSelectionData(XtermWidget xw,Char * line,size_t length)2476 _WriteSelectionData(XtermWidget xw, Char *line, size_t length)
2477 {
2478 /* Write data to pty a line at a time. */
2479 /* Doing this one line at a time may no longer be necessary
2480 because v_write has been re-written. */
2481
2482 #if OPT_PASTE64
2483 TScreen *screen = TScreenOf(xw);
2484 #endif
2485 Char *lag, *end;
2486
2487 /* in the VMS version, if tt_pasting isn't set to True then qio
2488 reads aren't blocked and an infinite loop is entered, where the
2489 pasted text shows up as new input, goes in again, shows up
2490 again, ad nauseum. */
2491 #ifdef VMS
2492 tt_pasting = True;
2493 #endif
2494
2495 end = &line[length];
2496 lag = line;
2497
2498 #if OPT_PASTE64
2499 if (screen->base64_paste) {
2500 _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2501 base64_flush(screen);
2502 } else
2503 #endif
2504 {
2505 if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2506 Char *cp;
2507 for (cp = line; cp != end; cp++) {
2508 if (*cp == '\n') {
2509 *cp = '\r';
2510 _qWriteSelectionData(xw, lag, (unsigned) (cp - lag + 1));
2511 lag = cp + 1;
2512 }
2513 }
2514 }
2515
2516 if (lag != end) {
2517 _qWriteSelectionData(xw, lag, (unsigned) (end - lag));
2518 }
2519 }
2520 #ifdef VMS
2521 tt_pasting = False;
2522 tt_start_read(); /* reenable reads or a character may be lost */
2523 #endif
2524 }
2525
2526 #if OPT_PASTE64 || OPT_READLINE
2527 static void
_WriteKey(TScreen * screen,const Char * in)2528 _WriteKey(TScreen *screen, const Char *in)
2529 {
2530 Char line[16];
2531 unsigned count = 0;
2532 size_t length = strlen((const char *) in);
2533
2534 if (screen->control_eight_bits) {
2535 line[count++] = ANSI_CSI;
2536 } else {
2537 line[count++] = ANSI_ESC;
2538 line[count++] = '[';
2539 }
2540 while (length--)
2541 line[count++] = *in++;
2542 line[count++] = '~';
2543 tty_vwrite(screen->respond, line, count);
2544 }
2545 #endif /* OPT_READLINE */
2546
2547 /*
2548 * Unless enabled by the user, strip control characters other than formatting.
2549 */
2550 static size_t
removeControls(XtermWidget xw,char * value)2551 removeControls(XtermWidget xw, char *value)
2552 {
2553 TScreen *screen = TScreenOf(xw);
2554 size_t dst = 0;
2555
2556 if (screen->allowPasteControls) {
2557 dst = strlen(value);
2558 } else {
2559 size_t src = 0;
2560 while ((value[dst] = value[src]) != '\0') {
2561 int ch = CharOf(value[src++]);
2562
2563 #define ReplacePaste(n) \
2564 if (screen->disallow_paste_controls[n]) \
2565 value[dst] = ' '
2566
2567 if (ch < 32) {
2568 ReplacePaste(epC0);
2569 ReplacePaste(ch);
2570 ++dst;
2571 } else if (ch == ANSI_DEL) {
2572 ReplacePaste(epDEL);
2573 ++dst;
2574 }
2575 #if OPT_WIDE_CHARS
2576 else if (screen->utf8_inparse || screen->utf8_nrc_mode)
2577 ++dst;
2578 #endif
2579 #if OPT_C1_PRINT || OPT_WIDE_CHARS
2580 else if (screen->c1_printable)
2581 ++dst;
2582 #endif
2583 else if (ch >= 128 && ch < 160)
2584 continue;
2585 else
2586 ++dst;
2587 }
2588 }
2589 return dst;
2590 }
2591
2592 #if OPT_SELECTION_OPS
2593 static void
beginInternalSelect(XtermWidget xw)2594 beginInternalSelect(XtermWidget xw)
2595 {
2596 TScreen *screen = TScreenOf(xw);
2597 InternalSelect *mydata = &(screen->internal_select);
2598
2599 (void) mydata;
2600 /* override flags so that SelectionReceived only updates a buffer */
2601 #if OPT_PASTE64
2602 mydata->base64_paste = screen->base64_paste;
2603 screen->base64_paste = 0;
2604 #endif
2605 #if OPT_PASTE64 || OPT_READLINE
2606 mydata->paste_brackets = screen->paste_brackets;
2607 SCREEN_FLAG_unset(screen, paste_brackets);
2608 #endif
2609 }
2610
2611 static void
finishInternalSelect(XtermWidget xw)2612 finishInternalSelect(XtermWidget xw)
2613 {
2614 TScreen *screen = TScreenOf(xw);
2615 InternalSelect *mydata = &(screen->internal_select);
2616
2617 (void) mydata;
2618 #if OPT_PASTE64
2619 screen->base64_paste = mydata->base64_paste;
2620 #endif
2621 #if OPT_PASTE64 || OPT_READLINE
2622 screen->paste_brackets = mydata->paste_brackets;
2623 #endif
2624 }
2625
2626 #else
2627 #define finishInternalSelect(xw) /* nothing */
2628 #endif /* OPT_SELECTION_OPS */
2629
2630 /* SelectionReceived: stuff received selection text into pty */
2631
2632 /* ARGSUSED */
2633 static void
SelectionReceived(Widget w,XtPointer client_data,Atom * selection GCC_UNUSED,Atom * type,XtPointer value,unsigned long * length,int * format)2634 SelectionReceived(Widget w,
2635 XtPointer client_data,
2636 Atom *selection GCC_UNUSED,
2637 Atom *type,
2638 XtPointer value,
2639 unsigned long *length,
2640 int *format)
2641 {
2642 char **text_list = NULL;
2643 int text_list_count = 0;
2644 XTextProperty text_prop;
2645 TScreen *screen;
2646 Display *dpy;
2647 #if OPT_TRACE && OPT_WIDE_CHARS
2648 Char *line = (Char *) value;
2649 #endif
2650
2651 XtermWidget xw;
2652
2653 if ((xw = getXtermWidget(w)) == 0)
2654 return;
2655
2656 screen = TScreenOf(xw);
2657 dpy = XtDisplay(w);
2658
2659 if (*type == 0 /*XT_CONVERT_FAIL */
2660 || *length == 0
2661 || value == NULL) {
2662 TRACE(("...no data to convert\n"));
2663 goto fail;
2664 }
2665
2666 text_prop.value = (unsigned char *) value;
2667 text_prop.encoding = *type;
2668 text_prop.format = *format;
2669 text_prop.nitems = *length;
2670
2671 TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2672 TraceAtomName(screen->display, *selection),
2673 visibleSelectionTarget(dpy, text_prop.encoding),
2674 text_prop.format,
2675 text_prop.nitems));
2676
2677 #if OPT_WIDE_CHARS
2678 if (XSupportsLocale() && screen->wide_chars) {
2679 if (*type == XA_UTF8_STRING(dpy) ||
2680 *type == XA_STRING ||
2681 *type == XA_COMPOUND_TEXT(dpy)) {
2682 GettingSelection(dpy, *type, line, *length);
2683 if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2684 &text_list,
2685 &text_list_count) < 0) {
2686 TRACE(("default Xutf8 Conversion failed\n"));
2687 text_list = NULL;
2688 }
2689 }
2690 } else
2691 #endif /* OPT_WIDE_CHARS */
2692 {
2693 /* Convert the selection to locale's multibyte encoding. */
2694
2695 if (*type == XA_UTF8_STRING(dpy) ||
2696 *type == XA_STRING ||
2697 *type == XA_COMPOUND_TEXT(dpy)) {
2698 Status rc;
2699
2700 GettingSelection(dpy, *type, line, *length);
2701
2702 #if OPT_WIDE_CHARS
2703 if (*type == XA_UTF8_STRING(dpy) &&
2704 !(screen->wide_chars || screen->c1_printable)) {
2705 rc = xtermUtf8ToTextList(xw, &text_prop,
2706 &text_list, &text_list_count);
2707 } else
2708 #endif
2709 if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2710 rc = XTextPropertyToStringList(&text_prop,
2711 &text_list, &text_list_count);
2712 } else {
2713 rc = XmbTextPropertyToTextList(dpy, &text_prop,
2714 &text_list,
2715 &text_list_count);
2716 }
2717 if (rc < 0) {
2718 TRACE(("Conversion failed\n"));
2719 text_list = NULL;
2720 }
2721 }
2722 }
2723
2724 if (text_list != NULL && text_list_count != 0) {
2725 int i;
2726
2727 #if OPT_PASTE64
2728 if (screen->base64_paste) {
2729 /* EMPTY */ ;
2730 } else
2731 #endif
2732 #if OPT_PASTE64 || OPT_READLINE
2733 if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2734 _WriteKey(screen, (const Char *) "200");
2735 }
2736 #endif
2737 for (i = 0; i < text_list_count; i++) {
2738 size_t len = removeControls(xw, text_list[i]);
2739
2740 if (screen->selectToBuffer) {
2741 InternalSelect *mydata = &(screen->internal_select);
2742 if (!mydata->done) {
2743 size_t have = (mydata->buffer
2744 ? strlen(mydata->buffer)
2745 : 0);
2746 size_t need = have + len + 1;
2747 char *buffer = realloc(mydata->buffer, need);
2748
2749 if (buffer != 0) {
2750 strcpy(buffer + have, text_list[i]);
2751 mydata->buffer = buffer;
2752 }
2753 TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2754 screen->startSel.row,
2755 screen->startSel.col,
2756 screen->endSel.row,
2757 screen->endSel.col,
2758 mydata->buffer));
2759 mydata->format_select(w, mydata->format, mydata->buffer,
2760 &(screen->startSel),
2761 &(screen->endSel));
2762 mydata->done = True;
2763 }
2764
2765 } else {
2766 _WriteSelectionData(xw, (Char *) text_list[i], len);
2767 }
2768 }
2769 #if OPT_PASTE64
2770 if (screen->base64_paste) {
2771 FinishPaste64(xw);
2772 } else
2773 #endif
2774 #if OPT_PASTE64 || OPT_READLINE
2775 if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2776 _WriteKey(screen, (const Char *) "201");
2777 }
2778 #endif
2779 if (screen->selectToBuffer) {
2780 InternalSelect *mydata = &(screen->internal_select);
2781 finishInternalSelect(xw);
2782 if (mydata->done) {
2783 free(mydata->format);
2784 free(mydata->buffer);
2785 memset(mydata, 0, sizeof(*mydata));
2786 }
2787 screen->selectToBuffer = False;
2788 }
2789 XFreeStringList(text_list);
2790 } else {
2791 TRACE(("...empty text-list\n"));
2792 goto fail;
2793 }
2794
2795 XtFree((char *) client_data);
2796 XtFree((char *) value);
2797
2798 return;
2799
2800 fail:
2801 if (client_data != 0) {
2802 struct _SelectionList *list = (struct _SelectionList *) client_data;
2803
2804 TRACE(("SelectionReceived ->xtermGetSelection\n"));
2805 xtermGetSelection(w, list->time,
2806 list->params, list->count, list->targets);
2807 XtFree((char *) client_data);
2808 #if OPT_PASTE64
2809 } else {
2810 FinishPaste64(xw);
2811 #endif
2812 }
2813 return;
2814 }
2815
2816 void
HandleInsertSelection(Widget w,XEvent * event,String * params,Cardinal * num_params)2817 HandleInsertSelection(Widget w,
2818 XEvent *event, /* assumed to be XButtonEvent* */
2819 String *params, /* selections in precedence order */
2820 Cardinal *num_params)
2821 {
2822 XtermWidget xw;
2823
2824 if ((xw = getXtermWidget(w)) != 0) {
2825 TRACE_EVENT("HandleInsertSelection", event, params, num_params);
2826 if (!SendMousePosition(xw, event)) {
2827 #if OPT_READLINE
2828 int ldelta;
2829 TScreen *screen = TScreenOf(xw);
2830 if (IsBtnEvent(event)
2831 && !OverrideEvent(event)
2832 && (okSendMousePos(xw) == MOUSE_OFF)
2833 && SCREEN_FLAG(screen, paste_moves)
2834 && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2835 ReadLineMovePoint(screen, eventColBetween(screen, event), ldelta);
2836 #endif /* OPT_READLINE */
2837
2838 xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2839 }
2840 }
2841 }
2842
2843 static SelectUnit
EvalSelectUnit(XtermWidget xw,Time buttonDownTime,SelectUnit defaultUnit,unsigned int button)2844 EvalSelectUnit(XtermWidget xw,
2845 Time buttonDownTime,
2846 SelectUnit defaultUnit,
2847 unsigned int button)
2848 {
2849 TScreen *screen = TScreenOf(xw);
2850 SelectUnit result;
2851 int delta;
2852
2853 if (button != screen->lastButton) {
2854 delta = screen->multiClickTime + 1;
2855 } else if (screen->lastButtonUpTime == (Time) 0) {
2856 /* first time and once in a blue moon */
2857 delta = screen->multiClickTime + 1;
2858 } else if (buttonDownTime > screen->lastButtonUpTime) {
2859 /* most of the time */
2860 delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2861 } else {
2862 /* time has rolled over since lastButtonUpTime */
2863 delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2864 }
2865
2866 if (delta > screen->multiClickTime) {
2867 screen->numberOfClicks = 1;
2868 result = defaultUnit;
2869 } else {
2870 result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2871 screen->numberOfClicks += 1;
2872 }
2873 TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2874 return result;
2875 }
2876
2877 static void
do_select_start(XtermWidget xw,XEvent * event,CELL * cell)2878 do_select_start(XtermWidget xw,
2879 XEvent *event, /* must be XButtonEvent* */
2880 CELL *cell)
2881 {
2882 TScreen *screen = TScreenOf(xw);
2883
2884 if (SendMousePosition(xw, event))
2885 return;
2886 screen->selectUnit = EvalSelectUnit(xw,
2887 event->xbutton.time,
2888 Select_CHAR,
2889 event->xbutton.button);
2890 screen->replyToEmacs = False;
2891
2892 #if OPT_READLINE
2893 lastButtonDownTime = event->xbutton.time;
2894 #endif
2895
2896 StartSelect(xw, cell);
2897 }
2898
2899 /* ARGSUSED */
2900 void
HandleSelectStart(Widget w,XEvent * event,String * params GCC_UNUSED,Cardinal * num_params GCC_UNUSED)2901 HandleSelectStart(Widget w,
2902 XEvent *event, /* must be XButtonEvent* */
2903 String *params GCC_UNUSED,
2904 Cardinal *num_params GCC_UNUSED)
2905 {
2906 XtermWidget xw;
2907
2908 if ((xw = getXtermWidget(w)) != 0) {
2909 TScreen *screen = TScreenOf(xw);
2910 CELL cell;
2911
2912 TRACE_EVENT("HandleSelectStart", event, params, num_params);
2913 screen->firstValidRow = 0;
2914 screen->lastValidRow = screen->max_row;
2915 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2916
2917 #if OPT_READLINE
2918 ExtendingSelection = 0;
2919 #endif
2920
2921 do_select_start(xw, event, &cell);
2922 }
2923 }
2924
2925 /* ARGSUSED */
2926 void
HandleKeyboardSelectStart(Widget w,XEvent * event,String * params GCC_UNUSED,Cardinal * num_params GCC_UNUSED)2927 HandleKeyboardSelectStart(Widget w,
2928 XEvent *event, /* must be XButtonEvent* */
2929 String *params GCC_UNUSED,
2930 Cardinal *num_params GCC_UNUSED)
2931 {
2932 XtermWidget xw;
2933
2934 if ((xw = getXtermWidget(w)) != 0) {
2935 TScreen *screen = TScreenOf(xw);
2936
2937 TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
2938 do_select_start(xw, event, &screen->cursorp);
2939 }
2940 }
2941
2942 static void
TrackDown(XtermWidget xw,XButtonEvent * event)2943 TrackDown(XtermWidget xw, XButtonEvent *event)
2944 {
2945 TScreen *screen = TScreenOf(xw);
2946 CELL cell;
2947
2948 screen->selectUnit = EvalSelectUnit(xw,
2949 event->time,
2950 Select_CHAR,
2951 event->button);
2952 if (screen->numberOfClicks > 1) {
2953 PointToCELL(screen, event->y, event->x, &cell);
2954 screen->replyToEmacs = True;
2955 StartSelect(xw, &cell);
2956 } else {
2957 screen->waitingForTrackInfo = True;
2958 EditorButton(xw, event);
2959 }
2960 }
2961
2962 #define boundsCheck(x) if (x < 0) \
2963 x = 0; \
2964 else if (x >= screen->max_row) \
2965 x = screen->max_row
2966
2967 void
TrackMouse(XtermWidget xw,int func,CELL * start,int firstrow,int lastrow)2968 TrackMouse(XtermWidget xw,
2969 int func,
2970 CELL *start,
2971 int firstrow,
2972 int lastrow)
2973 {
2974 TScreen *screen = TScreenOf(xw);
2975
2976 if (screen->waitingForTrackInfo) { /* if Timed, ignore */
2977 screen->waitingForTrackInfo = False;
2978
2979 if (func != 0) {
2980 CELL first = *start;
2981
2982 boundsCheck(first.row);
2983 boundsCheck(firstrow);
2984 boundsCheck(lastrow);
2985 screen->firstValidRow = firstrow;
2986 screen->lastValidRow = lastrow;
2987 screen->replyToEmacs = True;
2988 StartSelect(xw, &first);
2989 }
2990 }
2991 }
2992
2993 static void
StartSelect(XtermWidget xw,const CELL * cell)2994 StartSelect(XtermWidget xw, const CELL *cell)
2995 {
2996 TScreen *screen = TScreenOf(xw);
2997
2998 TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2999 if (screen->cursor_state)
3000 HideCursor(xw);
3001 if (screen->numberOfClicks == 1) {
3002 /* set start of selection */
3003 screen->rawPos = *cell;
3004 }
3005 /* else use old values in rawPos */
3006 screen->saveStartR = screen->startExt = screen->rawPos;
3007 screen->saveEndR = screen->endExt = screen->rawPos;
3008 if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
3009 screen->eventMode = LEFTEXTENSION;
3010 screen->startExt = *cell;
3011 } else {
3012 screen->eventMode = RIGHTEXTENSION;
3013 screen->endExt = *cell;
3014 }
3015 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3016 }
3017
3018 static void
EndExtend(XtermWidget xw,XEvent * event,String * params,Cardinal num_params,Bool use_cursor_loc)3019 EndExtend(XtermWidget xw,
3020 XEvent *event, /* must be XButtonEvent */
3021 String *params, /* selections */
3022 Cardinal num_params,
3023 Bool use_cursor_loc)
3024 {
3025 CELL cell;
3026 TScreen *screen = TScreenOf(xw);
3027
3028 TRACE_EVENT("EndExtend", event, params, &num_params);
3029 if (use_cursor_loc) {
3030 cell = screen->cursorp;
3031 } else {
3032 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3033 }
3034 ExtendExtend(xw, &cell);
3035
3036 screen->lastButtonUpTime = event->xbutton.time;
3037 screen->lastButton = event->xbutton.button;
3038
3039 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3040 if (screen->replyToEmacs) {
3041 Char line[64];
3042 unsigned count = 0;
3043
3044 if (screen->control_eight_bits) {
3045 line[count++] = ANSI_CSI;
3046 } else {
3047 line[count++] = ANSI_ESC;
3048 line[count++] = '[';
3049 }
3050 if (isSameCELL(&(screen->rawPos), &(screen->startSel))
3051 && isSameCELL(&cell, &(screen->endSel))) {
3052 /* Use short-form emacs select */
3053
3054 switch (screen->extend_coords) {
3055 case 0:
3056 case SET_EXT_MODE_MOUSE:
3057 line[count++] = 't';
3058 break;
3059 case SET_SGR_EXT_MODE_MOUSE:
3060 case SET_PIXEL_POSITION_MOUSE:
3061 line[count++] = '<';
3062 break;
3063 }
3064
3065 count = EmitMousePosition(screen, line, count, screen->endSel.col);
3066 count = EmitMousePositionSeparator(screen, line, count);
3067 count = EmitMousePosition(screen, line, count, screen->endSel.row);
3068
3069 switch (screen->extend_coords) {
3070 case SET_SGR_EXT_MODE_MOUSE:
3071 case SET_URXVT_EXT_MODE_MOUSE:
3072 case SET_PIXEL_POSITION_MOUSE:
3073 line[count++] = 't';
3074 break;
3075 }
3076 } else {
3077 /* long-form, specify everything */
3078
3079 switch (screen->extend_coords) {
3080 case 0:
3081 case SET_EXT_MODE_MOUSE:
3082 line[count++] = 'T';
3083 break;
3084 case SET_SGR_EXT_MODE_MOUSE:
3085 case SET_PIXEL_POSITION_MOUSE:
3086 line[count++] = '<';
3087 break;
3088 }
3089
3090 count = EmitMousePosition(screen, line, count, screen->startSel.col);
3091 count = EmitMousePositionSeparator(screen, line, count);
3092 count = EmitMousePosition(screen, line, count, screen->startSel.row);
3093 count = EmitMousePositionSeparator(screen, line, count);
3094 count = EmitMousePosition(screen, line, count, screen->endSel.col);
3095 count = EmitMousePositionSeparator(screen, line, count);
3096 count = EmitMousePosition(screen, line, count, screen->endSel.row);
3097 count = EmitMousePositionSeparator(screen, line, count);
3098 count = EmitMousePosition(screen, line, count, cell.col);
3099 count = EmitMousePositionSeparator(screen, line, count);
3100 count = EmitMousePosition(screen, line, count, cell.row);
3101
3102 switch (screen->extend_coords) {
3103 case SET_SGR_EXT_MODE_MOUSE:
3104 case SET_URXVT_EXT_MODE_MOUSE:
3105 case SET_PIXEL_POSITION_MOUSE:
3106 line[count++] = 'T';
3107 break;
3108 }
3109 }
3110 v_write(screen->respond, line, count);
3111 UnHiliteText(xw);
3112 }
3113 }
3114 SelectSet(xw, event, params, num_params);
3115 screen->eventMode = NORMAL;
3116 }
3117
3118 void
HandleSelectSet(Widget w,XEvent * event,String * params,Cardinal * num_params)3119 HandleSelectSet(Widget w,
3120 XEvent *event,
3121 String *params,
3122 Cardinal *num_params)
3123 {
3124 XtermWidget xw;
3125
3126 if ((xw = getXtermWidget(w)) != 0) {
3127 TRACE_EVENT("HandleSelectSet", event, params, num_params);
3128 SelectSet(xw, event, params, *num_params);
3129 }
3130 }
3131
3132 /* ARGSUSED */
3133 static void
SelectSet(XtermWidget xw,XEvent * event GCC_UNUSED,String * params,Cardinal num_params)3134 SelectSet(XtermWidget xw,
3135 XEvent *event GCC_UNUSED,
3136 String *params,
3137 Cardinal num_params)
3138 {
3139 TScreen *screen = TScreenOf(xw);
3140
3141 TRACE(("SelectSet\n"));
3142 /* Only do select stuff if non-null select */
3143 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3144 Cardinal n;
3145 for (n = 0; n < num_params; ++n) {
3146 SaltTextAway(xw,
3147 TargetToSelection(screen, params[n]),
3148 &(screen->startSel), &(screen->endSel));
3149 }
3150 _OwnSelection(xw, params, num_params);
3151 } else {
3152 ScrnDisownSelection(xw);
3153 }
3154 }
3155
3156 #define Abs(x) ((x) < 0 ? -(x) : (x))
3157
3158 /* ARGSUSED */
3159 static void
do_start_extend(XtermWidget xw,XEvent * event,String * params GCC_UNUSED,Cardinal * num_params GCC_UNUSED,Bool use_cursor_loc)3160 do_start_extend(XtermWidget xw,
3161 XEvent *event, /* must be XButtonEvent* */
3162 String *params GCC_UNUSED,
3163 Cardinal *num_params GCC_UNUSED,
3164 Bool use_cursor_loc)
3165 {
3166 TScreen *screen = TScreenOf(xw);
3167 int coord;
3168 CELL cell;
3169
3170 if (SendMousePosition(xw, event))
3171 return;
3172
3173 screen->firstValidRow = 0;
3174 screen->lastValidRow = screen->max_row;
3175 #if OPT_READLINE
3176 if (OverrideEvent(event)
3177 || event->xbutton.button != Button3
3178 || !(SCREEN_FLAG(screen, dclick3_deletes)))
3179 #endif
3180 screen->selectUnit = EvalSelectUnit(xw,
3181 event->xbutton.time,
3182 screen->selectUnit,
3183 event->xbutton.button);
3184 screen->replyToEmacs = False;
3185
3186 #if OPT_READLINE
3187 CheckSecondPress3(xw, screen, event);
3188 #endif
3189
3190 if (screen->numberOfClicks == 1
3191 || (SCREEN_FLAG(screen, dclick3_deletes)
3192 && !OverrideEvent(event))) {
3193 /* Save existing selection so we can reestablish it if the guy
3194 extends past the other end of the selection */
3195 screen->saveStartR = screen->startExt = screen->startRaw;
3196 screen->saveEndR = screen->endExt = screen->endRaw;
3197 } else {
3198 /* He just needed the selection mode changed, use old values. */
3199 screen->startExt = screen->startRaw = screen->saveStartR;
3200 screen->endExt = screen->endRaw = screen->saveEndR;
3201 }
3202 if (use_cursor_loc) {
3203 cell = screen->cursorp;
3204 } else {
3205 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3206 }
3207 coord = Coordinate(screen, &cell);
3208
3209 if (Abs(coord - Coordinate(screen, &(screen->startSel)))
3210 < Abs(coord - Coordinate(screen, &(screen->endSel)))
3211 || coord < Coordinate(screen, &(screen->startSel))) {
3212 /* point is close to left side of selection */
3213 screen->eventMode = LEFTEXTENSION;
3214 screen->startExt = cell;
3215 } else {
3216 /* point is close to left side of selection */
3217 screen->eventMode = RIGHTEXTENSION;
3218 screen->endExt = cell;
3219 }
3220 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
3221
3222 #if OPT_READLINE
3223 if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3224 ExtendingSelection = 1;
3225 #endif
3226 }
3227
3228 static void
ExtendExtend(XtermWidget xw,const CELL * cell)3229 ExtendExtend(XtermWidget xw, const CELL *cell)
3230 {
3231 TScreen *screen = TScreenOf(xw);
3232 int coord = Coordinate(screen, cell);
3233
3234 TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
3235 if (screen->eventMode == LEFTEXTENSION
3236 && ((coord + (screen->selectUnit != Select_CHAR))
3237 > Coordinate(screen, &(screen->endSel)))) {
3238 /* Whoops, he's changed his mind. Do RIGHTEXTENSION */
3239 screen->eventMode = RIGHTEXTENSION;
3240 screen->startExt = screen->saveStartR;
3241 } else if (screen->eventMode == RIGHTEXTENSION
3242 && coord < Coordinate(screen, &(screen->startSel))) {
3243 /* Whoops, he's changed his mind. Do LEFTEXTENSION */
3244 screen->eventMode = LEFTEXTENSION;
3245 screen->endExt = screen->saveEndR;
3246 }
3247 if (screen->eventMode == LEFTEXTENSION) {
3248 screen->startExt = *cell;
3249 } else {
3250 screen->endExt = *cell;
3251 }
3252 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3253
3254 #if OPT_READLINE
3255 if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3256 ExtendingSelection = 1;
3257 #endif
3258 }
3259
3260 void
HandleStartExtend(Widget w,XEvent * event,String * params,Cardinal * num_params)3261 HandleStartExtend(Widget w,
3262 XEvent *event, /* must be XButtonEvent* */
3263 String *params, /* unused */
3264 Cardinal *num_params) /* unused */
3265 {
3266 XtermWidget xw;
3267
3268 if ((xw = getXtermWidget(w)) != 0) {
3269 TRACE_EVENT("HandleStartExtend", event, params, num_params);
3270 do_start_extend(xw, event, params, num_params, False);
3271 }
3272 }
3273
3274 void
HandleKeyboardStartExtend(Widget w,XEvent * event,String * params,Cardinal * num_params)3275 HandleKeyboardStartExtend(Widget w,
3276 XEvent *event, /* must be XButtonEvent* */
3277 String *params, /* unused */
3278 Cardinal *num_params) /* unused */
3279 {
3280 XtermWidget xw;
3281
3282 if ((xw = getXtermWidget(w)) != 0) {
3283 TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
3284 do_start_extend(xw, event, params, num_params, True);
3285 }
3286 }
3287
3288 void
ScrollSelection(TScreen * screen,int amount,Bool always)3289 ScrollSelection(TScreen *screen, int amount, Bool always)
3290 {
3291 int minrow = INX2ROW(screen, -screen->savedlines);
3292 int maxrow = INX2ROW(screen, screen->max_row);
3293 int maxcol = screen->max_col;
3294
3295 #define scroll_update_one(cell) \
3296 (cell)->row += amount; \
3297 if ((cell)->row < minrow) { \
3298 (cell)->row = minrow; \
3299 (cell)->col = 0; \
3300 } \
3301 if ((cell)->row > maxrow) { \
3302 (cell)->row = maxrow; \
3303 (cell)->col = maxcol; \
3304 }
3305
3306 scroll_update_one(&(screen->startRaw));
3307 scroll_update_one(&(screen->endRaw));
3308 scroll_update_one(&(screen->startSel));
3309 scroll_update_one(&(screen->endSel));
3310
3311 scroll_update_one(&(screen->rawPos));
3312
3313 /*
3314 * If we are told to scroll the selection but it lies outside the scrolling
3315 * margins, then that could cause the selection to move (bad). It is not
3316 * simple to fix, because this function is called both for the scrollbar
3317 * actions as well as application scrolling. The 'always' flag is set in
3318 * the former case. The rest of the logic handles the latter.
3319 */
3320 if (ScrnHaveSelection(screen)) {
3321 int adjust;
3322
3323 adjust = ROW2INX(screen, screen->startH.row);
3324 if (always
3325 || !ScrnHaveRowMargins(screen)
3326 || ScrnIsRowInMargins(screen, adjust)) {
3327 scroll_update_one(&screen->startH);
3328 }
3329 adjust = ROW2INX(screen, screen->endH.row);
3330 if (always
3331 || !ScrnHaveRowMargins(screen)
3332 || ScrnIsRowInMargins(screen, adjust)) {
3333 scroll_update_one(&screen->endH);
3334 }
3335 }
3336
3337 screen->startHCoord = Coordinate(screen, &screen->startH);
3338 screen->endHCoord = Coordinate(screen, &screen->endH);
3339 }
3340
3341 /*ARGSUSED*/
3342 void
ResizeSelection(TScreen * screen,int rows,int cols)3343 ResizeSelection(TScreen *screen, int rows, int cols)
3344 {
3345 rows--; /* decr to get 0-max */
3346 cols--;
3347
3348 if (screen->startRaw.row > rows)
3349 screen->startRaw.row = rows;
3350 if (screen->startSel.row > rows)
3351 screen->startSel.row = rows;
3352 if (screen->endRaw.row > rows)
3353 screen->endRaw.row = rows;
3354 if (screen->endSel.row > rows)
3355 screen->endSel.row = rows;
3356 if (screen->rawPos.row > rows)
3357 screen->rawPos.row = rows;
3358
3359 if (screen->startRaw.col > cols)
3360 screen->startRaw.col = cols;
3361 if (screen->startSel.col > cols)
3362 screen->startSel.col = cols;
3363 if (screen->endRaw.col > cols)
3364 screen->endRaw.col = cols;
3365 if (screen->endSel.col > cols)
3366 screen->endSel.col = cols;
3367 if (screen->rawPos.col > cols)
3368 screen->rawPos.col = cols;
3369 }
3370
3371 #if OPT_WIDE_CHARS
3372 #define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
3373 #endif
3374
3375 static void
PointToCELL(TScreen * screen,int y,int x,CELL * cell)3376 PointToCELL(TScreen *screen,
3377 int y,
3378 int x,
3379 CELL *cell)
3380 /* Convert pixel coordinates to character coordinates.
3381 Rows are clipped between firstValidRow and lastValidRow.
3382 Columns are clipped between to be 0 or greater, but are not clipped to some
3383 maximum value. */
3384 {
3385 cell->row = (y - screen->border) / FontHeight(screen);
3386 if (cell->row < screen->firstValidRow)
3387 cell->row = screen->firstValidRow;
3388 else if (cell->row > screen->lastValidRow)
3389 cell->row = screen->lastValidRow;
3390 cell->col = (x - OriginX(screen)) / FontWidth(screen);
3391 if (cell->col < 0)
3392 cell->col = 0;
3393 else if (cell->col > MaxCols(screen)) {
3394 cell->col = MaxCols(screen);
3395 }
3396 #if OPT_WIDE_CHARS
3397 /*
3398 * If we got a click on the right half of a doublewidth character,
3399 * pretend it happened on the left half.
3400 */
3401 if (cell->col > 0
3402 && isWideCell(cell->row, cell->col - 1)
3403 && (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
3404 cell->col -= 1;
3405 }
3406 #endif
3407 }
3408
3409 /*
3410 * Find the last column at which text was drawn on the given row.
3411 */
3412 static int
LastTextCol(TScreen * screen,CLineData * ld,int row)3413 LastTextCol(TScreen *screen, CLineData *ld, int row)
3414 {
3415 int i = -1;
3416
3417 if (ld != 0) {
3418 if (okScrnRow(screen, row)) {
3419 const IAttr *ch;
3420 for (i = screen->max_col,
3421 ch = ld->attribs + i;
3422 i >= 0 && !(*ch & CHARDRAWN);
3423 ch--, i--) {
3424 ;
3425 }
3426 #if OPT_DEC_CHRSET
3427 if (CSET_DOUBLE(GetLineDblCS(ld))) {
3428 i *= 2;
3429 }
3430 #endif
3431 }
3432 }
3433 return (i);
3434 }
3435
3436 #if !OPT_WIDE_CHARS
3437 /*
3438 ** double click table for cut and paste in 8 bits
3439 **
3440 ** This table is divided in four parts :
3441 **
3442 ** - control characters [0,0x1f] U [0x80,0x9f]
3443 ** - separators [0x20,0x3f] U [0xa0,0xb9]
3444 ** - binding characters [0x40,0x7f] U [0xc0,0xff]
3445 ** - exceptions
3446 */
3447 /* *INDENT-OFF* */
3448 static int charClass[256] =
3449 {
3450 /* NUL SOH STX ETX EOT ENQ ACK BEL */
3451 32, 1, 1, 1, 1, 1, 1, 1,
3452 /* BS HT NL VT FF CR SO SI */
3453 1, 32, 1, 1, 1, 1, 1, 1,
3454 /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
3455 1, 1, 1, 1, 1, 1, 1, 1,
3456 /* CAN EM SUB ESC FS GS RS US */
3457 1, 1, 1, 1, 1, 1, 1, 1,
3458 /* SP ! " # $ % & ' */
3459 32, 33, 34, 35, 36, 37, 38, 39,
3460 /* ( ) * + , - . / */
3461 40, 41, 42, 43, 44, 45, 46, 47,
3462 /* 0 1 2 3 4 5 6 7 */
3463 48, 48, 48, 48, 48, 48, 48, 48,
3464 /* 8 9 : ; < = > ? */
3465 48, 48, 58, 59, 60, 61, 62, 63,
3466 /* @ A B C D E F G */
3467 64, 48, 48, 48, 48, 48, 48, 48,
3468 /* H I J K L M N O */
3469 48, 48, 48, 48, 48, 48, 48, 48,
3470 /* P Q R S T U V W */
3471 48, 48, 48, 48, 48, 48, 48, 48,
3472 /* X Y Z [ \ ] ^ _ */
3473 48, 48, 48, 91, 92, 93, 94, 48,
3474 /* ` a b c d e f g */
3475 96, 48, 48, 48, 48, 48, 48, 48,
3476 /* h i j k l m n o */
3477 48, 48, 48, 48, 48, 48, 48, 48,
3478 /* p q r s t u v w */
3479 48, 48, 48, 48, 48, 48, 48, 48,
3480 /* x y z { | } ~ DEL */
3481 48, 48, 48, 123, 124, 125, 126, 1,
3482 /* x80 x81 x82 x83 IND NEL SSA ESA */
3483 1, 1, 1, 1, 1, 1, 1, 1,
3484 /* HTS HTJ VTS PLD PLU RI SS2 SS3 */
3485 1, 1, 1, 1, 1, 1, 1, 1,
3486 /* DCS PU1 PU2 STS CCH MW SPA EPA */
3487 1, 1, 1, 1, 1, 1, 1, 1,
3488 /* x98 x99 x9A CSI ST OSC PM APC */
3489 1, 1, 1, 1, 1, 1, 1, 1,
3490 /* - i c/ L ox Y- | So */
3491 160, 161, 162, 163, 164, 165, 166, 167,
3492 /* .. c0 ip << _ R0 - */
3493 168, 169, 170, 171, 172, 173, 174, 175,
3494 /* o +- 2 3 ' u q| . */
3495 176, 177, 178, 179, 180, 181, 182, 183,
3496 /* , 1 2 >> 1/4 1/2 3/4 ? */
3497 184, 185, 186, 187, 188, 189, 190, 191,
3498 /* A` A' A^ A~ A: Ao AE C, */
3499 48, 48, 48, 48, 48, 48, 48, 48,
3500 /* E` E' E^ E: I` I' I^ I: */
3501 48, 48, 48, 48, 48, 48, 48, 48,
3502 /* D- N~ O` O' O^ O~ O: X */
3503 48, 48, 48, 48, 48, 48, 48, 215,
3504 /* O/ U` U' U^ U: Y' P B */
3505 48, 48, 48, 48, 48, 48, 48, 48,
3506 /* a` a' a^ a~ a: ao ae c, */
3507 48, 48, 48, 48, 48, 48, 48, 48,
3508 /* e` e' e^ e: i` i' i^ i: */
3509 48, 48, 48, 48, 48, 48, 48, 48,
3510 /* d n~ o` o' o^ o~ o: -: */
3511 48, 48, 48, 48, 48, 48, 48, 247,
3512 /* o/ u` u' u^ u: y' P y: */
3513 48, 48, 48, 48, 48, 48, 48, 48};
3514 /* *INDENT-ON* */
3515
3516 int
SetCharacterClassRange(int low,int high,int value)3517 SetCharacterClassRange(int low, /* in range of [0..255] */
3518 int high,
3519 int value) /* arbitrary */
3520 {
3521
3522 if (low < 0 || high > 255 || high < low)
3523 return (-1);
3524
3525 for (; low <= high; low++)
3526 charClass[low] = value;
3527
3528 return (0);
3529 }
3530 #endif
3531
3532 static int
class_of(LineData * ld,CELL * cell)3533 class_of(LineData *ld, CELL *cell)
3534 {
3535 CELL temp = *cell;
3536 int result = 0;
3537
3538 #if OPT_DEC_CHRSET
3539 if (CSET_DOUBLE(GetLineDblCS(ld))) {
3540 temp.col /= 2;
3541 }
3542 #endif
3543 if (temp.col < (int) ld->lineSize)
3544 result = CharacterClass((int) (ld->charData[temp.col]));
3545 return result;
3546 }
3547
3548 #if OPT_WIDE_CHARS
3549 #define CClassSelects(name, cclass) \
3550 (CClassOf(name) == cclass \
3551 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3552 #else
3553 #define CClassSelects(name, cclass) \
3554 (class_of(ld.name, &((screen->name))) == cclass)
3555 #endif
3556
3557 #define CClassOf(name) class_of(ld.name, &((screen->name)))
3558
3559 #if OPT_REPORT_CCLASS
3560 static int
show_cclass_range(int lo,int hi)3561 show_cclass_range(int lo, int hi)
3562 {
3563 int cclass = CharacterClass(lo);
3564 int ident = (cclass == lo);
3565 int more = 0;
3566 if (ident) {
3567 int ch;
3568 for (ch = lo + 1; ch <= hi; ch++) {
3569 if (CharacterClass(ch) != ch) {
3570 ident = 0;
3571 break;
3572 }
3573 }
3574 if (ident && (hi < 255)) {
3575 ch = hi + 1;
3576 if (CharacterClass(ch) == ch) {
3577 if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3578 more = 1;
3579 }
3580 }
3581 }
3582 }
3583 if (!more) {
3584 if (lo == hi) {
3585 printf("\t%d", lo);
3586 } else {
3587 printf("\t%d-%d", lo, hi);
3588 }
3589 if (!ident)
3590 printf(":%d", cclass);
3591 if (hi < 255)
3592 printf(", \\");
3593 printf("\n");
3594 }
3595 return !more;
3596 }
3597
3598 void
report_char_class(XtermWidget xw)3599 report_char_class(XtermWidget xw)
3600 {
3601 /* simple table, to match documentation */
3602 static const char charnames[] =
3603 "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3604 " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3605 "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3606 "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3607 " SP\0" " !\0" " \"\0" " #\0" " $\0" " %\0" " &\0" " '\0"
3608 " (\0" " )\0" " *\0" " +\0" " ,\0" " -\0" " .\0" " /\0"
3609 " 0\0" " 1\0" " 2\0" " 3\0" " 4\0" " 5\0" " 6\0" " 7\0"
3610 " 8\0" " 9\0" " :\0" " ;\0" " <\0" " =\0" " >\0" " ?\0"
3611 " @\0" " A\0" " B\0" " C\0" " D\0" " E\0" " F\0" " G\0"
3612 " H\0" " I\0" " J\0" " K\0" " L\0" " M\0" " N\0" " O\0"
3613 " P\0" " Q\0" " R\0" " S\0" " T\0" " U\0" " V\0" " W\0"
3614 " X\0" " Y\0" " Z\0" " [\0" " \\\0" " ]\0" " ^\0" " _\0"
3615 " `\0" " a\0" " b\0" " c\0" " d\0" " e\0" " f\0" " g\0"
3616 " h\0" " i\0" " j\0" " k\0" " l\0" " m\0" " n\0" " o\0"
3617 " p\0" " q\0" " r\0" " s\0" " t\0" " u\0" " v\0" " w\0"
3618 " x\0" " y\0" " z\0" " {\0" " |\0" " }\0" " ~\0" "DEL\0"
3619 "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3620 "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3621 "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3622 "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3623 " -\0" " i\0" " c/\0" " L\0" " ox\0" " Y-\0" " |\0" " So\0"
3624 " ..\0" " c0\0" " ip\0" " <<\0" " _\0" " \0" " R0\0" " -\0"
3625 " o\0" " +-\0" " 2\0" " 3\0" " '\0" " u\0" " q|\0" " .\0"
3626 " ,\0" " 1\0" " 2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" " ?\0"
3627 " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3628 " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3629 " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" " X\0"
3630 " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" " P\0" " B\0"
3631 " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3632 " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3633 " d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3634 " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" " P\0" " y:\0";
3635 int ch, dh;
3636 int class_p;
3637
3638 (void) xw;
3639
3640 printf("static int charClass[256] = {\n");
3641 for (ch = 0; ch < 256; ++ch) {
3642 const char *s = charnames + (ch * 4);
3643 if ((ch & 7) == 0)
3644 printf("/*");
3645 printf(" %s ", s);
3646 if (((ch + 1) & 7) == 0) {
3647 printf("*/\n ");
3648 for (dh = ch - 7; dh <= ch; ++dh) {
3649 printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3650 }
3651 printf("\n");
3652 }
3653 }
3654
3655 /* print the table as if it were the charClass resource */
3656 printf("\n");
3657 printf("The table is equivalent to this \"charClass\" resource:\n");
3658 class_p = CharacterClass(dh = 0);
3659 for (ch = 0; ch < 256; ++ch) {
3660 int class_c = CharacterClass(ch);
3661 if (class_c != class_p) {
3662 if (show_cclass_range(dh, ch - 1)) {
3663 dh = ch;
3664 class_p = class_c;
3665 }
3666 }
3667 }
3668 if (dh < 255) {
3669 show_cclass_range(dh, 255);
3670 }
3671
3672 if_OPT_WIDE_CHARS(TScreenOf(xw), {
3673 /* if this is a wide-character configuration, print all intervals */
3674 report_wide_char_class();
3675 });
3676 }
3677 #endif
3678
3679 /*
3680 * If the given column is past the end of text on the given row, bump to the
3681 * beginning of the next line.
3682 */
3683 static Boolean
okPosition(TScreen * screen,LineData ** ld,CELL * cell)3684 okPosition(TScreen *screen,
3685 LineData **ld,
3686 CELL *cell)
3687 {
3688 Boolean result = True;
3689
3690 if (cell->row > screen->max_row) {
3691 result = False;
3692 TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
3693 } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3694 TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
3695 (LastTextCol(screen, *ld, cell->row) + 1)));
3696 if (cell->row < screen->max_row) {
3697 TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
3698 cell->col = 0;
3699 *ld = GET_LINEDATA(screen, ++cell->row);
3700 result = False;
3701 }
3702 }
3703 return result;
3704 }
3705
3706 static void
trimLastLine(TScreen * screen,LineData ** ld,CELL * last)3707 trimLastLine(TScreen *screen,
3708 LineData **ld,
3709 CELL *last)
3710 {
3711 if (screen->cutNewline && last->row < screen->max_row) {
3712 last->col = 0;
3713 *ld = GET_LINEDATA(screen, ++last->row);
3714 } else {
3715 last->col = LastTextCol(screen, *ld, last->row) + 1;
3716 }
3717 }
3718
3719 #if OPT_SELECT_REGEX
3720 /*
3721 * Returns the first row of a wrapped line.
3722 */
3723 static int
firstRowOfLine(TScreen * screen,int row,Bool visible)3724 firstRowOfLine(TScreen *screen, int row, Bool visible)
3725 {
3726 LineData *ld = 0;
3727 int limit = visible ? 0 : -screen->savedlines;
3728
3729 while (row > limit &&
3730 (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3731 LineTstWrapped(ld)) {
3732 --row;
3733 }
3734 return row;
3735 }
3736
3737 /*
3738 * Returns the last row of a wrapped line.
3739 */
3740 static int
lastRowOfLine(TScreen * screen,int row)3741 lastRowOfLine(TScreen *screen, int row)
3742 {
3743 LineData *ld;
3744
3745 while (row < screen->max_row &&
3746 (ld = GET_LINEDATA(screen, row)) != 0 &&
3747 LineTstWrapped(ld)) {
3748 ++row;
3749 }
3750 return row;
3751 }
3752
3753 /*
3754 * Returns the number of cells on the range of rows.
3755 */
3756 static unsigned
lengthOfLines(TScreen * screen,int firstRow,int lastRow)3757 lengthOfLines(TScreen *screen, int firstRow, int lastRow)
3758 {
3759 unsigned length = 0;
3760 int n;
3761
3762 for (n = firstRow; n <= lastRow; ++n) {
3763 LineData *ld = GET_LINEDATA(screen, n);
3764 int value = LastTextCol(screen, ld, n);
3765 if (value >= 0)
3766 length += (unsigned) (value + 1);
3767 }
3768 return length;
3769 }
3770
3771 /*
3772 * Make a copy of the wrapped-line which corresponds to the given row as a
3773 * string of bytes. Construct an index for the columns from the beginning of
3774 * the line.
3775 */
3776 static char *
make_indexed_text(TScreen * screen,int row,unsigned length,int * indexed)3777 make_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3778 {
3779 Char *result = 0;
3780 size_t need = (length + 1);
3781
3782 /*
3783 * Get a quick upper bound to the number of bytes needed, if the whole
3784 * string were UTF-8.
3785 */
3786 if_OPT_WIDE_CHARS(screen, {
3787 need *= ((screen->lineExtra + 1) * 6);
3788 });
3789
3790 if ((result = TypeCallocN(Char, need + 1)) != 0) {
3791 LineData *ld = GET_LINEDATA(screen, row);
3792 unsigned used = 0;
3793 Char *last = result;
3794
3795 do {
3796 int col = 0;
3797 int limit = LastTextCol(screen, ld, row);
3798
3799 while (col <= limit) {
3800 Char *next = last;
3801 unsigned data = ld->charData[col];
3802
3803 assert(col < (int) ld->lineSize);
3804 /* some internal points may not be drawn */
3805 if (data == 0)
3806 data = ' ';
3807
3808 if_WIDE_OR_NARROW(screen, {
3809 next = convertToUTF8(last, data);
3810 }
3811 , {
3812 *next++ = CharOf(data);
3813 });
3814
3815 if_OPT_WIDE_CHARS(screen, {
3816 size_t off;
3817 for_each_combData(off, ld) {
3818 data = ld->combData[off][col];
3819 if (data == 0)
3820 break;
3821 next = convertToUTF8(next, data);
3822 }
3823 });
3824
3825 indexed[used] = (int) (last - result);
3826 *next = 0;
3827 /* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3828 last = next;
3829 ++used;
3830 ++col;
3831 indexed[used] = (int) (next - result);
3832 }
3833 } while (used < length &&
3834 LineTstWrapped(ld) &&
3835 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3836 row < screen->max_row);
3837 }
3838 /* TRACE(("result:%s\n", result)); */
3839 return (char *) result;
3840 }
3841
3842 /*
3843 * Find the column given an offset into the character string by using the
3844 * index constructed in make_indexed_text().
3845 */
3846 static int
indexToCol(int * indexed,int len,int off)3847 indexToCol(int *indexed, int len, int off)
3848 {
3849 int col = 0;
3850 while (indexed[col] < len) {
3851 if (indexed[col] >= off)
3852 break;
3853 ++col;
3854 }
3855 return col;
3856 }
3857
3858 /*
3859 * Given a row number, and a column offset from that (which may be wrapped),
3860 * set the cell to the actual row/column values.
3861 */
3862 static void
columnToCell(TScreen * screen,int row,int col,CELL * cell)3863 columnToCell(TScreen *screen, int row, int col, CELL *cell)
3864 {
3865 while (row < screen->max_row) {
3866 CLineData *ld = GET_LINEDATA(screen, row);
3867 int last = LastTextCol(screen, ld, row);
3868
3869 /* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3870 if (col <= last) {
3871 break;
3872 }
3873 /*
3874 * Stop if the current row does not wrap (does not continue the current
3875 * line).
3876 */
3877 if (!LineTstWrapped(ld)) {
3878 col = last + 1;
3879 break;
3880 }
3881 col -= (last + 1);
3882 ++row;
3883 }
3884 if (col < 0)
3885 col = 0;
3886 cell->row = row;
3887 cell->col = col;
3888 }
3889
3890 /*
3891 * Given a cell, find the corresponding column offset.
3892 */
3893 static int
cellToColumn(TScreen * screen,CELL * cell)3894 cellToColumn(TScreen *screen, CELL *cell)
3895 {
3896 CLineData *ld = 0;
3897 int col = cell->col;
3898 int row = firstRowOfLine(screen, cell->row, False);
3899 while (row < cell->row) {
3900 ld = GET_LINEDATA(screen, row);
3901 col += LastTextCol(screen, ld, row++);
3902 }
3903 #if OPT_DEC_CHRSET
3904 if (ld == 0)
3905 ld = GET_LINEDATA(screen, row);
3906 if (CSET_DOUBLE(GetLineDblCS(ld)))
3907 col /= 2;
3908 #endif
3909 return col;
3910 }
3911
3912 static void
do_select_regex(TScreen * screen,CELL * startc,CELL * endc)3913 do_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3914 {
3915 LineData *ld = GET_LINEDATA(screen, startc->row);
3916 int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3917 char *expr = screen->selectExpr[inx];
3918 regex_t preg;
3919 regmatch_t match;
3920
3921 TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3922 if (okPosition(screen, &ld, startc) && expr != 0) {
3923 if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3924 int firstRow = firstRowOfLine(screen, startc->row, True);
3925 int lastRow = lastRowOfLine(screen, firstRow);
3926 unsigned size = lengthOfLines(screen, firstRow, lastRow);
3927 int actual = cellToColumn(screen, startc);
3928 int *indexed;
3929
3930 TRACE(("regcomp ok rows %d..%d bytes %d\n",
3931 firstRow, lastRow, size));
3932
3933 if ((indexed = TypeCallocN(int, size + 1)) != 0) {
3934 char *search;
3935 if ((search = make_indexed_text(screen,
3936 firstRow,
3937 size,
3938 indexed)) != 0) {
3939 int len = (int) strlen(search);
3940 int col;
3941 int best_col = -1;
3942 int best_len = -1;
3943
3944 startc->row = 0;
3945 startc->col = 0;
3946 endc->row = 0;
3947 endc->col = 0;
3948
3949 for (col = 0; indexed[col] < len; ++col) {
3950 if (regexec(&preg,
3951 search + indexed[col],
3952 (size_t) 1, &match, 0) == 0) {
3953 int start_inx = (int) (match.rm_so + indexed[col]);
3954 int finis_inx = (int) (match.rm_eo + indexed[col]);
3955 int start_col = indexToCol(indexed, len, start_inx);
3956 int finis_col = indexToCol(indexed, len, finis_inx);
3957
3958 if (start_col <= actual &&
3959 actual <= finis_col) {
3960 int test = finis_col - start_col;
3961 if (best_len < test) {
3962 best_len = test;
3963 best_col = start_col;
3964 TRACE(("match column %d len %d\n",
3965 best_col,
3966 best_len));
3967 }
3968 }
3969 }
3970 }
3971 if (best_col >= 0) {
3972 int best_nxt = best_col + best_len;
3973 columnToCell(screen, firstRow, best_col, startc);
3974 columnToCell(screen, firstRow, best_nxt, endc);
3975 TRACE(("search::%s\n", search));
3976 TRACE(("indexed:%d..%d -> %d..%d\n",
3977 best_col, best_nxt,
3978 indexed[best_col],
3979 indexed[best_nxt]));
3980 TRACE(("matched:%d:%s\n",
3981 indexed[best_nxt] + 1 -
3982 indexed[best_col],
3983 visibleChars((Char *) (search + indexed[best_col]),
3984 (unsigned) (indexed[best_nxt] +
3985 1 -
3986 indexed[best_col]))));
3987 }
3988 free(search);
3989 }
3990 free(indexed);
3991 #if OPT_DEC_CHRSET
3992 if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3993 if (CSET_DOUBLE(GetLineDblCS(ld)))
3994 startc->col *= 2;
3995 }
3996 if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3997 if (CSET_DOUBLE(GetLineDblCS(ld)))
3998 endc->col *= 2;
3999 }
4000 #endif
4001 }
4002 regfree(&preg);
4003 }
4004 }
4005 }
4006 #endif /* OPT_SELECT_REGEX */
4007
4008 #define InitRow(name) \
4009 ld.name = GET_LINEDATA(screen, screen->name.row)
4010
4011 #define NextRow(name) \
4012 ld.name = GET_LINEDATA(screen, ++screen->name.row)
4013
4014 #define PrevRow(name) \
4015 ld.name = GET_LINEDATA(screen, --screen->name.row)
4016
4017 #define MoreRows(name) \
4018 (screen->name.row < screen->max_row)
4019
4020 #define isPrevWrapped(name) \
4021 (screen->name.row > 0 \
4022 && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
4023 && LineTstWrapped(ltmp))
4024
4025 /*
4026 * sets startSel endSel
4027 * ensuring that they have legal values
4028 */
4029 static void
ComputeSelect(XtermWidget xw,CELL * startc,CELL * endc,Bool extend,Bool normal)4030 ComputeSelect(XtermWidget xw,
4031 CELL *startc,
4032 CELL *endc,
4033 Bool extend,
4034 Bool normal)
4035 {
4036 TScreen *screen = TScreenOf(xw);
4037
4038 int cclass;
4039 CELL first = *startc;
4040 CELL last = *endc;
4041 Boolean ignored = False;
4042
4043 struct {
4044 LineData *startSel;
4045 LineData *endSel;
4046 } ld;
4047 LineData *ltmp;
4048
4049 TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
4050 first.row, first.col,
4051 last.row, last.col,
4052 extend ? "" : "no"));
4053
4054 #if OPT_WIDE_CHARS
4055 if (first.col > 1
4056 && isWideCell(first.row, first.col - 1)
4057 && XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
4058 TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
4059 first.col -= 1;
4060 if (last.col == (first.col + 1))
4061 last.col--;
4062 }
4063
4064 if (last.col > 1
4065 && isWideCell(last.row, last.col - 1)
4066 && XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
4067 last.col += 1;
4068 }
4069 #endif
4070
4071 if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
4072 screen->startSel = screen->startRaw = first;
4073 screen->endSel = screen->endRaw = last;
4074 } else { /* Swap them */
4075 screen->startSel = screen->startRaw = last;
4076 screen->endSel = screen->endRaw = first;
4077 }
4078
4079 InitRow(startSel);
4080 InitRow(endSel);
4081
4082 switch (screen->selectUnit) {
4083 case Select_CHAR:
4084 (void) okPosition(screen, &(ld.startSel), &(screen->startSel));
4085 (void) okPosition(screen, &(ld.endSel), &(screen->endSel));
4086 break;
4087
4088 case Select_WORD:
4089 TRACE(("Select_WORD\n"));
4090 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4091 CELL mark;
4092 cclass = CClassOf(startSel);
4093 TRACE(("...starting with class %d\n", cclass));
4094 do {
4095 mark = screen->startSel;
4096 --screen->startSel.col;
4097 if (screen->startSel.col < 0
4098 && isPrevWrapped(startSel)) {
4099 PrevRow(startSel);
4100 screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
4101 }
4102 } while (screen->startSel.col >= 0
4103 && CClassSelects(startSel, cclass));
4104 if (normal)
4105 ++screen->startSel.col;
4106 else
4107 screen->startSel = mark;
4108 }
4109 #if OPT_WIDE_CHARS
4110 #define SkipHiddenCell(mark) \
4111 if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
4112 mark.col++
4113 #else
4114 #define SkipHiddenCell(mark) /* nothing */
4115 #endif
4116 SkipHiddenCell(screen->startSel);
4117
4118 if (!normal) {
4119 screen->endSel = screen->startSel;
4120 ld.endSel = ld.startSel;
4121 }
4122
4123 if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
4124 int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4125 cclass = CClassOf(endSel);
4126 TRACE(("...ending with class %d\n", cclass));
4127 do {
4128 ++screen->endSel.col;
4129 if (screen->endSel.col > length
4130 && LineTstWrapped(ld.endSel)) {
4131 if (!MoreRows(endSel))
4132 break;
4133 screen->endSel.col = 0;
4134 NextRow(endSel);
4135 length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4136 }
4137 } while (screen->endSel.col <= length
4138 && CClassSelects(endSel, cclass));
4139 if (normal
4140 && screen->endSel.col > length + 1
4141 && MoreRows(endSel)) {
4142 screen->endSel.col = 0;
4143 NextRow(endSel);
4144 }
4145 }
4146 SkipHiddenCell(screen->endSel);
4147
4148 screen->saveStartW = screen->startSel;
4149 break;
4150
4151 case Select_LINE:
4152 TRACE(("Select_LINE\n"));
4153 while (LineTstWrapped(ld.endSel)
4154 && MoreRows(endSel)) {
4155 NextRow(endSel);
4156 }
4157 if (screen->cutToBeginningOfLine
4158 || screen->startSel.row < screen->saveStartW.row) {
4159 screen->startSel.col = 0;
4160 while (isPrevWrapped(startSel)) {
4161 PrevRow(startSel);
4162 }
4163 } else if (!extend) {
4164 if ((first.row < screen->saveStartW.row)
4165 || (isSameRow(&first, &(screen->saveStartW))
4166 && first.col < screen->saveStartW.col)) {
4167 screen->startSel.col = 0;
4168 while (isPrevWrapped(startSel)) {
4169 PrevRow(startSel);
4170 }
4171 } else {
4172 screen->startSel = screen->saveStartW;
4173 }
4174 }
4175 trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4176 break;
4177
4178 case Select_GROUP: /* paragraph */
4179 TRACE(("Select_GROUP\n"));
4180 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4181 /* scan backward for beginning of group */
4182 while (screen->startSel.row > 0 &&
4183 (LastTextCol(screen, ld.startSel, screen->startSel.row -
4184 1) > 0 ||
4185 isPrevWrapped(startSel))) {
4186 PrevRow(startSel);
4187 }
4188 screen->startSel.col = 0;
4189 /* scan forward for end of group */
4190 while (MoreRows(endSel) &&
4191 (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
4192 0 ||
4193 LineTstWrapped(ld.endSel))) {
4194 NextRow(endSel);
4195 }
4196 trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4197 }
4198 break;
4199
4200 case Select_PAGE: /* everything one can see */
4201 TRACE(("Select_PAGE\n"));
4202 screen->startSel.row = 0;
4203 screen->startSel.col = 0;
4204 screen->endSel.row = MaxRows(screen);
4205 screen->endSel.col = 0;
4206 break;
4207
4208 case Select_ALL: /* counts scrollback if in normal screen */
4209 TRACE(("Select_ALL\n"));
4210 screen->startSel.row = -screen->savedlines;
4211 screen->startSel.col = 0;
4212 screen->endSel.row = MaxRows(screen);
4213 screen->endSel.col = 0;
4214 break;
4215
4216 #if OPT_SELECT_REGEX
4217 case Select_REGEX:
4218 do_select_regex(screen, &(screen->startSel), &(screen->endSel));
4219 break;
4220 #endif
4221
4222 case NSELECTUNITS: /* always ignore */
4223 ignored = True;
4224 break;
4225 }
4226
4227 if (!ignored) {
4228 /* check boundaries */
4229 ScrollSelection(screen, 0, False);
4230 TrackText(xw, &(screen->startSel), &(screen->endSel));
4231 }
4232
4233 return;
4234 }
4235
4236 /* Guaranteed (first.row, first.col) <= (last.row, last.col) */
4237 static void
TrackText(XtermWidget xw,const CELL * firstp,const CELL * lastp)4238 TrackText(XtermWidget xw,
4239 const CELL *firstp,
4240 const CELL *lastp)
4241 {
4242 TScreen *screen = TScreenOf(xw);
4243 int from, to;
4244 CELL old_start, old_end;
4245 CELL first = *firstp;
4246 CELL last = *lastp;
4247
4248 TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
4249 first.row, first.col, last.row, last.col));
4250
4251 old_start = screen->startH;
4252 old_end = screen->endH;
4253 TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
4254 old_start.row, old_start.col,
4255 old_end.row, old_end.col));
4256 if (isSameCELL(&first, &old_start) &&
4257 isSameCELL(&last, &old_end)) {
4258 return;
4259 }
4260
4261 screen->startH = first;
4262 screen->endH = last;
4263 from = Coordinate(screen, &screen->startH);
4264 to = Coordinate(screen, &screen->endH);
4265 if (to <= screen->startHCoord || from > screen->endHCoord) {
4266 /* No overlap whatsoever between old and new hilite */
4267 ReHiliteText(xw, &old_start, &old_end);
4268 ReHiliteText(xw, &first, &last);
4269 } else {
4270 if (from < screen->startHCoord) {
4271 /* Extend left end */
4272 ReHiliteText(xw, &first, &old_start);
4273 } else if (from > screen->startHCoord) {
4274 /* Shorten left end */
4275 ReHiliteText(xw, &old_start, &first);
4276 }
4277 if (to > screen->endHCoord) {
4278 /* Extend right end */
4279 ReHiliteText(xw, &old_end, &last);
4280 } else if (to < screen->endHCoord) {
4281 /* Shorten right end */
4282 ReHiliteText(xw, &last, &old_end);
4283 }
4284 }
4285 screen->startHCoord = from;
4286 screen->endHCoord = to;
4287 }
4288
4289 static void
UnHiliteText(XtermWidget xw)4290 UnHiliteText(XtermWidget xw)
4291 {
4292 TrackText(xw, &zeroCELL, &zeroCELL);
4293 }
4294
4295 /* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
4296 static void
ReHiliteText(XtermWidget xw,CELL * firstp,CELL * lastp)4297 ReHiliteText(XtermWidget xw,
4298 CELL *firstp,
4299 CELL *lastp)
4300 {
4301 TScreen *screen = TScreenOf(xw);
4302 CELL first = *firstp;
4303 CELL last = *lastp;
4304
4305 TRACE(("ReHiliteText from %d.%d to %d.%d\n",
4306 first.row, first.col, last.row, last.col));
4307
4308 if (first.row < 0)
4309 first.row = first.col = 0;
4310 else if (first.row > screen->max_row)
4311 return; /* nothing to do, since last.row >= first.row */
4312
4313 if (last.row < 0)
4314 return; /* nothing to do, since first.row <= last.row */
4315 else if (last.row > screen->max_row) {
4316 last.row = screen->max_row;
4317 last.col = MaxCols(screen);
4318 }
4319 if (isSameCELL(&first, &last))
4320 return;
4321
4322 if (!isSameRow(&first, &last)) { /* do multiple rows */
4323 int i;
4324 if ((i = screen->max_col - first.col + 1) > 0) { /* first row */
4325 ScrnRefresh(xw, first.row, first.col, 1, i, True);
4326 }
4327 if ((i = last.row - first.row - 1) > 0) { /* middle rows */
4328 ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
4329 }
4330 if (last.col > 0 && last.row <= screen->max_row) { /* last row */
4331 ScrnRefresh(xw, last.row, 0, 1, last.col, True);
4332 }
4333 } else { /* do single row */
4334 ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
4335 }
4336 }
4337
4338 /*
4339 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
4340 * and that both points are valid
4341 * (may have cell->row = screen->max_row+1, cell->col = 0).
4342 */
4343 static void
SaltTextAway(XtermWidget xw,int which,CELL * cellc,CELL * cell)4344 SaltTextAway(XtermWidget xw,
4345 int which,
4346 CELL *cellc,
4347 CELL *cell)
4348 {
4349 TScreen *screen = TScreenOf(xw);
4350 SelectedCells *scp;
4351 int i;
4352 int eol;
4353 int need = 0;
4354 size_t have = 0;
4355 Char *line;
4356 Char *lp;
4357 CELL first = *cellc;
4358 CELL last = *cell;
4359
4360 if (which < 0 || which >= MAX_SELECTIONS) {
4361 TRACE(("SaltTextAway - which selection?\n"));
4362 return;
4363 }
4364 scp = &(screen->selected_cells[which]);
4365
4366 TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
4367 which, first.row, first.col, last.row, last.col));
4368
4369 if (isSameRow(&first, &last) && first.col > last.col) {
4370 int tmp;
4371 EXCHANGE(first.col, last.col, tmp);
4372 }
4373
4374 --last.col;
4375 /* first we need to know how long the string is before we can save it */
4376
4377 if (isSameRow(&last, &first)) {
4378 need = Length(screen, first.row, first.col, last.col);
4379 } else { /* two cases, cut is on same line, cut spans multiple lines */
4380 need += Length(screen, first.row, first.col, screen->max_col) + 1;
4381 for (i = first.row + 1; i < last.row; i++)
4382 need += Length(screen, i, 0, screen->max_col) + 1;
4383 if (last.col >= 0)
4384 need += Length(screen, last.row, 0, last.col);
4385 }
4386
4387 /* UTF-8 may require more space */
4388 if_OPT_WIDE_CHARS(screen, {
4389 if (need > 0) {
4390 if (screen->max_combining > 0)
4391 need += screen->max_combining;
4392 need *= 6;
4393 }
4394 });
4395
4396 /* now get some memory to save it in */
4397 if (need < 0)
4398 return;
4399
4400 if (scp->data_limit <= (unsigned) need) {
4401 if ((line = (Char *) malloc((size_t) need + 1)) == 0)
4402 SysError(ERROR_BMALLOC2);
4403 free(scp->data_buffer);
4404 scp->data_buffer = line;
4405 scp->data_limit = (size_t) (need + 1);
4406 } else {
4407 line = scp->data_buffer;
4408 }
4409
4410 if (line == 0)
4411 return;
4412
4413 line[need] = '\0'; /* make sure it is null terminated */
4414 lp = line; /* lp points to where to save the text */
4415 if (isSameRow(&last, &first)) {
4416 lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
4417 } else {
4418 lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
4419 if (eol)
4420 *lp++ = '\n'; /* put in newline at end of line */
4421 for (i = first.row + 1; i < last.row; i++) {
4422 lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
4423 if (eol)
4424 *lp++ = '\n';
4425 }
4426 if (last.col >= 0)
4427 lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
4428 }
4429 *lp = '\0'; /* make sure we have end marked */
4430
4431 have = (size_t) (lp - line);
4432 /*
4433 * Scanning the buffer twice is unnecessary. Discard unwanted memory if
4434 * the estimate is too-far off.
4435 */
4436 if ((have * 2) < (size_t) need) {
4437 Char *next;
4438 scp->data_limit = have + 1;
4439 next = realloc(line, scp->data_limit);
4440 if (next == NULL) {
4441 free(line);
4442 scp->data_length = 0;
4443 scp->data_limit = 0;
4444 }
4445 scp->data_buffer = next;
4446 }
4447 scp->data_length = have;
4448
4449 TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
4450 visibleChars(scp->data_buffer, (unsigned) have)));
4451 }
4452
4453 #if OPT_PASTE64
4454 void
ClearSelectionBuffer(TScreen * screen,String selection)4455 ClearSelectionBuffer(TScreen *screen, String selection)
4456 {
4457 int which = TargetToSelection(screen, selection);
4458 SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4459 FreeAndNull(scp->data_buffer);
4460 scp->data_limit = 0;
4461 scp->data_length = 0;
4462 screen->base64_count = 0;
4463 }
4464
4465 static void
AppendStrToSelectionBuffer(SelectedCells * scp,Char * text,size_t len)4466 AppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
4467 {
4468 if (len != 0) {
4469 size_t j = (scp->data_length + len);
4470 size_t k = j + (j >> 2) + 80;
4471 if (j + 1 >= scp->data_limit) {
4472 Char *line;
4473 if (!scp->data_length) {
4474 line = (Char *) malloc(k);
4475 } else {
4476 line = (Char *) realloc(scp->data_buffer, k);
4477 }
4478 if (line == 0)
4479 SysError(ERROR_BMALLOC2);
4480 scp->data_buffer = line;
4481 scp->data_limit = k;
4482 }
4483 if (scp->data_buffer != 0) {
4484 memcpy(scp->data_buffer + scp->data_length, text, len);
4485 scp->data_length += len;
4486 scp->data_buffer[scp->data_length] = 0;
4487 }
4488 }
4489 }
4490
4491 void
AppendToSelectionBuffer(TScreen * screen,unsigned c,String selection)4492 AppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
4493 {
4494 int which = TargetToSelection(screen, selection);
4495 SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4496 unsigned six;
4497 Char ch;
4498
4499 /* Decode base64 character */
4500 if (c >= 'A' && c <= 'Z')
4501 six = c - 'A';
4502 else if (c >= 'a' && c <= 'z')
4503 six = c - 'a' + 26;
4504 else if (c >= '0' && c <= '9')
4505 six = c - '0' + 52;
4506 else if (c == '+')
4507 six = 62;
4508 else if (c == '/')
4509 six = 63;
4510 else
4511 return;
4512
4513 /* Accumulate bytes */
4514 switch (screen->base64_count) {
4515 case 0:
4516 screen->base64_accu = six;
4517 screen->base64_count = 6;
4518 break;
4519
4520 case 2:
4521 ch = CharOf((screen->base64_accu << 6) + six);
4522 screen->base64_count = 0;
4523 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4524 break;
4525
4526 case 4:
4527 ch = CharOf((screen->base64_accu << 4) + (six >> 2));
4528 screen->base64_accu = (six & 0x3);
4529 screen->base64_count = 2;
4530 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4531 break;
4532
4533 case 6:
4534 ch = CharOf((screen->base64_accu << 2) + (six >> 4));
4535 screen->base64_accu = (six & 0xF);
4536 screen->base64_count = 4;
4537 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4538 break;
4539 }
4540 }
4541
4542 void
CompleteSelection(XtermWidget xw,String * args,Cardinal len)4543 CompleteSelection(XtermWidget xw, String *args, Cardinal len)
4544 {
4545 TScreen *screen = TScreenOf(xw);
4546
4547 screen->base64_count = 0;
4548 screen->base64_accu = 0;
4549 _OwnSelection(xw, args, len);
4550 }
4551 #endif /* OPT_PASTE64 */
4552
4553 static Bool
_ConvertSelectionHelper(Widget w,SelectedCells * scp,Atom * type,XtPointer * value,unsigned long * length,int * format,int (* conversion_function)(Display *,char **,int,XICCEncodingStyle,XTextProperty *),XICCEncodingStyle conversion_style)4554 _ConvertSelectionHelper(Widget w,
4555 SelectedCells * scp,
4556 Atom *type,
4557 XtPointer *value,
4558 unsigned long *length,
4559 int *format,
4560 int (*conversion_function) (Display *,
4561 char **, int,
4562 XICCEncodingStyle,
4563 XTextProperty *),
4564 XICCEncodingStyle conversion_style)
4565 {
4566 *value = 0;
4567 *length = 0;
4568 *type = 0;
4569 *format = 0;
4570
4571 if (getXtermWidget(w) != 0) {
4572 Display *dpy = XtDisplay(w);
4573 XTextProperty textprop;
4574 int out_n = 0;
4575 char *result = 0;
4576 char *the_data = (char *) scp->data_buffer;
4577 char *the_next;
4578 unsigned long remaining = scp->data_length;
4579
4580 TRACE(("converting %ld:'%s'\n",
4581 (long) scp->data_length,
4582 visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
4583 /*
4584 * For most selections, we can convert in one pass. It is possible
4585 * that some applications contain embedded nulls, e.g., using xterm's
4586 * paste64 feature. For those cases, we will build up the result in
4587 * parts.
4588 */
4589 if (memchr(the_data, 0, scp->data_length) != 0) {
4590 TRACE(("selection contains embedded nulls\n"));
4591 result = calloc(scp->data_length + 1, sizeof(char));
4592 }
4593
4594 next_try:
4595 memset(&textprop, 0, sizeof(textprop));
4596 if (conversion_function(dpy, &the_data, 1,
4597 conversion_style,
4598 &textprop) >= Success) {
4599 if ((result != 0)
4600 && (textprop.value != 0)
4601 && (textprop.format == 8)) {
4602 char *text_values = (char *) textprop.value;
4603 unsigned long in_n;
4604
4605 if (out_n == 0) {
4606 *value = result;
4607 *type = textprop.encoding;
4608 *format = textprop.format;
4609 }
4610 for (in_n = 0; in_n < textprop.nitems; ++in_n) {
4611 result[out_n++] = text_values[in_n];
4612 }
4613 *length += textprop.nitems;
4614 if ((the_next = memchr(the_data, 0, remaining)) != 0) {
4615 unsigned long this_was = (unsigned long) (the_next - the_data);
4616 this_was++;
4617 the_data += this_was;
4618 remaining -= this_was;
4619 result[out_n++] = 0;
4620 *length += 1;
4621 if (remaining)
4622 goto next_try;
4623 }
4624 return True;
4625 } else {
4626 free(result);
4627 *value = (XtPointer) textprop.value;
4628 *length = textprop.nitems;
4629 *type = textprop.encoding;
4630 *format = textprop.format;
4631 return True;
4632 }
4633 }
4634 free(result);
4635 }
4636 return False;
4637 }
4638
4639 static Boolean
SaveConvertedLength(XtPointer * target,unsigned long source)4640 SaveConvertedLength(XtPointer *target, unsigned long source)
4641 {
4642 Boolean result = False;
4643
4644 *target = XtMalloc(4);
4645 if (*target != 0) {
4646 result = True;
4647 if (sizeof(unsigned long) == 4) {
4648 *(unsigned long *) *target = source;
4649 } else if (sizeof(unsigned) == 4) {
4650 *(unsigned *) *target = (unsigned) source;
4651 } else if (sizeof(unsigned short) == 4) {
4652 *(unsigned short *) *target = (unsigned short) source;
4653 } else {
4654 /* FIXME - does this depend on byte-order? */
4655 unsigned long temp = source;
4656 memcpy((char *) *target,
4657 ((char *) &temp) + sizeof(temp) - 4,
4658 (size_t) 4);
4659 }
4660 }
4661 return result;
4662 }
4663
4664 #define keepClipboard(d,atom) ((screen->keepClipboard) && \
4665 (atom == XA_CLIPBOARD(d)))
4666
4667 static Boolean
ConvertSelection(Widget w,Atom * selection,Atom * target,Atom * type,XtPointer * value,unsigned long * length,int * format)4668 ConvertSelection(Widget w,
4669 Atom *selection,
4670 Atom *target,
4671 Atom *type,
4672 XtPointer *value,
4673 unsigned long *length,
4674 int *format)
4675 {
4676 Display *dpy = XtDisplay(w);
4677 TScreen *screen;
4678 SelectedCells *scp;
4679 Bool result = False;
4680
4681 Char *data;
4682 unsigned long data_length;
4683
4684 XtermWidget xw;
4685
4686 if ((xw = getXtermWidget(w)) == 0)
4687 return False;
4688
4689 screen = TScreenOf(xw);
4690
4691 TRACE(("ConvertSelection %s -> %s\n",
4692 TraceAtomName(screen->display, *selection),
4693 visibleSelectionTarget(dpy, *target)));
4694
4695 if (keepClipboard(dpy, *selection)) {
4696 TRACE(("asked for clipboard\n"));
4697 scp = &(screen->clipboard_data);
4698 } else {
4699 TRACE(("asked for selection\n"));
4700 scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
4701 }
4702
4703 data = scp->data_buffer;
4704 data_length = scp->data_length;
4705 if (data == NULL) {
4706 TRACE(("...no selection-data\n"));
4707 return False;
4708 }
4709
4710 if (*target == XA_TARGETS(dpy)) {
4711 Atom *targetP;
4712 XPointer std_return = 0;
4713 unsigned long std_length;
4714
4715 if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4716 target, type, &std_return,
4717 &std_length, format)) {
4718 Atom *my_targets = _SelectionTargets(w);
4719 Atom *allocP;
4720 Atom *std_targets;
4721
4722 TRACE(("XmuConvertStandardSelection - success\n"));
4723 std_targets = (Atom *) (void *) (std_return);
4724 *length = std_length + 6;
4725
4726 targetP = TypeXtMallocN(Atom, *length);
4727 allocP = targetP;
4728
4729 *value = (XtPointer) targetP;
4730
4731 if (my_targets != 0) {
4732 while (*my_targets != None) {
4733 *targetP++ = *my_targets++;
4734 }
4735 }
4736 *targetP++ = XA_LENGTH(dpy);
4737 *targetP++ = XA_LIST_LENGTH(dpy);
4738
4739 *length = std_length + (unsigned long) (targetP - allocP);
4740
4741 memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4742 XtFree((char *) std_targets);
4743 *type = XA_ATOM;
4744 *format = 32;
4745 result = True;
4746 } else {
4747 TRACE(("XmuConvertStandardSelection - failed\n"));
4748 }
4749 }
4750 #if OPT_WIDE_CHARS
4751 else if (screen->wide_chars && *target == XA_STRING) {
4752 result =
4753 _ConvertSelectionHelper(w, scp,
4754 type, value, length, format,
4755 Xutf8TextListToTextProperty,
4756 XStringStyle);
4757 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4758 } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4759 result =
4760 _ConvertSelectionHelper(w, scp,
4761 type, value, length, format,
4762 Xutf8TextListToTextProperty,
4763 XUTF8StringStyle);
4764 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4765 } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4766 result =
4767 _ConvertSelectionHelper(w, scp,
4768 type, value, length, format,
4769 Xutf8TextListToTextProperty,
4770 XStdICCTextStyle);
4771 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4772 } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4773 result =
4774 _ConvertSelectionHelper(w, scp,
4775 type, value, length, format,
4776 Xutf8TextListToTextProperty,
4777 XCompoundTextStyle);
4778 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4779 }
4780 #endif
4781
4782 else if (*target == XA_STRING) { /* not wide_chars */
4783 /* We can only reach this point if the selection requestor
4784 requested STRING before any of TEXT, COMPOUND_TEXT or
4785 UTF8_STRING. We therefore assume that the requestor is not
4786 properly internationalised, and dump raw eight-bit data
4787 with no conversion into the selection. Yes, this breaks
4788 the ICCCM in non-Latin-1 locales. */
4789 *type = XA_STRING;
4790 *value = (XtPointer) data;
4791 *length = data_length;
4792 *format = 8;
4793 result = True;
4794 TRACE(("...raw 8-bit data:%d\n", result));
4795 } else if (*target == XA_TEXT(dpy)) { /* not wide_chars */
4796 result =
4797 _ConvertSelectionHelper(w, scp,
4798 type, value, length, format,
4799 XmbTextListToTextProperty,
4800 XStdICCTextStyle);
4801 TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4802 } else if (*target == XA_COMPOUND_TEXT(dpy)) { /* not wide_chars */
4803 result =
4804 _ConvertSelectionHelper(w, scp,
4805 type, value, length, format,
4806 XmbTextListToTextProperty,
4807 XCompoundTextStyle);
4808 TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4809 }
4810 #ifdef X_HAVE_UTF8_STRING
4811 else if (*target == XA_UTF8_STRING(dpy)) { /* not wide_chars */
4812 result =
4813 _ConvertSelectionHelper(w, scp,
4814 type, value, length, format,
4815 XmbTextListToTextProperty,
4816 XUTF8StringStyle);
4817 TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4818 }
4819 #endif
4820 else if (*target == XA_LIST_LENGTH(dpy)) {
4821 result = SaveConvertedLength(value, (unsigned long) 1);
4822 *type = XA_INTEGER;
4823 *length = 1;
4824 *format = 32;
4825 TRACE(("...list of values:%d\n", result));
4826 } else if (*target == XA_LENGTH(dpy)) {
4827 /* This value is wrong if we have UTF-8 text */
4828 result = SaveConvertedLength(value, scp->data_length);
4829 *type = XA_INTEGER;
4830 *length = 1;
4831 *format = 32;
4832 TRACE(("...list of values:%d\n", result));
4833 } else if (XmuConvertStandardSelection(w,
4834 screen->selection_time, selection,
4835 target, type, (XPointer *) value,
4836 length, format)) {
4837 result = True;
4838 TRACE(("...XmuConvertStandardSelection:%d\n", result));
4839 }
4840
4841 /* else */
4842 return (Boolean) result;
4843 }
4844
4845 static void
LoseSelection(Widget w,Atom * selection)4846 LoseSelection(Widget w, Atom *selection)
4847 {
4848 TScreen *screen;
4849 Atom *atomP;
4850 Cardinal i;
4851
4852 XtermWidget xw;
4853
4854 if ((xw = getXtermWidget(w)) == 0)
4855 return;
4856
4857 screen = TScreenOf(xw);
4858 TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
4859
4860 for (i = 0, atomP = screen->selection_atoms;
4861 i < screen->selection_count; i++, atomP++) {
4862 if (*selection == *atomP)
4863 *atomP = (Atom) 0;
4864 if (CutBuffer(*atomP) >= 0) {
4865 *atomP = (Atom) 0;
4866 }
4867 }
4868
4869 for (i = screen->selection_count; i; i--) {
4870 if (screen->selection_atoms[i - 1] != 0)
4871 break;
4872 }
4873 screen->selection_count = i;
4874
4875 for (i = 0, atomP = screen->selection_atoms;
4876 i < screen->selection_count; i++, atomP++) {
4877 if (*atomP == (Atom) 0) {
4878 *atomP = screen->selection_atoms[--screen->selection_count];
4879 }
4880 }
4881
4882 if (screen->selection_count == 0)
4883 UnHiliteText(xw);
4884 }
4885
4886 /* ARGSUSED */
4887 static void
SelectionDone(Widget w GCC_UNUSED,Atom * selection GCC_UNUSED,Atom * target GCC_UNUSED)4888 SelectionDone(Widget w GCC_UNUSED,
4889 Atom *selection GCC_UNUSED,
4890 Atom *target GCC_UNUSED)
4891 {
4892 /* empty proc so Intrinsics know we want to keep storage */
4893 TRACE(("SelectionDone\n"));
4894 }
4895
4896 static void
_OwnSelection(XtermWidget xw,String * selections,Cardinal count)4897 _OwnSelection(XtermWidget xw,
4898 String *selections,
4899 Cardinal count)
4900 {
4901 TScreen *screen = TScreenOf(xw);
4902 Display *dpy = screen->display;
4903 Atom *atoms = screen->selection_atoms;
4904 Cardinal i;
4905 Bool have_selection = False;
4906 SelectedCells *scp;
4907
4908 if (count == 0)
4909 return;
4910
4911 TRACE(("_OwnSelection count %d\n", count));
4912 selections = MapSelections(xw, selections, count);
4913
4914 if (count > screen->sel_atoms_size) {
4915 XtFree((char *) atoms);
4916 atoms = TypeXtMallocN(Atom, count);
4917 screen->selection_atoms = atoms;
4918 screen->sel_atoms_size = count;
4919 }
4920 XmuInternStrings(dpy, selections, count, atoms);
4921 for (i = 0; i < count; i++) {
4922 int cutbuffer = CutBuffer(atoms[i]);
4923 if (cutbuffer >= 0) {
4924 unsigned long limit =
4925 (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
4926 scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
4927 if (scp->data_length > limit) {
4928 TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4929 (unsigned long) scp->data_length, cutbuffer));
4930 xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4931 (unsigned long) scp->data_length, cutbuffer);
4932 } else {
4933 /* This used to just use the UTF-8 data, which was totally
4934 * broken as not even the corresponding paste code in xterm
4935 * understood this! So now it converts to Latin1 first.
4936 * Robert Brady, 2000-09-05
4937 */
4938 unsigned long length = scp->data_length;
4939 Char *data = scp->data_buffer;
4940 if_OPT_WIDE_CHARS((screen), {
4941 data = UTF8toLatin1(screen, data, length, &length);
4942 });
4943 TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4944 XStoreBuffer(dpy,
4945 (char *) data,
4946 (int) length,
4947 cutbuffer);
4948 }
4949 } else {
4950 int which = AtomToSelection(dpy, atoms[i]);
4951 if (keepClipboard(dpy, atoms[i])) {
4952 Char *buf;
4953 SelectedCells *tcp = &(screen->clipboard_data);
4954 TRACE(("saving selection to clipboard buffer\n"));
4955 scp = &(screen->selected_cells[CLIPBOARD_CODE]);
4956 if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0)
4957 SysError(ERROR_BMALLOC2);
4958
4959 free(tcp->data_buffer);
4960 memcpy(buf, scp->data_buffer, scp->data_length);
4961 tcp->data_buffer = buf;
4962 tcp->data_limit = scp->data_length;
4963 tcp->data_length = scp->data_length;
4964 }
4965 scp = &(screen->selected_cells[which]);
4966 if (scp->data_length == 0) {
4967 TRACE(("XtDisownSelection(%s, @%ld)\n",
4968 TraceAtomName(screen->display, atoms[i]),
4969 (long) screen->selection_time));
4970 XtDisownSelection((Widget) xw,
4971 atoms[i],
4972 screen->selection_time);
4973 } else if (!screen->replyToEmacs && atoms[i] != 0) {
4974 TRACE(("XtOwnSelection(%s, @%ld)\n",
4975 TraceAtomName(screen->display, atoms[i]),
4976 (long) screen->selection_time));
4977 have_selection |=
4978 XtOwnSelection((Widget) xw, atoms[i],
4979 screen->selection_time,
4980 ConvertSelection,
4981 LoseSelection,
4982 SelectionDone);
4983 }
4984 }
4985 TRACE(("... _OwnSelection used length %lu value %s\n",
4986 (unsigned long) scp->data_length,
4987 visibleChars(scp->data_buffer,
4988 (unsigned) scp->data_length)));
4989 }
4990 if (!screen->replyToEmacs)
4991 screen->selection_count = count;
4992 if (!have_selection)
4993 UnHiliteText(xw);
4994 }
4995
4996 static void
ResetSelectionState(TScreen * screen)4997 ResetSelectionState(TScreen *screen)
4998 {
4999 screen->selection_count = 0;
5000 screen->startH = zeroCELL;
5001 screen->endH = zeroCELL;
5002 }
5003
5004 void
DisownSelection(XtermWidget xw)5005 DisownSelection(XtermWidget xw)
5006 {
5007 TScreen *screen = TScreenOf(xw);
5008 Atom *atoms = screen->selection_atoms;
5009 Cardinal count = screen->selection_count;
5010 Cardinal i;
5011
5012 TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
5013 count,
5014 screen->startH.row,
5015 screen->startH.col,
5016 screen->endH.row,
5017 screen->endH.col));
5018
5019 for (i = 0; i < count; i++) {
5020 int cutbuffer = CutBuffer(atoms[i]);
5021 if (cutbuffer < 0) {
5022 XtDisownSelection((Widget) xw, atoms[i],
5023 screen->selection_time);
5024 }
5025 }
5026 /*
5027 * If none of the callbacks via XtDisownSelection() reset highlighting
5028 * do it now.
5029 */
5030 if (ScrnHaveSelection(screen)) {
5031 /* save data which will be reset */
5032 CELL first = screen->startH;
5033 CELL last = screen->endH;
5034
5035 ResetSelectionState(screen);
5036 ReHiliteText(xw, &first, &last);
5037 } else {
5038 ResetSelectionState(screen);
5039 }
5040 }
5041
5042 void
UnhiliteSelection(XtermWidget xw)5043 UnhiliteSelection(XtermWidget xw)
5044 {
5045 TScreen *screen = TScreenOf(xw);
5046
5047 if (ScrnHaveSelection(screen)) {
5048 CELL first = screen->startH;
5049 CELL last = screen->endH;
5050
5051 screen->startH = zeroCELL;
5052 screen->endH = zeroCELL;
5053 ReHiliteText(xw, &first, &last);
5054 }
5055 }
5056
5057 /* returns number of chars in line from scol to ecol out */
5058 /* ARGSUSED */
5059 static int
Length(TScreen * screen,int row,int scol,int ecol)5060 Length(TScreen *screen,
5061 int row,
5062 int scol,
5063 int ecol)
5064 {
5065 CLineData *ld = GET_LINEDATA(screen, row);
5066 const int lastcol = LastTextCol(screen, ld, row);
5067
5068 if (ecol > lastcol)
5069 ecol = lastcol;
5070 return (ecol - scol + 1);
5071 }
5072
5073 /* copies text into line, preallocated */
5074 static Char *
SaveText(TScreen * screen,int row,int scol,int ecol,Char * lp,int * eol)5075 SaveText(TScreen *screen,
5076 int row,
5077 int scol,
5078 int ecol,
5079 Char *lp, /* pointer to where to put the text */
5080 int *eol)
5081 {
5082 LineData *ld;
5083 int i = 0;
5084 Char *result = lp;
5085 #if OPT_WIDE_CHARS
5086 unsigned previous = 0;
5087 #endif
5088
5089 ld = GET_LINEDATA(screen, row);
5090 i = Length(screen, row, scol, ecol);
5091 ecol = scol + i;
5092 #if OPT_DEC_CHRSET
5093 if (CSET_DOUBLE(GetLineDblCS(ld))) {
5094 scol = (scol + 0) / 2;
5095 ecol = (ecol + 1) / 2;
5096 }
5097 #endif
5098 *eol = !LineTstWrapped(ld);
5099 for (i = scol; i < ecol; i++) {
5100 unsigned c;
5101 assert(i < (int) ld->lineSize);
5102 c = E2A(ld->charData[i]);
5103 #if OPT_WIDE_CHARS
5104 /* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
5105 * wide character.
5106 */
5107 if (c == HIDDEN_CHAR) {
5108 if (isWide((int) previous)) {
5109 previous = c;
5110 /* Combining characters attached to double-width characters
5111 are in memory attached to the HIDDEN_CHAR */
5112 if_OPT_WIDE_CHARS(screen, {
5113 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5114 size_t off;
5115 for_each_combData(off, ld) {
5116 unsigned ch = ld->combData[off][i];
5117 if (ch == 0)
5118 break;
5119 lp = convertToUTF8(lp, ch);
5120 }
5121 }
5122 });
5123 continue;
5124 } else {
5125 c = ' '; /* should not happen, but just in case... */
5126 }
5127 }
5128 previous = c;
5129 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5130 lp = convertToUTF8(lp, (c != 0) ? c : ' ');
5131 if_OPT_WIDE_CHARS(screen, {
5132 size_t off;
5133 for_each_combData(off, ld) {
5134 unsigned ch = ld->combData[off][i];
5135 if (ch == 0)
5136 break;
5137 lp = convertToUTF8(lp, ch);
5138 }
5139 });
5140 } else
5141 #endif
5142 {
5143 if (c == 0) {
5144 c = E2A(' ');
5145 } else if (c < E2A(' ')) {
5146 c = DECtoASCII(c);
5147 } else if (c == 0x7f) {
5148 c = 0x5f;
5149 }
5150 *lp++ = CharOf(A2E(c));
5151 }
5152 if (c != E2A(' '))
5153 result = lp;
5154 }
5155
5156 /*
5157 * If requested, trim trailing blanks from selected lines. Do not do this
5158 * if the line is wrapped.
5159 */
5160 if (!*eol || !screen->trim_selection)
5161 result = lp;
5162
5163 return (result);
5164 }
5165
5166 /*
5167 * This adds together the bits:
5168 * shift key -> 1
5169 * meta key -> 2
5170 * control key -> 4
5171 */
5172 static unsigned
KeyState(XtermWidget xw,unsigned x)5173 KeyState(XtermWidget xw, unsigned x)
5174 {
5175 return ((((x) & (ShiftMask | ControlMask)))
5176 + (((x) & MetaMask(xw)) ? 2 : 0));
5177 }
5178
5179 /* 32 + following 8-bit word:
5180
5181 1:0 Button no: 0, 1, 2. 3=release.
5182 2 shift
5183 3 meta
5184 4 ctrl
5185 5 set for motion notify
5186 6 set for wheel (and button 6 and 7)
5187 7 set for buttons 8 to 11
5188 */
5189
5190 /* Position: 32 - 255. */
5191 static int
BtnCode(XtermWidget xw,XButtonEvent * event,int button)5192 BtnCode(XtermWidget xw, XButtonEvent *event, int button)
5193 {
5194 int result = (int) (32 + (KeyState(xw, event->state) << 2));
5195
5196 if (event->type == MotionNotify)
5197 result += 32;
5198
5199 if (button < 0) {
5200 result += 3;
5201 } else {
5202 result += button & 3;
5203 if (button & 4)
5204 result += 64;
5205 if (button & 8)
5206 result += 128;
5207 }
5208 TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
5209 button,
5210 visibleEventType(event->type),
5211 ARG_MODIFIER_NAMES(event->state),
5212 result));
5213 return result;
5214 }
5215
5216 static unsigned
EmitButtonCode(XtermWidget xw,Char * line,unsigned count,XButtonEvent * event,int button)5217 EmitButtonCode(XtermWidget xw,
5218 Char *line,
5219 unsigned count,
5220 XButtonEvent *event,
5221 int button)
5222 {
5223 TScreen *screen = TScreenOf(xw);
5224 int value;
5225
5226 if (okSendMousePos(xw) == X10_MOUSE) {
5227 value = CharOf(' ' + button);
5228 } else {
5229 value = BtnCode(xw, event, button);
5230 }
5231
5232 switch (screen->extend_coords) {
5233 default:
5234 line[count++] = CharOf(value);
5235 break;
5236 case SET_SGR_EXT_MODE_MOUSE:
5237 case SET_PIXEL_POSITION_MOUSE:
5238 value -= 32; /* encoding starts at zero */
5239 /* FALLTHRU */
5240 case SET_URXVT_EXT_MODE_MOUSE:
5241 count += (unsigned) sprintf((char *) line + count, "%d", value);
5242 break;
5243 case SET_EXT_MODE_MOUSE:
5244 if (value < 128) {
5245 line[count++] = CharOf(value);
5246 } else {
5247 line[count++] = CharOf(0xC0 + (value >> 6));
5248 line[count++] = CharOf(0x80 + (value & 0x3F));
5249 }
5250 break;
5251 }
5252 return count;
5253 }
5254
5255 static int
FirstBitN(int bits)5256 FirstBitN(int bits)
5257 {
5258 int result = -1;
5259 if (bits > 0) {
5260 result = 0;
5261 while (!(bits & 1)) {
5262 bits /= 2;
5263 ++result;
5264 }
5265 }
5266 return result;
5267 }
5268
5269 #define ButtonBit(button) ((button >= 0) ? (1 << (button)) : 0)
5270
5271 #define EMIT_BUTTON(button) EmitButtonCode(xw, line, count, event, button)
5272
5273 static void
EditorButton(XtermWidget xw,XButtonEvent * event)5274 EditorButton(XtermWidget xw, XButtonEvent *event)
5275 {
5276 TScreen *screen = TScreenOf(xw);
5277 int pty = screen->respond;
5278 int mouse_limit = MouseLimit(screen);
5279 Char line[32];
5280 Char final = 'M';
5281 int row, col;
5282 int button;
5283 unsigned count = 0;
5284 Boolean changed = True;
5285
5286 /* If button event, get button # adjusted for DEC compatibility */
5287 button = (int) (event->button - 1);
5288 if (button >= 3)
5289 button++;
5290
5291 /* Ignore buttons that cannot be encoded */
5292 if (screen->send_mouse_pos == X10_MOUSE) {
5293 if (button > 3)
5294 return;
5295 } else if (screen->extend_coords == SET_SGR_EXT_MODE_MOUSE
5296 || screen->extend_coords == SET_URXVT_EXT_MODE_MOUSE
5297 || screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5298 if (button > 15) {
5299 return;
5300 }
5301 } else {
5302 if (button > 11) {
5303 return;
5304 }
5305 }
5306
5307 if (screen->extend_coords == SET_PIXEL_POSITION_MOUSE) {
5308 row = event->y - OriginY(screen);
5309 col = event->x - OriginX(screen);
5310 } else {
5311 /* Compute character position of mouse pointer */
5312 row = (event->y - screen->border) / FontHeight(screen);
5313 col = (event->x - OriginX(screen)) / FontWidth(screen);
5314
5315 /* Limit to screen dimensions */
5316 if (row < 0)
5317 row = 0;
5318 else if (row > screen->max_row)
5319 row = screen->max_row;
5320
5321 if (col < 0)
5322 col = 0;
5323 else if (col > screen->max_col)
5324 col = screen->max_col;
5325
5326 if (mouse_limit > 0) {
5327 /* Limit to representable mouse dimensions */
5328 if (row > mouse_limit)
5329 row = mouse_limit;
5330 if (col > mouse_limit)
5331 col = mouse_limit;
5332 }
5333 }
5334
5335 /* Build key sequence starting with \E[M */
5336 if (screen->control_eight_bits) {
5337 line[count++] = ANSI_CSI;
5338 } else {
5339 line[count++] = ANSI_ESC;
5340 line[count++] = '[';
5341 }
5342 switch (screen->extend_coords) {
5343 case 0:
5344 case SET_EXT_MODE_MOUSE:
5345 #if OPT_SCO_FUNC_KEYS
5346 if (xw->keyboard.type == keyboardIsSCO) {
5347 /*
5348 * SCO function key F1 is \E[M, which would conflict with xterm's
5349 * normal kmous.
5350 */
5351 line[count++] = '>';
5352 }
5353 #endif
5354 line[count++] = final;
5355 break;
5356 case SET_SGR_EXT_MODE_MOUSE:
5357 case SET_PIXEL_POSITION_MOUSE:
5358 line[count++] = '<';
5359 break;
5360 }
5361
5362 /* Add event code to key sequence */
5363 if (okSendMousePos(xw) == X10_MOUSE) {
5364 count = EMIT_BUTTON(button);
5365 } else {
5366 /* Button-Motion events */
5367 switch (event->type) {
5368 case ButtonPress:
5369 screen->mouse_button |= ButtonBit(button);
5370 count = EMIT_BUTTON(button);
5371 break;
5372 case ButtonRelease:
5373 /*
5374 * The (vertical) wheel mouse interface generates release-events
5375 * for buttons 4 and 5.
5376 *
5377 * The X10/X11 xterm protocol maps the release for buttons 1..3 to
5378 * a -1, which will be later mapped into a "0" (some button was
5379 * released), At this point, buttons 1..3 are encoded 0..2 (the
5380 * code 3 is unused).
5381 *
5382 * The SGR (extended) xterm mouse protocol keeps the button number
5383 * and uses a "m" to indicate button release.
5384 *
5385 * The behavior for mice with more buttons is unclear, and may be
5386 * revised -TD
5387 */
5388 screen->mouse_button &= ~ButtonBit(button);
5389 if (button < 3 || button > 5) {
5390 switch (screen->extend_coords) {
5391 case SET_SGR_EXT_MODE_MOUSE:
5392 case SET_PIXEL_POSITION_MOUSE:
5393 final = 'm';
5394 break;
5395 default:
5396 button = -1;
5397 break;
5398 }
5399 }
5400 count = EMIT_BUTTON(button);
5401 break;
5402 case MotionNotify:
5403 /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion
5404 * events only if character cell has changed.
5405 */
5406 if ((row == screen->mouse_row)
5407 && (col == screen->mouse_col)) {
5408 changed = False;
5409 } else {
5410 count = EMIT_BUTTON(FirstBitN(screen->mouse_button));
5411 }
5412 break;
5413 default:
5414 changed = False;
5415 break;
5416 }
5417 }
5418
5419 if (changed) {
5420 screen->mouse_row = row;
5421 screen->mouse_col = col;
5422
5423 TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, line[count - 1]));
5424
5425 /* Add pointer position to key sequence */
5426 count = EmitMousePositionSeparator(screen, line, count);
5427 count = EmitMousePosition(screen, line, count, col);
5428 count = EmitMousePositionSeparator(screen, line, count);
5429 count = EmitMousePosition(screen, line, count, row);
5430
5431 switch (screen->extend_coords) {
5432 case SET_SGR_EXT_MODE_MOUSE:
5433 case SET_URXVT_EXT_MODE_MOUSE:
5434 case SET_PIXEL_POSITION_MOUSE:
5435 line[count++] = final;
5436 break;
5437 }
5438
5439 /* Transmit key sequence to process running under xterm */
5440 TRACE(("EditorButton -> %s\n", visibleChars(line, count)));
5441 v_write(pty, line, count);
5442 }
5443 return;
5444 }
5445
5446 /*
5447 * Check the current send_mouse_pos against allowed mouse-operations, returning
5448 * none if it is disallowed.
5449 */
5450 XtermMouseModes
okSendMousePos(XtermWidget xw)5451 okSendMousePos(XtermWidget xw)
5452 {
5453 TScreen *screen = TScreenOf(xw);
5454 XtermMouseModes result = (XtermMouseModes) screen->send_mouse_pos;
5455
5456 switch ((int) result) {
5457 case MOUSE_OFF:
5458 break;
5459 case X10_MOUSE:
5460 if (!AllowMouseOps(xw, emX10))
5461 result = MOUSE_OFF;
5462 break;
5463 case VT200_MOUSE:
5464 if (!AllowMouseOps(xw, emVT200Click))
5465 result = MOUSE_OFF;
5466 break;
5467 case VT200_HIGHLIGHT_MOUSE:
5468 if (!AllowMouseOps(xw, emVT200Hilite))
5469 result = MOUSE_OFF;
5470 break;
5471 case BTN_EVENT_MOUSE:
5472 if (!AllowMouseOps(xw, emAnyButton))
5473 result = MOUSE_OFF;
5474 break;
5475 case ANY_EVENT_MOUSE:
5476 if (!AllowMouseOps(xw, emAnyEvent))
5477 result = MOUSE_OFF;
5478 break;
5479 case DEC_LOCATOR:
5480 if (!AllowMouseOps(xw, emLocator))
5481 result = MOUSE_OFF;
5482 break;
5483 }
5484 return result;
5485 }
5486
5487 #if OPT_FOCUS_EVENT
5488 /*
5489 * Check the current send_focus_pos against allowed mouse-operations, returning
5490 * none if it is disallowed.
5491 */
5492 static int
okSendFocusPos(XtermWidget xw)5493 okSendFocusPos(XtermWidget xw)
5494 {
5495 TScreen *screen = TScreenOf(xw);
5496 int result = screen->send_focus_pos;
5497
5498 if (!AllowMouseOps(xw, emFocusEvent)) {
5499 result = False;
5500 }
5501 return result;
5502 }
5503
5504 void
SendFocusButton(XtermWidget xw,XFocusChangeEvent * event)5505 SendFocusButton(XtermWidget xw, XFocusChangeEvent *event)
5506 {
5507 if (okSendFocusPos(xw)) {
5508 ANSI reply;
5509
5510 memset(&reply, 0, sizeof(reply));
5511 reply.a_type = ANSI_CSI;
5512
5513 #if OPT_SCO_FUNC_KEYS
5514 if (xw->keyboard.type == keyboardIsSCO) {
5515 reply.a_pintro = '>';
5516 }
5517 #endif
5518 reply.a_final = CharOf((event->type == FocusIn) ? 'I' : 'O');
5519 unparseseq(xw, &reply);
5520 }
5521 return;
5522 }
5523 #endif /* OPT_FOCUS_EVENT */
5524
5525 #if OPT_SELECTION_OPS
5526 /*
5527 * Get the event-time, needed to process selections.
5528 */
5529 static Time
getEventTime(XEvent * event)5530 getEventTime(XEvent *event)
5531 {
5532 Time result;
5533
5534 if (IsBtnEvent(event)) {
5535 result = ((XButtonEvent *) event)->time;
5536 } else if (IsKeyEvent(event)) {
5537 result = ((XKeyEvent *) event)->time;
5538 } else {
5539 result = 0;
5540 }
5541
5542 return result;
5543 }
5544
5545 /* obtain the selection string, passing the endpoints to caller's parameters */
5546 static void
doSelectionFormat(XtermWidget xw,Widget w,XEvent * event,String * params,Cardinal * num_params,FormatSelect format_select)5547 doSelectionFormat(XtermWidget xw,
5548 Widget w,
5549 XEvent *event,
5550 String *params,
5551 Cardinal *num_params,
5552 FormatSelect format_select)
5553 {
5554 TScreen *screen = TScreenOf(xw);
5555 InternalSelect *mydata = &(screen->internal_select);
5556
5557 memset(mydata, 0, sizeof(*mydata));
5558 mydata->format = x_strdup(params[0]);
5559 mydata->format_select = format_select;
5560
5561 screen->selectToBuffer = True;
5562 beginInternalSelect(xw);
5563
5564 xtermGetSelection(w, getEventTime(event), params + 1, *num_params - 1, NULL);
5565
5566 if (screen->selectToBuffer)
5567 finishInternalSelect(xw);
5568 }
5569
5570 /* obtain data from the screen, passing the endpoints to caller's parameters */
5571 static char *
getDataFromScreen(XtermWidget xw,XEvent * event,String method,CELL * start,CELL * finish)5572 getDataFromScreen(XtermWidget xw, XEvent *event, String method, CELL *start, CELL *finish)
5573 {
5574 TScreen *screen = TScreenOf(xw);
5575
5576 CELL save_old_start = screen->startH;
5577 CELL save_old_end = screen->endH;
5578
5579 CELL save_startSel = screen->startSel;
5580 CELL save_startRaw = screen->startRaw;
5581 CELL save_finishSel = screen->endSel;
5582 CELL save_finishRaw = screen->endRaw;
5583
5584 int save_firstValidRow = screen->firstValidRow;
5585 int save_lastValidRow = screen->lastValidRow;
5586
5587 const Cardinal noClick = 0;
5588 int save_numberOfClicks = screen->numberOfClicks;
5589
5590 SelectUnit saveUnits = screen->selectUnit;
5591 SelectUnit saveMap = screen->selectMap[noClick];
5592 #if OPT_SELECT_REGEX
5593 char *saveExpr = screen->selectExpr[noClick];
5594 #endif
5595 SelectedCells *scp = &(screen->selected_cells[PRIMARY_CODE]);
5596 SelectedCells save_selection = *scp;
5597
5598 char *result = 0;
5599
5600 TRACE(("getDataFromScreen %s\n", method));
5601
5602 memset(scp, 0, sizeof(*scp));
5603
5604 screen->numberOfClicks = 1;
5605 lookupSelectUnit(xw, noClick, method);
5606 screen->selectUnit = screen->selectMap[noClick];
5607
5608 memset(start, 0, sizeof(*start));
5609 if (IsBtnEvent(event)) {
5610 XButtonEvent *btn_event = (XButtonEvent *) event;
5611 CELL cell;
5612 screen->firstValidRow = 0;
5613 screen->lastValidRow = screen->max_row;
5614 PointToCELL(screen, btn_event->y, btn_event->x, &cell);
5615 start->row = cell.row;
5616 start->col = cell.col;
5617 finish->row = cell.row;
5618 finish->col = screen->max_col;
5619 } else {
5620 start->row = screen->cur_row;
5621 start->col = screen->cur_col;
5622 finish->row = screen->cur_row;
5623 finish->col = screen->max_col;
5624 }
5625
5626 ComputeSelect(xw, start, finish, False, False);
5627 SaltTextAway(xw,
5628 TargetToSelection(screen, PRIMARY_NAME),
5629 &(screen->startSel), &(screen->endSel));
5630
5631 if (scp->data_limit && scp->data_buffer) {
5632 TRACE(("...getDataFromScreen selection-data %.*s\n",
5633 (int) scp->data_limit,
5634 scp->data_buffer));
5635 result = malloc(scp->data_limit + 1);
5636 if (result) {
5637 memcpy(result, scp->data_buffer, scp->data_limit);
5638 result[scp->data_limit] = 0;
5639 }
5640 free(scp->data_buffer);
5641 scp->data_limit = 0;
5642 }
5643
5644 TRACE(("...getDataFromScreen restoring previous selection\n"));
5645
5646 screen->startSel = save_startSel;
5647 screen->startRaw = save_startRaw;
5648 screen->endSel = save_finishSel;
5649 screen->endRaw = save_finishRaw;
5650
5651 screen->firstValidRow = save_firstValidRow;
5652 screen->lastValidRow = save_lastValidRow;
5653
5654 screen->numberOfClicks = save_numberOfClicks;
5655 screen->selectUnit = saveUnits;
5656 screen->selectMap[noClick] = saveMap;
5657 #if OPT_SELECT_REGEX
5658 screen->selectExpr[noClick] = saveExpr;
5659 #endif
5660
5661 screen->selected_cells[0] = save_selection;
5662
5663 TrackText(xw, &save_old_start, &save_old_end);
5664
5665 TRACE(("...getDataFromScreen done\n"));
5666 return result;
5667 }
5668
5669 /*
5670 * Split-up the format before substituting data, to avoid quoting issues.
5671 * The resource mechanism has a limited ability to handle escapes. We take
5672 * the result as if it were an sh-type string and parse it into a regular
5673 * argv array.
5674 */
5675 static char **
tokenizeFormat(String format)5676 tokenizeFormat(String format)
5677 {
5678 char **result = 0;
5679
5680 format = x_skip_blanks(format);
5681 if (*format != '\0') {
5682 char *blob = x_strdup(format);
5683 int pass;
5684
5685 for (pass = 0; pass < 2; ++pass) {
5686 int used = 0;
5687 int first = 1;
5688 int escaped = 0;
5689 int squoted = 0;
5690 int dquoted = 0;
5691 int n;
5692 int argc = 0;
5693
5694 for (n = 0; format[n] != '\0'; ++n) {
5695 if (escaped) {
5696 blob[used++] = format[n];
5697 escaped = 0;
5698 } else if (format[n] == '"') {
5699 if (!squoted) {
5700 if (!dquoted)
5701 blob[used++] = format[n];
5702 dquoted = !dquoted;
5703 }
5704 } else if (format[n] == '\'') {
5705 if (!dquoted) {
5706 if (!squoted)
5707 blob[used++] = format[n];
5708 squoted = !squoted;
5709 }
5710 } else if (format[n] == '\\') {
5711 blob[used++] = format[n];
5712 escaped = 1;
5713 } else {
5714 if (first) {
5715 first = 0;
5716 if (pass) {
5717 result[argc] = &blob[n];
5718 }
5719 ++argc;
5720 }
5721 if (isspace((Char) format[n])) {
5722 first = !isspace((Char) format[n + 1]);
5723 if (squoted || dquoted) {
5724 blob[used++] = format[n];
5725 } else if (first) {
5726 blob[used++] = '\0';
5727 }
5728 } else {
5729 blob[used++] = format[n];
5730 }
5731 }
5732 }
5733 blob[used] = '\0';
5734 assert(strlen(blob) <= strlen(format));
5735 if (!pass) {
5736 result = TypeCallocN(char *, argc + 1);
5737 if (result == 0) {
5738 free(blob);
5739 break;
5740 }
5741 }
5742 }
5743 }
5744 #if OPT_TRACE
5745 if (result) {
5746 int n;
5747 TRACE(("tokenizeFormat %s\n", format));
5748 for (n = 0; result[n]; ++n) {
5749 TRACE(("argv[%d] = %s\n", n, result[n]));
5750 }
5751 }
5752 #endif
5753
5754 return result;
5755 }
5756
5757 static void
formatVideoAttrs(XtermWidget xw,char * buffer,CELL * cell)5758 formatVideoAttrs(XtermWidget xw, char *buffer, CELL *cell)
5759 {
5760 TScreen *screen = TScreenOf(xw);
5761 LineData *ld = GET_LINEDATA(screen, cell->row);
5762
5763 *buffer = '\0';
5764 if (ld != 0 && cell->col < (int) ld->lineSize) {
5765 IAttr attribs = ld->attribs[cell->col];
5766 const char *delim = "";
5767
5768 if (attribs & INVERSE) {
5769 buffer += sprintf(buffer, "7");
5770 delim = ";";
5771 }
5772 if (attribs & UNDERLINE) {
5773 buffer += sprintf(buffer, "%s4", delim);
5774 delim = ";";
5775 }
5776 if (attribs & BOLD) {
5777 buffer += sprintf(buffer, "%s1", delim);
5778 delim = ";";
5779 }
5780 if (attribs & BLINK) {
5781 buffer += sprintf(buffer, "%s5", delim);
5782 delim = ";";
5783 }
5784 #if OPT_ISO_COLORS
5785 if (attribs & FG_COLOR) {
5786 Pixel fg = extract_fg(xw, ld->color[cell->col], attribs);
5787 if (fg < 8) {
5788 fg += 30;
5789 } else if (fg < 16) {
5790 fg += 90;
5791 } else {
5792 buffer += sprintf(buffer, "%s38;5", delim);
5793 delim = ";";
5794 }
5795 buffer += sprintf(buffer, "%s%lu", delim, fg);
5796 delim = ";";
5797 }
5798 if (attribs & BG_COLOR) {
5799 Pixel bg = extract_bg(xw, ld->color[cell->col], attribs);
5800 if (bg < 8) {
5801 bg += 40;
5802 } else if (bg < 16) {
5803 bg += 100;
5804 } else {
5805 buffer += sprintf(buffer, "%s48;5", delim);
5806 delim = ";";
5807 }
5808 (void) sprintf(buffer, "%s%lu", delim, bg);
5809 }
5810 #endif
5811 }
5812 }
5813
5814 static char *
formatStrlen(char * target,char * source,int freeit)5815 formatStrlen(char *target, char *source, int freeit)
5816 {
5817 if (source != 0) {
5818 sprintf(target, "%u", (unsigned) strlen(source));
5819 if (freeit) {
5820 free(source);
5821 }
5822 } else {
5823 strcpy(target, "0");
5824 }
5825 return target;
5826 }
5827
5828 /* substitute data into format, reallocating the result */
5829 static char *
expandFormat(XtermWidget xw,const char * format,char * data,CELL * start,CELL * finish)5830 expandFormat(XtermWidget xw,
5831 const char *format,
5832 char *data,
5833 CELL *start,
5834 CELL *finish)
5835 {
5836 char *result = 0;
5837 if (!IsEmpty(format)) {
5838 static char empty[1];
5839 int pass;
5840 int n;
5841 char numbers[80];
5842
5843 if (data == 0)
5844 data = empty;
5845
5846 for (pass = 0; pass < 2; ++pass) {
5847 size_t need = 0;
5848
5849 for (n = 0; format[n] != '\0'; ++n) {
5850
5851 if (format[n] == '%') {
5852 char *value = 0;
5853
5854 switch (format[++n]) {
5855 case '%':
5856 if (pass) {
5857 result[need] = format[n];
5858 }
5859 ++need;
5860 break;
5861 case 'P':
5862 sprintf(numbers, "%d;%d",
5863 TScreenOf(xw)->topline + start->row + 1,
5864 start->col + 1);
5865 value = numbers;
5866 break;
5867 case 'p':
5868 sprintf(numbers, "%d;%d",
5869 TScreenOf(xw)->topline + finish->row + 1,
5870 finish->col + 1);
5871 value = numbers;
5872 break;
5873 case 'R':
5874 value = formatStrlen(numbers, x_strrtrim(data), 1);
5875 break;
5876 case 'r':
5877 value = x_strrtrim(data);
5878 break;
5879 case 'S':
5880 value = formatStrlen(numbers, data, 0);
5881 break;
5882 case 's':
5883 value = data;
5884 break;
5885 case 'T':
5886 value = formatStrlen(numbers, x_strtrim(data), 1);
5887 break;
5888 case 't':
5889 value = x_strtrim(data);
5890 break;
5891 case 'V':
5892 formatVideoAttrs(xw, numbers, start);
5893 value = numbers;
5894 break;
5895 case 'v':
5896 formatVideoAttrs(xw, numbers, finish);
5897 value = numbers;
5898 break;
5899 default:
5900 if (pass) {
5901 result[need] = format[n];
5902 }
5903 --n;
5904 ++need;
5905 break;
5906 }
5907 if (value != 0) {
5908 if (pass) {
5909 strcpy(result + need, value);
5910 }
5911 need += strlen(value);
5912 if (value != numbers && value != data) {
5913 free(value);
5914 }
5915 }
5916 } else {
5917 if (pass) {
5918 result[need] = format[n];
5919 }
5920 ++need;
5921 }
5922 }
5923 if (pass) {
5924 result[need] = '\0';
5925 } else {
5926 ++need;
5927 result = malloc(need);
5928 if (result == 0) {
5929 break;
5930 }
5931 }
5932 }
5933 }
5934 TRACE(("expandFormat(%s) = %s\n", NonNull(format), NonNull(result)));
5935 return result;
5936 }
5937
5938 /* execute the command after forking. The main process frees its data */
5939 static void
executeCommand(pid_t pid,char ** argv)5940 executeCommand(pid_t pid, char **argv)
5941 {
5942 (void) pid;
5943 if (argv != 0 && argv[0] != 0) {
5944 char *child_cwd = ProcGetCWD(pid);
5945
5946 if (fork() == 0) {
5947 if (child_cwd) {
5948 IGNORE_RC(chdir(child_cwd)); /* We don't care if this fails */
5949 }
5950 execvp(argv[0], argv);
5951 exit(EXIT_FAILURE);
5952 }
5953 free(child_cwd);
5954 }
5955 }
5956
5957 static void
freeArgv(char * blob,char ** argv)5958 freeArgv(char *blob, char **argv)
5959 {
5960 if (blob) {
5961 free(blob);
5962 if (argv) {
5963 int n;
5964 for (n = 0; argv[n]; ++n)
5965 free(argv[n]);
5966 free(argv);
5967 }
5968 }
5969 }
5970
5971 static void
reallyExecFormatted(Widget w,char * format,char * data,CELL * start,CELL * finish)5972 reallyExecFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
5973 {
5974 XtermWidget xw;
5975
5976 if ((xw = getXtermWidget(w)) != 0) {
5977 char **argv;
5978
5979 if ((argv = tokenizeFormat(format)) != 0) {
5980 char *blob = argv[0];
5981 int argc;
5982
5983 for (argc = 0; argv[argc] != 0; ++argc) {
5984 argv[argc] = expandFormat(xw, argv[argc], data, start, finish);
5985 }
5986 executeCommand(TScreenOf(xw)->pid, argv);
5987 freeArgv(blob, argv);
5988 }
5989 }
5990 }
5991
5992 void
HandleExecFormatted(Widget w,XEvent * event,String * params,Cardinal * num_params)5993 HandleExecFormatted(Widget w,
5994 XEvent *event,
5995 String *params, /* selections */
5996 Cardinal *num_params)
5997 {
5998 XtermWidget xw;
5999
6000 TRACE_EVENT("HandleExecFormatted", event, params, num_params);
6001 if ((xw = getXtermWidget(w)) != 0 &&
6002 (*num_params > 1)) {
6003 doSelectionFormat(xw, w, event, params, num_params, reallyExecFormatted);
6004 }
6005 }
6006
6007 void
HandleExecSelectable(Widget w,XEvent * event,String * params,Cardinal * num_params)6008 HandleExecSelectable(Widget w,
6009 XEvent *event,
6010 String *params, /* selections */
6011 Cardinal *num_params)
6012 {
6013 XtermWidget xw;
6014
6015 if ((xw = getXtermWidget(w)) != 0) {
6016 TRACE_EVENT("HandleExecSelectable", event, params, num_params);
6017
6018 if (*num_params == 2) {
6019 CELL start, finish;
6020 char *data;
6021 char **argv;
6022
6023 data = getDataFromScreen(xw, event, params[1], &start, &finish);
6024 if (data != 0) {
6025 if ((argv = tokenizeFormat(params[0])) != 0) {
6026 char *blob = argv[0];
6027 int argc;
6028
6029 for (argc = 0; argv[argc] != 0; ++argc) {
6030 argv[argc] = expandFormat(xw, argv[argc], data,
6031 &start, &finish);
6032 }
6033 executeCommand(TScreenOf(xw)->pid, argv);
6034 freeArgv(blob, argv);
6035 }
6036 free(data);
6037 }
6038 }
6039 }
6040 }
6041
6042 static void
reallyInsertFormatted(Widget w,char * format,char * data,CELL * start,CELL * finish)6043 reallyInsertFormatted(Widget w, char *format, char *data, CELL *start, CELL *finish)
6044 {
6045 XtermWidget xw;
6046
6047 if ((xw = getXtermWidget(w)) != 0) {
6048 char *exps;
6049
6050 if ((exps = expandFormat(xw, format, data, start, finish)) != 0) {
6051 unparseputs(xw, exps);
6052 unparse_end(xw);
6053 free(exps);
6054 }
6055 }
6056 }
6057
6058 void
HandleInsertFormatted(Widget w,XEvent * event,String * params,Cardinal * num_params)6059 HandleInsertFormatted(Widget w,
6060 XEvent *event,
6061 String *params, /* selections */
6062 Cardinal *num_params)
6063 {
6064 XtermWidget xw;
6065
6066 TRACE_EVENT("HandleInsertFormatted", event, params, num_params);
6067 if ((xw = getXtermWidget(w)) != 0 &&
6068 (*num_params > 1)) {
6069 doSelectionFormat(xw, w, event, params, num_params, reallyInsertFormatted);
6070 }
6071 }
6072
6073 void
HandleInsertSelectable(Widget w,XEvent * event,String * params,Cardinal * num_params)6074 HandleInsertSelectable(Widget w,
6075 XEvent *event,
6076 String *params, /* selections */
6077 Cardinal *num_params)
6078 {
6079 XtermWidget xw;
6080
6081 if ((xw = getXtermWidget(w)) != 0) {
6082 TRACE_EVENT("HandleInsertSelectable", event, params, num_params);
6083
6084 if (*num_params == 2) {
6085 CELL start, finish;
6086 char *data;
6087 char *temp = x_strdup(params[0]);
6088
6089 data = getDataFromScreen(xw, event, params[1], &start, &finish);
6090 if (data != 0) {
6091 char *exps = expandFormat(xw, temp, data, &start, &finish);
6092 if (exps != 0) {
6093 unparseputs(xw, exps);
6094 unparse_end(xw);
6095 free(exps);
6096 }
6097 free(data);
6098 }
6099 free(temp);
6100 }
6101 }
6102 }
6103 #endif /* OPT_SELECTION_OPS */
6104