"Fossies" - the Fresh Open Source Software Archive 
Member "xterm-379/button.c" (13 Feb 2023, 156745 Bytes) of package /linux/misc/xterm-379.tgz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "button.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
377_vs_379.
1 /* $XTermId: button.c,v 1.653 2023/02/13 22:34:51 tom Exp $ */
2
3 /*
4 * Copyright 1999-2022,2023 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
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
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
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 *
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 *
326 scanTrans(char *source, int *this_is, int *next_is, unsigned *first, unsigned *last)
327 {
328 char *target = source;
329
330 *first = *last = 0;
331 if (IsEmpty(target)) {
332 target = 0;
333 } else {
334 do {
335 char ch;
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1291 eventRow(TScreen *screen, XEvent *event) /* must be XButtonEvent */
1292 {
1293 return (event->xbutton.y - screen->border) / FontHeight(screen);
1294 }
1295
1296 static int
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
1305 ReadLineMovePoint(XtermWidget xw, int col, int ldelta)
1306 {
1307 TScreen *screen = TScreenOf(xw);
1308 Char line[6];
1309 unsigned count = 0;
1310
1311 col += ldelta * MaxCols(screen) - screen->cur_col;
1312 if (col == 0)
1313 return 0;
1314 if (screen->control_eight_bits) {
1315 line[count++] = ANSI_CSI;
1316 } else {
1317 line[count++] = ANSI_ESC;
1318 line[count++] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
1319 }
1320 line[count] = CharOf(col > 0 ? 'C' : 'D');
1321 if (col < 0)
1322 col = -col;
1323 while (col--)
1324 v_write(screen->respond, line, (size_t) 3);
1325 return 1;
1326 }
1327
1328 static int
1329 ReadLineDelete(TScreen *screen, CELL *cell1, CELL *cell2)
1330 {
1331 int del;
1332 Char erases[2];
1333
1334 erases[0] = (Char) get_tty_erase(screen->respond, XTERM_ERASE, "pty");
1335 erases[1] = 0;
1336
1337 del = (cell2->col - cell1->col) + ((cell2->row - cell1->row) * MaxCols(screen));
1338 if (del <= 0) /* Just in case... */
1339 return 0;
1340 while (del--)
1341 v_write(screen->respond, erases, (size_t) 1);
1342 return 1;
1343 }
1344
1345 static void
1346 readlineExtend(XtermWidget xw, XEvent *event)
1347 {
1348 TScreen *screen = TScreenOf(xw);
1349 int ldelta1, ldelta2;
1350
1351 if (IsBtnEvent(event)) {
1352 XButtonEvent *my_event = (XButtonEvent *) event;
1353 if (isClick1_clean(xw, my_event)
1354 && SCREEN_FLAG(screen, click1_moves)
1355 && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta1)) {
1356 ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta1);
1357 }
1358 if (isDoubleClick3(xw, screen, my_event)
1359 && SCREEN_FLAG(screen, dclick3_deletes)
1360 && rowOnCurrentLine(screen, screen->startSel.row, &ldelta1)
1361 && rowOnCurrentLine(screen, screen->endSel.row, &ldelta2)) {
1362 ReadLineMovePoint(xw, screen->endSel.col, ldelta2);
1363 ReadLineDelete(screen, &screen->startSel, &(screen->endSel));
1364 }
1365 }
1366 }
1367 #endif /* OPT_READLINE */
1368
1369 /* ^XM-G<line+' '><col+' '> */
1370 void
1371 DiredButton(Widget w,
1372 XEvent *event, /* must be XButtonEvent */
1373 String *params GCC_UNUSED, /* selections */
1374 Cardinal *num_params GCC_UNUSED)
1375 {
1376 XtermWidget xw;
1377
1378 if ((xw = getXtermWidget(w)) != 0) {
1379 TScreen *screen = TScreenOf(xw);
1380
1381 if (IsBtnEvent(event)
1382 && (event->xbutton.y >= screen->border)
1383 && (event->xbutton.x >= OriginX(screen))) {
1384 Char Line[6];
1385 unsigned line, col;
1386
1387 line = (unsigned) ((event->xbutton.y - screen->border)
1388 / FontHeight(screen));
1389 col = (unsigned) ((event->xbutton.x - OriginX(screen))
1390 / FontWidth(screen));
1391 Line[0] = CONTROL('X');
1392 Line[1] = ANSI_ESC;
1393 Line[2] = 'G';
1394 Line[3] = CharOf(' ' + col);
1395 Line[4] = CharOf(' ' + line);
1396 v_write(screen->respond, Line, (size_t) 5);
1397 }
1398 }
1399 }
1400
1401 #if OPT_READLINE
1402 void
1403 ReadLineButton(Widget w,
1404 XEvent *event, /* must be XButtonEvent */
1405 String *params, /* selections */
1406 Cardinal *num_params)
1407 {
1408 XtermWidget xw;
1409
1410 if ((xw = getXtermWidget(w)) != 0) {
1411 TScreen *screen = TScreenOf(xw);
1412 Char Line[6];
1413 int line, col, ldelta = 0;
1414
1415 if (!IsBtnEvent(event)
1416 || (okSendMousePos(xw) != MOUSE_OFF) || ExtendingSelection)
1417 goto finish;
1418 if (event->type == ButtonRelease) {
1419 int delta;
1420
1421 if (lastButtonDownTime == (Time) 0) {
1422 /* first time and once in a blue moon */
1423 delta = screen->multiClickTime + 1;
1424 } else if (event->xbutton.time > lastButtonDownTime) {
1425 /* most of the time */
1426 delta = (int) (event->xbutton.time - lastButtonDownTime);
1427 } else {
1428 /* time has rolled over since lastButtonUpTime */
1429 delta = (int) ((((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time);
1430 }
1431 if (delta > screen->multiClickTime)
1432 goto finish; /* All this work for this... */
1433 }
1434 line = (event->xbutton.y - screen->border) / FontHeight(screen);
1435 if (!rowOnCurrentLine(screen, line, &ldelta))
1436 goto finish;
1437 /* Correct by half a width - we are acting on a boundary, not on a cell. */
1438 col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1)
1439 / 2)
1440 / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen);
1441 if (col == 0)
1442 goto finish;
1443 Line[0] = ANSI_ESC;
1444 Line[1] = (xw->keyboard.flags & MODE_DECCKM) ? 'O' : '[';
1445 Line[2] = CharOf(col > 0 ? 'C' : 'D');
1446 if (col < 0)
1447 col = -col;
1448 while (col--)
1449 v_write(screen->respond, Line, (size_t) 3);
1450 finish:
1451 if (event->type == ButtonRelease)
1452 do_select_end(xw, event, params, num_params, False);
1453 }
1454 }
1455 #endif /* OPT_READLINE */
1456
1457 /* repeats <ESC>n or <ESC>p */
1458 void
1459 ViButton(Widget w,
1460 XEvent *event, /* must be XButtonEvent */
1461 String *params GCC_UNUSED, /* selections */
1462 Cardinal *num_params GCC_UNUSED)
1463 {
1464 XtermWidget xw;
1465
1466 if ((xw = getXtermWidget(w)) != 0) {
1467 TScreen *screen = TScreenOf(xw);
1468 int pty = screen->respond;
1469
1470 if (IsBtnEvent(event)) {
1471 int line;
1472
1473 line = screen->cur_row -
1474 ((event->xbutton.y - screen->border) / FontHeight(screen));
1475
1476 if (line != 0) {
1477 Char Line[6];
1478
1479 Line[0] = ANSI_ESC; /* force an exit from insert-mode */
1480 v_write(pty, Line, (size_t) 1);
1481
1482 if (line < 0) {
1483 line = -line;
1484 Line[0] = CONTROL('n');
1485 } else {
1486 Line[0] = CONTROL('p');
1487 }
1488 while (--line >= 0)
1489 v_write(pty, Line, (size_t) 1);
1490 }
1491 }
1492 }
1493 }
1494
1495 /*
1496 * This function handles button-motion events
1497 */
1498 /*ARGSUSED*/
1499 void
1500 HandleSelectExtend(Widget w,
1501 XEvent *event, /* must be XMotionEvent */
1502 String *params GCC_UNUSED,
1503 Cardinal *num_params GCC_UNUSED)
1504 {
1505 XtermWidget xw;
1506
1507 if ((xw = getXtermWidget(w)) != 0) {
1508 TScreen *screen = TScreenOf(xw);
1509 CELL cell;
1510
1511 TRACE_EVENT("HandleSelectExtend", event, params, num_params);
1512
1513 screen->selection_time = event->xmotion.time;
1514 switch (screen->eventMode) {
1515 /* If not in one of the DEC mouse-reporting modes */
1516 case LEFTEXTENSION:
1517 case RIGHTEXTENSION:
1518 PointToCELL(screen, event->xmotion.y, event->xmotion.x, &cell);
1519 ExtendExtend(xw, &cell);
1520 break;
1521
1522 /* If in motion reporting mode, send mouse position to
1523 character process as a key sequence \E[M... */
1524 case NORMAL:
1525 /* will get here if send_mouse_pos != MOUSE_OFF */
1526 if (okSendMousePos(xw) == BTN_EVENT_MOUSE
1527 || okSendMousePos(xw) == ANY_EVENT_MOUSE) {
1528 (void) SendMousePosition(xw, event);
1529 }
1530 break;
1531 }
1532 }
1533 }
1534
1535 void
1536 HandleKeyboardSelectExtend(Widget w,
1537 XEvent *event GCC_UNUSED, /* must be XButtonEvent */
1538 String *params GCC_UNUSED,
1539 Cardinal *num_params GCC_UNUSED)
1540 {
1541 XtermWidget xw;
1542
1543 if ((xw = getXtermWidget(w)) != 0) {
1544 TScreen *screen = TScreenOf(xw);
1545
1546 TRACE_EVENT("HandleKeyboardSelectExtend", event, params, num_params);
1547 ExtendExtend(xw, &screen->cursorp);
1548 }
1549 }
1550
1551 static void
1552 do_select_end(XtermWidget xw,
1553 XEvent *event, /* must be XButtonEvent */
1554 String *params, /* selections */
1555 Cardinal *num_params,
1556 Bool use_cursor_loc)
1557 {
1558 TScreen *screen = TScreenOf(xw);
1559
1560 screen->selection_time = event->xbutton.time;
1561
1562 TRACE(("do_select_end %s @%ld\n",
1563 visibleEventMode(screen->eventMode),
1564 screen->selection_time));
1565
1566 switch (screen->eventMode) {
1567 case NORMAL:
1568 (void) SendMousePosition(xw, event);
1569 break;
1570 case LEFTEXTENSION:
1571 case RIGHTEXTENSION:
1572 EndExtend(xw, event, params, *num_params, use_cursor_loc);
1573 #if OPT_READLINE
1574 readlineExtend(xw, event);
1575 #endif /* OPT_READLINE */
1576 break;
1577 }
1578 }
1579
1580 void
1581 HandleSelectEnd(Widget w,
1582 XEvent *event, /* must be XButtonEvent */
1583 String *params, /* selections */
1584 Cardinal *num_params)
1585 {
1586 XtermWidget xw;
1587
1588 if ((xw = getXtermWidget(w)) != 0) {
1589 TRACE(("HandleSelectEnd\n"));
1590 do_select_end(xw, event, params, num_params, False);
1591 }
1592 }
1593
1594 void
1595 HandleKeyboardSelectEnd(Widget w,
1596 XEvent *event, /* must be XButtonEvent */
1597 String *params, /* selections */
1598 Cardinal *num_params)
1599 {
1600 XtermWidget xw;
1601
1602 if ((xw = getXtermWidget(w)) != 0) {
1603 TRACE(("HandleKeyboardSelectEnd\n"));
1604 do_select_end(xw, event, params, num_params, True);
1605 }
1606 }
1607
1608 void
1609 HandlePointerMotion(Widget w,
1610 XEvent *event,
1611 String *params, /* selections */
1612 Cardinal *num_params)
1613 {
1614 XtermWidget xw;
1615
1616 (void) params;
1617 (void) num_params;
1618 if ((xw = getXtermWidget(w)) != 0) {
1619 TRACE(("HandlePointerMotion\n"));
1620 if (event->type == MotionNotify)
1621 (void) SendMousePosition(xw, event);
1622 }
1623 }
1624
1625 void
1626 HandlePointerButton(Widget w,
1627 XEvent *event,
1628 String *params, /* selections */
1629 Cardinal *num_params)
1630 {
1631 XtermWidget xw;
1632
1633 (void) params;
1634 (void) num_params;
1635 if ((xw = getXtermWidget(w)) != 0) {
1636 TRACE(("HandlePointerButton\n"));
1637 if (IsBtnEvent(event))
1638 (void) SendMousePosition(xw, event);
1639 }
1640 }
1641
1642 /*
1643 * Copy the selection data to the given target(s).
1644 */
1645 void
1646 HandleCopySelection(Widget w,
1647 XEvent *event,
1648 String *params, /* list of targets */
1649 Cardinal *num_params)
1650 {
1651 XtermWidget xw;
1652
1653 if ((xw = getXtermWidget(w)) != 0) {
1654 TRACE_EVENT("HandleCopySelection", event, params, num_params);
1655 SelectSet(xw, event, params, *num_params);
1656 }
1657 }
1658
1659 struct _SelectionList {
1660 String *params;
1661 Cardinal count;
1662 Atom *targets;
1663 Time time;
1664 };
1665
1666 static unsigned
1667 DECtoASCII(unsigned ch)
1668 {
1669 if (xtermIsDecGraphic(ch)) {
1670 ch = CharOf("###########+++++##-##++++|######"[ch]);
1671 /* 01234567890123456789012345678901 */
1672 }
1673 return ch;
1674 }
1675
1676 #if OPT_WIDE_CHARS
1677 static Cardinal
1678 addXtermChar(Char **buffer, Cardinal *used, Cardinal offset, unsigned value)
1679 {
1680 if (offset + 1 >= *used) {
1681 *used = 1 + (2 * (offset + 1));
1682 allocXtermChars(buffer, *used);
1683 }
1684 (*buffer)[offset++] = (Char) value;
1685 return offset;
1686 }
1687 #define AddChar(buffer, used, offset, value) \
1688 offset = addXtermChar(buffer, used, offset, (unsigned) value)
1689
1690 /*
1691 * Convert a UTF-8 string to Latin-1, replacing non Latin-1 characters by `#',
1692 * or ASCII/Latin-1 equivalents for special cases.
1693 */
1694 static Char *
1695 UTF8toLatin1(TScreen *screen, Char *s, unsigned long len, unsigned long *result)
1696 {
1697 static Char *buffer;
1698 static Cardinal used;
1699
1700 Cardinal offset = 0;
1701
1702 if (len != 0) {
1703 PtyData data;
1704
1705 fakePtyData(&data, s, s + len);
1706 while (decodeUtf8(screen, &data)) {
1707 Bool fails = False;
1708 Bool extra = False;
1709 IChar value;
1710 skipPtyData(&data, value);
1711 if (value == UCS_REPL) {
1712 fails = True;
1713 } else if (value < 256) {
1714 AddChar(&buffer, &used, offset, CharOf(value));
1715 } else {
1716 unsigned eqv = ucs2dec(screen, value);
1717 if (xtermIsDecGraphic(eqv)) {
1718 AddChar(&buffer, &used, offset, DECtoASCII(eqv));
1719 } else {
1720 eqv = AsciiEquivs(value);
1721 if (eqv == value) {
1722 fails = True;
1723 } else {
1724 AddChar(&buffer, &used, offset, eqv);
1725 }
1726 if (isWide((wchar_t) value))
1727 extra = True;
1728 }
1729 }
1730
1731 /*
1732 * If we're not able to plug in a single-byte result, insert the
1733 * defaultString (which normally is a single "#", but could be
1734 * whatever the user wants).
1735 */
1736 if (fails) {
1737 const Char *p;
1738
1739 for (p = (const Char *) screen->default_string; *p != '\0'; ++p) {
1740 AddChar(&buffer, &used, offset, *p);
1741 }
1742 }
1743 if (extra)
1744 AddChar(&buffer, &used, offset, ' ');
1745 }
1746 AddChar(&buffer, &used, offset, '\0');
1747 *result = (unsigned long) (offset - 1);
1748 } else {
1749 *result = 0;
1750 }
1751 return buffer;
1752 }
1753
1754 int
1755 xtermUtf8ToTextList(XtermWidget xw,
1756 XTextProperty * text_prop,
1757 char ***text_list,
1758 int *text_list_count)
1759 {
1760 TScreen *screen = TScreenOf(xw);
1761 Display *dpy = screen->display;
1762 int rc = -1;
1763
1764 if (text_prop->format == 8
1765 && (rc = Xutf8TextPropertyToTextList(dpy, text_prop,
1766 text_list,
1767 text_list_count)) >= 0) {
1768 if (*text_list != NULL && *text_list_count != 0) {
1769 int i;
1770 Char *data;
1771 char **new_text_list, *tmp;
1772 unsigned long size, new_size;
1773
1774 TRACE(("xtermUtf8ToTextList size %d\n", *text_list_count));
1775
1776 /*
1777 * XLib StringList actually uses only two pointers, one for the
1778 * list itself, and one for the data. Pointer to the data is the
1779 * first element of the list, the rest (if any) list elements point
1780 * to the same memory block as the first element
1781 */
1782 new_size = 0;
1783 for (i = 0; i < *text_list_count; ++i) {
1784 data = (Char *) (*text_list)[i];
1785 size = strlen((*text_list)[i]) + 1;
1786 (void) UTF8toLatin1(screen, data, size, &size);
1787 new_size += size + 1;
1788 }
1789 new_text_list = TypeXtMallocN(char *, *text_list_count);
1790 new_text_list[0] = tmp = XtMalloc((Cardinal) new_size);
1791 for (i = 0; i < (*text_list_count); ++i) {
1792 data = (Char *) (*text_list)[i];
1793 size = strlen((*text_list)[i]) + 1;
1794 if ((data = UTF8toLatin1(screen, data, size, &size)) != 0) {
1795 memcpy(tmp, data, size + 1);
1796 new_text_list[i] = tmp;
1797 tmp += size + 1;
1798 }
1799 }
1800 XFreeStringList((*text_list));
1801 *text_list = new_text_list;
1802 } else {
1803 rc = -1;
1804 }
1805 }
1806 return rc;
1807 }
1808 #endif /* OPT_WIDE_CHARS */
1809
1810 static char *
1811 parseItem(char *value, char *nextc)
1812 {
1813 char *nextp = value;
1814 while (*nextp != '\0' && *nextp != ',') {
1815 *nextp = x_toupper(*nextp);
1816 ++nextp;
1817 }
1818 *nextc = *nextp;
1819 *nextp = '\0';
1820
1821 return nextp;
1822 }
1823
1824 /*
1825 * All of the wanted strings are unique in the first character, so we can
1826 * use simple abbreviations.
1827 */
1828 static Bool
1829 sameItem(const char *actual, const char *wanted)
1830 {
1831 Bool result = False;
1832 size_t have = strlen(actual);
1833 size_t need = strlen(wanted);
1834
1835 if (have != 0 && have <= need) {
1836 if (!strncmp(actual, wanted, have)) {
1837 TRACE(("...matched \"%s\"\n", wanted));
1838 result = True;
1839 }
1840 }
1841
1842 return result;
1843 }
1844
1845 /*
1846 * Handle the eightBitSelectTypes or utf8SelectTypes resource values.
1847 */
1848 static Bool
1849 overrideTargets(Widget w, String value, Atom **resultp)
1850 {
1851 Bool override = False;
1852 XtermWidget xw;
1853
1854 if ((xw = getXtermWidget(w)) != 0) {
1855 TScreen *screen = TScreenOf(xw);
1856
1857 if (!IsEmpty(value)) {
1858 char *copied = x_strdup(value);
1859 if (copied != 0) {
1860 Atom *result = 0;
1861 Cardinal count = 1;
1862 int n;
1863
1864 TRACE(("decoding SelectTypes \"%s\"\n", value));
1865 for (n = 0; copied[n] != '\0'; ++n) {
1866 if (copied[n] == ',')
1867 ++count;
1868 }
1869 result = TypeXtMallocN(Atom, (2 * count) + 1);
1870 if (result == NULL) {
1871 TRACE(("Couldn't allocate selection types\n"));
1872 } else {
1873 char nextc = '?';
1874 char *listp = (char *) copied;
1875 count = 0;
1876 do {
1877 char *nextp = parseItem(listp, &nextc);
1878 char *item = x_strtrim(listp);
1879 size_t len = (item ? strlen(item) : 0);
1880
1881 if (len == 0) {
1882 /* EMPTY */ ;
1883 }
1884 #if OPT_WIDE_CHARS
1885 else if (sameItem(item, "UTF8")) {
1886 result[count++] = XA_UTF8_STRING(XtDisplay(w));
1887 }
1888 #endif
1889 else if (sameItem(item, "I18N")) {
1890 if (screen->i18nSelections) {
1891 result[count++] = XA_TEXT(XtDisplay(w));
1892 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1893 }
1894 } else if (sameItem(item, "TEXT")) {
1895 result[count++] = XA_TEXT(XtDisplay(w));
1896 } else if (sameItem(item, "COMPOUND_TEXT")) {
1897 result[count++] = XA_COMPOUND_TEXT(XtDisplay(w));
1898 } else if (sameItem(item, "STRING")) {
1899 result[count++] = XA_STRING;
1900 }
1901 *nextp++ = nextc;
1902 listp = nextp;
1903 free(item);
1904 } while (nextc != '\0');
1905 if (count) {
1906 result[count] = None;
1907 override = True;
1908 *resultp = result;
1909 } else {
1910 XtFree((char *) result);
1911 }
1912 }
1913 free(copied);
1914 } else {
1915 TRACE(("Couldn't allocate copy of selection types\n"));
1916 }
1917 }
1918 }
1919 return override;
1920 }
1921
1922 #if OPT_WIDE_CHARS
1923 static Atom *
1924 allocUtf8Targets(Widget w, TScreen *screen)
1925 {
1926 Atom **resultp = &(screen->selection_targets_utf8);
1927
1928 if (*resultp == 0) {
1929 Atom *result;
1930
1931 if (!overrideTargets(w, screen->utf8_select_types, &result)) {
1932 result = TypeXtMallocN(Atom, 5);
1933 if (result == NULL) {
1934 TRACE(("Couldn't allocate utf-8 selection targets\n"));
1935 } else {
1936 int n = 0;
1937
1938 if (XSupportsLocale()) {
1939 result[n++] = XA_UTF8_STRING(XtDisplay(w));
1940 #ifdef X_HAVE_UTF8_STRING
1941 if (screen->i18nSelections) {
1942 result[n++] = XA_TEXT(XtDisplay(w));
1943 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1944 }
1945 #endif
1946 }
1947 result[n++] = XA_STRING;
1948 result[n] = None;
1949 }
1950 }
1951
1952 *resultp = result;
1953 }
1954
1955 return *resultp;
1956 }
1957 #endif
1958
1959 static Atom *
1960 alloc8bitTargets(Widget w, TScreen *screen)
1961 {
1962 Atom **resultp = &(screen->selection_targets_8bit);
1963
1964 if (*resultp == 0) {
1965 Atom *result = 0;
1966
1967 if (!overrideTargets(w, screen->eightbit_select_types, &result)) {
1968 result = TypeXtMallocN(Atom, 5);
1969 if (result == NULL) {
1970 TRACE(("Couldn't allocate 8bit selection targets\n"));
1971 } else {
1972 int n = 0;
1973
1974 if (XSupportsLocale()) {
1975 #ifdef X_HAVE_UTF8_STRING
1976 result[n++] = XA_UTF8_STRING(XtDisplay(w));
1977 #endif
1978 if (screen->i18nSelections) {
1979 result[n++] = XA_TEXT(XtDisplay(w));
1980 result[n++] = XA_COMPOUND_TEXT(XtDisplay(w));
1981 }
1982 }
1983 result[n++] = XA_STRING;
1984 result[n] = None;
1985 }
1986 }
1987
1988 *resultp = result;
1989 }
1990
1991 return *resultp;
1992 }
1993
1994 static Atom *
1995 _SelectionTargets(Widget w)
1996 {
1997 Atom *result;
1998 XtermWidget xw;
1999
2000 if ((xw = getXtermWidget(w)) == 0) {
2001 result = NULL;
2002 } else {
2003 TScreen *screen = TScreenOf(xw);
2004
2005 #if OPT_WIDE_CHARS
2006 if (screen->wide_chars) {
2007 result = allocUtf8Targets(w, screen);
2008 } else
2009 #endif
2010 {
2011 /* not screen->wide_chars */
2012 result = alloc8bitTargets(w, screen);
2013 }
2014 }
2015
2016 return result;
2017 }
2018
2019 #define isSELECT(value) (!strcmp(NonNull(value), "SELECT"))
2020
2021 static int
2022 DefaultSelection(TScreen *screen)
2023 {
2024 return (screen->selectToClipboard ? 1 : 0);
2025 }
2026
2027 static int
2028 TargetToSelection(TScreen *screen, String name)
2029 {
2030 int result = -1;
2031 int cutb;
2032
2033 if (isSELECT(name)) {
2034 result = DefaultSelection(screen);
2035 } else if (!strcmp(name, PRIMARY_NAME)) {
2036 result = PRIMARY_CODE;
2037 } else if (!strcmp(name, CLIPBOARD_NAME)) {
2038 result = CLIPBOARD_CODE;
2039 } else if (!strcmp(name, SECONDARY_NAME)) {
2040 result = SECONDARY_CODE;
2041 } else if (sscanf(name, "CUT_BUFFER%d", &cutb) == 1) {
2042 if (cutb >= 0 && cutb < MAX_CUT_BUFFER) {
2043 result = CutBufferToCode(cutb);
2044 } else {
2045 xtermWarning("unexpected cut-buffer code: %d\n", cutb);
2046 }
2047 } else {
2048 xtermWarning("unexpected selection target: %s\n", name);
2049 }
2050 TRACE2(("TargetToSelection(%s) ->%d\n", name, result));
2051 return result;
2052 }
2053
2054 void
2055 UnmapSelections(XtermWidget xw)
2056 {
2057 TScreen *screen = TScreenOf(xw);
2058
2059 FreeAndNull(screen->mappedSelect);
2060 }
2061
2062 /*
2063 * xterm generally uses the primary selection. Some applications prefer
2064 * (or are limited to) the clipboard. Since the translations resource is
2065 * complicated, users seldom change the way it affects selection. But it
2066 * is simple to remap the choice between primary and clipboard before the
2067 * call to XmuInternStrings().
2068 */
2069 static String *
2070 MapSelections(XtermWidget xw, String *params, Cardinal num_params)
2071 {
2072 String *result = params;
2073
2074 if (params != 0 && num_params > 0) {
2075 Cardinal j;
2076 Boolean map = False;
2077
2078 for (j = 0; j < num_params; ++j) {
2079 TRACE(("param[%d]:%s\n", j, params[j]));
2080 if (isSELECT(params[j])) {
2081 map = True;
2082 break;
2083 }
2084 }
2085 if (map) {
2086 TScreen *screen = TScreenOf(xw);
2087 const char *mapTo = (screen->selectToClipboard
2088 ? CLIPBOARD_NAME
2089 : PRIMARY_NAME);
2090
2091 UnmapSelections(xw);
2092 if ((result = TypeMallocN(String, num_params + 1)) != 0) {
2093 result[num_params] = 0;
2094 for (j = 0; j < num_params; ++j) {
2095 result[j] = (String) (isSELECT(params[j])
2096 ? mapTo
2097 : params[j]);
2098 if (result[j] == 0) {
2099 UnmapSelections(xw);
2100 FreeAndNull(result);
2101 break;
2102 }
2103 }
2104 screen->mappedSelect = result;
2105 }
2106 }
2107 }
2108 return result;
2109 }
2110
2111 /*
2112 * Lookup the cut-buffer number, which will be in the range 0-7.
2113 * If it is not a cut-buffer, it is a type of selection, e.g., primary.
2114 */
2115 static int
2116 CutBuffer(Atom code)
2117 {
2118 int cutbuffer;
2119 switch ((unsigned) code) {
2120 case XA_CUT_BUFFER0:
2121 cutbuffer = 0;
2122 break;
2123 case XA_CUT_BUFFER1:
2124 cutbuffer = 1;
2125 break;
2126 case XA_CUT_BUFFER2:
2127 cutbuffer = 2;
2128 break;
2129 case XA_CUT_BUFFER3:
2130 cutbuffer = 3;
2131 break;
2132 case XA_CUT_BUFFER4:
2133 cutbuffer = 4;
2134 break;
2135 case XA_CUT_BUFFER5:
2136 cutbuffer = 5;
2137 break;
2138 case XA_CUT_BUFFER6:
2139 cutbuffer = 6;
2140 break;
2141 case XA_CUT_BUFFER7:
2142 cutbuffer = 7;
2143 break;
2144 default:
2145 cutbuffer = -1;
2146 break;
2147 }
2148 TRACE2(("CutBuffer(%d) = %d\n", (int) code, cutbuffer));
2149 return cutbuffer;
2150 }
2151
2152 #if OPT_PASTE64
2153 static void
2154 FinishPaste64(XtermWidget xw)
2155 {
2156 TScreen *screen = TScreenOf(xw);
2157
2158 TRACE(("FinishPaste64(%d)\n", screen->base64_paste));
2159 if (screen->base64_paste) {
2160 screen->base64_paste = 0;
2161 unparseputc1(xw, screen->base64_final);
2162 unparse_end(xw);
2163 }
2164 }
2165 #endif
2166
2167 #if !OPT_PASTE64
2168 static
2169 #endif
2170 void
2171 xtermGetSelection(Widget w,
2172 Time ev_time,
2173 String *params, /* selections in precedence order */
2174 Cardinal num_params,
2175 Atom *targets)
2176 {
2177 Atom selection;
2178 int cutbuffer;
2179 Atom target;
2180
2181 XtermWidget xw;
2182
2183 if (num_params == 0)
2184 return;
2185 if ((xw = getXtermWidget(w)) == 0)
2186 return;
2187
2188 TRACE(("xtermGetSelection num_params %d @%ld\n", num_params, ev_time));
2189 params = MapSelections(xw, params, num_params);
2190
2191 XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection);
2192 cutbuffer = CutBuffer(selection);
2193
2194 TRACE(("Cutbuffer: %d, target: %s\n", cutbuffer,
2195 (targets
2196 ? visibleSelectionTarget(XtDisplay(w), targets[0])
2197 : "None")));
2198
2199 if (cutbuffer >= 0) {
2200 int inbytes;
2201 unsigned long nbytes;
2202 int fmt8 = 8;
2203 Atom type = XA_STRING;
2204 char *line;
2205
2206 /* 'line' is freed in SelectionReceived */
2207 line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer);
2208 nbytes = (unsigned long) inbytes;
2209
2210 if (nbytes > 0) {
2211 SelectionReceived(w, NULL, &selection, &type, (XtPointer) line,
2212 &nbytes, &fmt8);
2213 } else if (num_params > 1) {
2214 xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL);
2215 }
2216 #if OPT_PASTE64
2217 else {
2218 FinishPaste64(xw);
2219 }
2220 #endif
2221 } else {
2222
2223 if (targets == NULL || targets[0] == None) {
2224 targets = _SelectionTargets(w);
2225 }
2226
2227 if (targets != 0) {
2228 struct _SelectionList *list;
2229
2230 target = targets[0];
2231
2232 if (targets[1] == None) { /* last target in list */
2233 params++;
2234 num_params--;
2235 targets = _SelectionTargets(w);
2236 } else {
2237 targets = &(targets[1]);
2238 }
2239
2240 if (num_params) {
2241 /* 'list' is freed in SelectionReceived */
2242 list = TypeXtMalloc(struct _SelectionList);
2243 if (list != 0) {
2244 list->params = params;
2245 list->count = num_params;
2246 list->targets = targets;
2247 list->time = ev_time;
2248 }
2249 } else {
2250 list = NULL;
2251 }
2252
2253 XtGetSelectionValue(w, selection,
2254 target,
2255 SelectionReceived,
2256 (XtPointer) list, ev_time);
2257 }
2258 }
2259 }
2260
2261 #if OPT_TRACE && OPT_WIDE_CHARS
2262 static void
2263 GettingSelection(Display *dpy, Atom type, Char *line, unsigned long len)
2264 {
2265 Char *cp;
2266 const char *name = TraceAtomName(dpy, type);
2267
2268 TRACE(("Getting %s (type=%ld, length=%ld)\n", name, (long int) type, len));
2269 for (cp = line; cp < line + len; cp++) {
2270 TRACE(("[%d:%lu]", (int) (cp + 1 - line), len));
2271 if (isprint(*cp)) {
2272 TRACE(("%c\n", *cp));
2273 } else {
2274 TRACE(("\\x%02x\n", *cp));
2275 }
2276 }
2277 }
2278 #else
2279 #define GettingSelection(dpy,type,line,len) /* nothing */
2280 #endif
2281
2282 #ifdef VMS
2283 # define tty_vwrite(pty,lag,l) tt_write(lag,l)
2284 #else /* !( VMS ) */
2285 # define tty_vwrite(pty,lag,l) v_write(pty,lag,(size_t) l)
2286 #endif /* defined VMS */
2287
2288 #if OPT_PASTE64
2289 /* Return base64 code character given 6-bit number */
2290 static const char base64_code[] = "\
2291 ABCDEFGHIJKLMNOPQRSTUVWXYZ\
2292 abcdefghijklmnopqrstuvwxyz\
2293 0123456789+/";
2294 static void
2295 base64_flush(TScreen *screen)
2296 {
2297 Char x;
2298
2299 TRACE(("base64_flush count %d, pad %d (%d)\n",
2300 screen->base64_count,
2301 screen->base64_pad,
2302 screen->base64_pad & 3));
2303
2304 switch (screen->base64_count) {
2305 case 0:
2306 break;
2307 case 2:
2308 x = CharOf(base64_code[screen->base64_accu << 4]);
2309 tty_vwrite(screen->respond, &x, 1);
2310 break;
2311 case 4:
2312 x = CharOf(base64_code[screen->base64_accu << 2]);
2313 tty_vwrite(screen->respond, &x, 1);
2314 break;
2315 }
2316 if (screen->base64_pad & 3) {
2317 tty_vwrite(screen->respond,
2318 (const Char *) "===",
2319 (unsigned) (3 - (screen->base64_pad & 3)));
2320 }
2321 screen->base64_count = 0;
2322 screen->base64_accu = 0;
2323 screen->base64_pad = 0;
2324 }
2325 #endif /* OPT_PASTE64 */
2326
2327 /*
2328 * Translate ISO-8859-1 or UTF-8 data to NRCS.
2329 */
2330 static void
2331 ToNational(XtermWidget xw, Char *buffer, size_t *length)
2332 {
2333 TScreen *screen = TScreenOf(xw);
2334 DECNRCM_codes gsetL = screen->gsets[screen->curgl];
2335 DECNRCM_codes gsetR = screen->gsets[screen->curgr];
2336
2337 #if OPT_WIDE_CHARS
2338 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
2339 Char *p;
2340 PtyData *data = TypeXtMallocX(PtyData, *length);
2341
2342 memset(data, 0, sizeof(*data));
2343 data->next = data->buffer;
2344 data->last = data->buffer + *length;
2345 memcpy(data->buffer, buffer, *length);
2346 p = buffer;
2347 while (data->next < data->last) {
2348 unsigned chr, out, gl, gr;
2349
2350 if (!decodeUtf8(screen, data)) {
2351 data->utf_size = 1;
2352 data->utf_data = data->next[0];
2353 }
2354 data->next += data->utf_size;
2355 chr = data->utf_data;
2356 out = chr;
2357 if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2358 out = gl;
2359 } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2360 out = gr;
2361 }
2362 *p++ = (Char) ((out < 256) ? out : ' ');
2363 }
2364 *length = (size_t) (p - buffer);
2365 free(data);
2366 } else
2367 #endif
2368 {
2369 Char *p;
2370
2371 for (p = buffer; (size_t) (p - buffer) < *length; ++p) {
2372 unsigned gl, gr;
2373 unsigned chr = *p;
2374 unsigned out = chr;
2375 if ((gl = xtermCharSetIn(xw, chr, gsetL)) != chr) {
2376 out = gl;
2377 } else if ((gr = xtermCharSetIn(xw, chr, gsetR)) != chr) {
2378 out = gr;
2379 }
2380 *p = (Char) out;
2381 }
2382 }
2383 }
2384
2385 static void
2386 _qWriteSelectionData(XtermWidget xw, Char *lag, size_t length)
2387 {
2388 TScreen *screen = TScreenOf(xw);
2389
2390 /*
2391 * If we are pasting into a window which is using NRCS, we want to map
2392 * the text from the normal encoding (ISO-8859-1 or UTF-8) into the coding
2393 * that an application would use to write characters with NRCS.
2394 *
2395 * TODO: handle conversion from UTF-8, and adjust length. This can be done
2396 * in the same buffer because the target is always 8-bit.
2397 */
2398 if ((xw->flags & NATIONAL) && (length != 0)) {
2399 ToNational(xw, lag, &length);
2400 }
2401 #if OPT_PASTE64
2402 if (screen->base64_paste) {
2403 /* Send data as base64 */
2404 Char *p = lag;
2405 Char buf[64];
2406 unsigned x = 0;
2407
2408 TRACE(("convert to base64 %lu:%s\n",
2409 (unsigned long) length,
2410 visibleChars(p, length)));
2411
2412 /*
2413 * Handle the case where the selection is from _this_ xterm, which
2414 * puts part of the reply in the buffer before the selection callback
2415 * happens.
2416 */
2417 if (screen->base64_paste && screen->unparse_len) {
2418 unparse_end(xw);
2419 }
2420 while (length--) {
2421 switch (screen->base64_count) {
2422 case 0:
2423 buf[x++] = CharOf(base64_code[*p >> 2]);
2424 screen->base64_accu = (unsigned) (*p & 0x3);
2425 screen->base64_count = 2;
2426 ++p;
2427 break;
2428 case 2:
2429 buf[x++] = CharOf(base64_code[(screen->base64_accu << 4) +
2430 (*p >> 4)]);
2431 screen->base64_accu = (unsigned) (*p & 0xF);
2432 screen->base64_count = 4;
2433 ++p;
2434 break;
2435 case 4:
2436 buf[x++] = CharOf(base64_code[(screen->base64_accu << 2) +
2437 (*p >> 6)]);
2438 buf[x++] = CharOf(base64_code[*p & 0x3F]);
2439 screen->base64_accu = 0;
2440 screen->base64_count = 0;
2441 ++p;
2442 break;
2443 }
2444 if (x >= 63) {
2445 /* Write 63 or 64 characters */
2446 screen->base64_pad += x;
2447 TRACE(("writing base64 interim %s\n", visibleChars(buf, x)));
2448 tty_vwrite(screen->respond, buf, x);
2449 x = 0;
2450 }
2451 }
2452 if (x != 0) {
2453 screen->base64_pad += x;
2454 TRACE(("writing base64 finish %s\n", visibleChars(buf, x)));
2455 tty_vwrite(screen->respond, buf, x);
2456 }
2457 } else
2458 #endif /* OPT_PASTE64 */
2459 #if OPT_READLINE
2460 if (SCREEN_FLAG(screen, paste_quotes)) {
2461 Char quote[2];
2462 quote[0] = (Char) get_tty_lnext(screen->respond, XTERM_LNEXT, "pty");
2463 quote[1] = 0;
2464 TRACE(("writing quoted selection data %s\n", visibleChars(lag, length)));
2465 while (length--) {
2466 tty_vwrite(screen->respond, quote, 1);
2467 tty_vwrite(screen->respond, lag++, 1);
2468 }
2469 } else
2470 #endif
2471 {
2472 TRACE(("writing selection data %s\n", visibleChars(lag, length)));
2473 tty_vwrite(screen->respond, lag, length);
2474 }
2475 }
2476
2477 static void
2478 _WriteSelectionData(XtermWidget xw, Char *line, size_t length)
2479 {
2480 #if OPT_PASTE64 || OPT_READLINE
2481 TScreen *screen = TScreenOf(xw);
2482 #endif
2483
2484 /* in the VMS version, if tt_pasting isn't set to True then qio
2485 reads aren't blocked and an infinite loop is entered, where the
2486 pasted text shows up as new input, goes in again, shows up
2487 again, ad nauseum. */
2488 #ifdef VMS
2489 tt_pasting = True;
2490 #endif
2491
2492 #if OPT_PASTE64
2493 if (screen->base64_paste) {
2494 _qWriteSelectionData(xw, line, length);
2495 base64_flush(screen);
2496 } else
2497 #endif
2498 {
2499 if (!SCREEN_FLAG(screen, paste_literal_nl)) {
2500 size_t n;
2501 for (n = 0; n < length; ++n) {
2502 if (line[n] == '\n') {
2503 line[n] = '\r';
2504 }
2505 }
2506 }
2507
2508 _qWriteSelectionData(xw, line, length);
2509 }
2510 #ifdef VMS
2511 tt_pasting = False;
2512 tt_start_read(); /* reenable reads or a character may be lost */
2513 #endif
2514 }
2515
2516 #if OPT_PASTE64 || OPT_READLINE
2517 static void
2518 _WriteKey(TScreen *screen, const Char *in)
2519 {
2520 Char line[16];
2521 unsigned count = 0;
2522 size_t length = strlen((const char *) in);
2523
2524 if (screen->control_eight_bits) {
2525 line[count++] = ANSI_CSI;
2526 } else {
2527 line[count++] = ANSI_ESC;
2528 line[count++] = '[';
2529 }
2530 while (length--)
2531 line[count++] = *in++;
2532 line[count++] = '~';
2533 tty_vwrite(screen->respond, line, count);
2534 }
2535 #endif /* OPT_READLINE */
2536
2537 /*
2538 * Unless enabled by the user, strip control characters other than formatting.
2539 */
2540 static size_t
2541 removeControls(XtermWidget xw, char *value)
2542 {
2543 TScreen *screen = TScreenOf(xw);
2544 size_t dst = 0;
2545
2546 if (screen->allowPasteControls) {
2547 dst = strlen(value);
2548 } else {
2549 size_t src = 0;
2550 while ((value[dst] = value[src]) != '\0') {
2551 int ch = CharOf(value[src++]);
2552
2553 #define ReplacePaste(n) \
2554 if (screen->disallow_paste_ops[n]) \
2555 value[dst] = ' '
2556
2557 if (ch < 32) {
2558 ReplacePaste(epC0);
2559 ReplacePaste(ch);
2560 ++dst;
2561 } else if (ch == ANSI_DEL) {
2562 ReplacePaste(epDEL);
2563 ++dst;
2564 }
2565 #if OPT_WIDE_CHARS
2566 else if (screen->utf8_inparse || screen->utf8_nrc_mode)
2567 ++dst;
2568 #endif
2569 #if OPT_C1_PRINT || OPT_WIDE_CHARS
2570 else if (screen->c1_printable)
2571 ++dst;
2572 #endif
2573 else if (ch >= 128 && ch < 160)
2574 continue;
2575 else
2576 ++dst;
2577 }
2578 }
2579 return dst;
2580 }
2581
2582 #if OPT_SELECTION_OPS
2583 static void
2584 beginInternalSelect(XtermWidget xw)
2585 {
2586 TScreen *screen = TScreenOf(xw);
2587 InternalSelect *mydata = &(screen->internal_select);
2588
2589 (void) mydata;
2590 /* override flags so that SelectionReceived only updates a buffer */
2591 #if OPT_PASTE64
2592 mydata->base64_paste = screen->base64_paste;
2593 screen->base64_paste = 0;
2594 #endif
2595 #if OPT_PASTE64 || OPT_READLINE
2596 mydata->paste_brackets = screen->paste_brackets;
2597 SCREEN_FLAG_unset(screen, paste_brackets);
2598 #endif
2599 }
2600
2601 static void
2602 finishInternalSelect(XtermWidget xw)
2603 {
2604 TScreen *screen = TScreenOf(xw);
2605 InternalSelect *mydata = &(screen->internal_select);
2606
2607 (void) mydata;
2608 #if OPT_PASTE64
2609 screen->base64_paste = mydata->base64_paste;
2610 #endif
2611 #if OPT_PASTE64 || OPT_READLINE
2612 screen->paste_brackets = mydata->paste_brackets;
2613 #endif
2614 }
2615
2616 #else
2617 #define finishInternalSelect(xw) /* nothing */
2618 #endif /* OPT_SELECTION_OPS */
2619
2620 /* SelectionReceived: stuff received selection text into pty */
2621
2622 /* ARGSUSED */
2623 static void
2624 SelectionReceived(Widget w,
2625 XtPointer client_data,
2626 Atom *selection GCC_UNUSED,
2627 Atom *type,
2628 XtPointer value,
2629 unsigned long *length,
2630 int *format)
2631 {
2632 char **text_list = NULL;
2633 int text_list_count = 0;
2634 XTextProperty text_prop;
2635 TScreen *screen;
2636 Display *dpy;
2637 #if OPT_TRACE && OPT_WIDE_CHARS
2638 Char *line = (Char *) value;
2639 #endif
2640
2641 XtermWidget xw;
2642
2643 if ((xw = getXtermWidget(w)) == 0)
2644 return;
2645
2646 screen = TScreenOf(xw);
2647 dpy = XtDisplay(w);
2648
2649 if (*type == 0 /*XT_CONVERT_FAIL */
2650 || *length == 0
2651 || value == NULL) {
2652 TRACE(("...no data to convert\n"));
2653 goto fail;
2654 }
2655
2656 text_prop.value = (unsigned char *) value;
2657 text_prop.encoding = *type;
2658 text_prop.format = *format;
2659 text_prop.nitems = *length;
2660
2661 TRACE(("SelectionReceived %s %s format %d, nitems %ld\n",
2662 TraceAtomName(screen->display, *selection),
2663 visibleSelectionTarget(dpy, text_prop.encoding),
2664 text_prop.format,
2665 text_prop.nitems));
2666
2667 #if OPT_WIDE_CHARS
2668 if (XSupportsLocale() && screen->wide_chars) {
2669 if (*type == XA_UTF8_STRING(dpy) ||
2670 *type == XA_STRING ||
2671 *type == XA_COMPOUND_TEXT(dpy)) {
2672 GettingSelection(dpy, *type, line, *length);
2673 if (Xutf8TextPropertyToTextList(dpy, &text_prop,
2674 &text_list,
2675 &text_list_count) < 0) {
2676 TRACE(("default Xutf8 Conversion failed\n"));
2677 text_list = NULL;
2678 }
2679 }
2680 } else
2681 #endif /* OPT_WIDE_CHARS */
2682 {
2683 /* Convert the selection to locale's multibyte encoding. */
2684
2685 if (*type == XA_UTF8_STRING(dpy) ||
2686 *type == XA_STRING ||
2687 *type == XA_COMPOUND_TEXT(dpy)) {
2688 Status rc;
2689
2690 GettingSelection(dpy, *type, line, *length);
2691
2692 #if OPT_WIDE_CHARS
2693 if (*type == XA_UTF8_STRING(dpy) &&
2694 !(screen->wide_chars || screen->c1_printable)) {
2695 rc = xtermUtf8ToTextList(xw, &text_prop,
2696 &text_list, &text_list_count);
2697 } else
2698 #endif
2699 if (*type == XA_STRING && (!XSupportsLocale() || screen->brokenSelections)) {
2700 rc = XTextPropertyToStringList(&text_prop,
2701 &text_list, &text_list_count);
2702 } else {
2703 rc = XmbTextPropertyToTextList(dpy, &text_prop,
2704 &text_list,
2705 &text_list_count);
2706 }
2707 if (rc < 0) {
2708 TRACE(("Conversion failed\n"));
2709 text_list = NULL;
2710 }
2711 }
2712 }
2713
2714 if (text_list != NULL && text_list_count != 0) {
2715 int i;
2716
2717 #if OPT_PASTE64
2718 if (screen->base64_paste) {
2719 /* EMPTY */ ;
2720 } else
2721 #endif
2722 #if OPT_PASTE64 || OPT_READLINE
2723 if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2724 _WriteKey(screen, (const Char *) "200");
2725 }
2726 #endif
2727 for (i = 0; i < text_list_count; i++) {
2728 size_t len = removeControls(xw, text_list[i]);
2729
2730 if (screen->selectToBuffer) {
2731 InternalSelect *mydata = &(screen->internal_select);
2732 if (!mydata->done) {
2733 size_t have = (mydata->buffer
2734 ? strlen(mydata->buffer)
2735 : 0);
2736 size_t need = have + len + 1;
2737 char *buffer = realloc(mydata->buffer, need);
2738
2739 if (buffer != 0) {
2740 strcpy(buffer + have, text_list[i]);
2741 mydata->buffer = buffer;
2742 }
2743 TRACE(("FormatSelect %d.%d .. %d.%d %s\n",
2744 screen->startSel.row,
2745 screen->startSel.col,
2746 screen->endSel.row,
2747 screen->endSel.col,
2748 mydata->buffer));
2749 mydata->format_select(w, mydata->format, mydata->buffer,
2750 &(screen->startSel),
2751 &(screen->endSel));
2752 mydata->done = True;
2753 }
2754
2755 } else {
2756 _WriteSelectionData(xw, (Char *) text_list[i], len);
2757 }
2758 }
2759 #if OPT_PASTE64
2760 if (screen->base64_paste) {
2761 FinishPaste64(xw);
2762 } else
2763 #endif
2764 #if OPT_PASTE64 || OPT_READLINE
2765 if (SCREEN_FLAG(screen, paste_brackets) && !screen->selectToBuffer) {
2766 _WriteKey(screen, (const Char *) "201");
2767 }
2768 #endif
2769 if (screen->selectToBuffer) {
2770 InternalSelect *mydata = &(screen->internal_select);
2771 finishInternalSelect(xw);
2772 if (mydata->done) {
2773 free(mydata->format);
2774 free(mydata->buffer);
2775 memset(mydata, 0, sizeof(*mydata));
2776 }
2777 screen->selectToBuffer = False;
2778 }
2779 XFreeStringList(text_list);
2780 } else {
2781 TRACE(("...empty text-list\n"));
2782 goto fail;
2783 }
2784
2785 XtFree((char *) client_data);
2786 XtFree((char *) value);
2787
2788 return;
2789
2790 fail:
2791 if (client_data != 0) {
2792 struct _SelectionList *list = (struct _SelectionList *) client_data;
2793
2794 TRACE(("SelectionReceived ->xtermGetSelection\n"));
2795 xtermGetSelection(w, list->time,
2796 list->params, list->count, list->targets);
2797 XtFree((char *) client_data);
2798 #if OPT_PASTE64
2799 } else {
2800 FinishPaste64(xw);
2801 #endif
2802 }
2803 return;
2804 }
2805
2806 void
2807 HandleInsertSelection(Widget w,
2808 XEvent *event, /* assumed to be XButtonEvent* */
2809 String *params, /* selections in precedence order */
2810 Cardinal *num_params)
2811 {
2812 XtermWidget xw;
2813
2814 if ((xw = getXtermWidget(w)) != 0) {
2815 TRACE_EVENT("HandleInsertSelection", event, params, num_params);
2816 if (!SendMousePosition(xw, event)) {
2817 #if OPT_READLINE
2818 int ldelta;
2819 TScreen *screen = TScreenOf(xw);
2820 if (IsBtnEvent(event)
2821 && !OverrideEvent(event)
2822 && (okSendMousePos(xw) == MOUSE_OFF)
2823 && SCREEN_FLAG(screen, paste_moves)
2824 && rowOnCurrentLine(screen, eventRow(screen, event), &ldelta))
2825 ReadLineMovePoint(xw, eventColBetween(screen, event), ldelta);
2826 #endif /* OPT_READLINE */
2827
2828 xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL);
2829 }
2830 }
2831 }
2832
2833 static SelectUnit
2834 EvalSelectUnit(XtermWidget xw,
2835 Time buttonDownTime,
2836 SelectUnit defaultUnit,
2837 unsigned int button)
2838 {
2839 TScreen *screen = TScreenOf(xw);
2840 SelectUnit result;
2841 int delta;
2842
2843 if (button != screen->lastButton) {
2844 delta = screen->multiClickTime + 1;
2845 } else if (screen->lastButtonUpTime == (Time) 0) {
2846 /* first time and once in a blue moon */
2847 delta = screen->multiClickTime + 1;
2848 } else if (buttonDownTime > screen->lastButtonUpTime) {
2849 /* most of the time */
2850 delta = (int) (buttonDownTime - screen->lastButtonUpTime);
2851 } else {
2852 /* time has rolled over since lastButtonUpTime */
2853 delta = (int) ((((Time) ~ 0) - screen->lastButtonUpTime) + buttonDownTime);
2854 }
2855
2856 if (delta > screen->multiClickTime) {
2857 screen->numberOfClicks = 1;
2858 result = defaultUnit;
2859 } else {
2860 result = screen->selectMap[screen->numberOfClicks % screen->maxClicks];
2861 screen->numberOfClicks += 1;
2862 }
2863 TRACE(("EvalSelectUnit(%d) = %d\n", screen->numberOfClicks, result));
2864 return result;
2865 }
2866
2867 static void
2868 do_select_start(XtermWidget xw,
2869 XEvent *event, /* must be XButtonEvent* */
2870 CELL *cell)
2871 {
2872 TScreen *screen = TScreenOf(xw);
2873
2874 if (SendMousePosition(xw, event))
2875 return;
2876 screen->selectUnit = EvalSelectUnit(xw,
2877 event->xbutton.time,
2878 Select_CHAR,
2879 event->xbutton.button);
2880 screen->replyToEmacs = False;
2881
2882 #if OPT_READLINE
2883 lastButtonDownTime = event->xbutton.time;
2884 #endif
2885
2886 StartSelect(xw, cell);
2887 }
2888
2889 /* ARGSUSED */
2890 void
2891 HandleSelectStart(Widget w,
2892 XEvent *event, /* must be XButtonEvent* */
2893 String *params GCC_UNUSED,
2894 Cardinal *num_params GCC_UNUSED)
2895 {
2896 XtermWidget xw;
2897
2898 if ((xw = getXtermWidget(w)) != 0) {
2899 TScreen *screen = TScreenOf(xw);
2900 CELL cell;
2901
2902 TRACE_EVENT("HandleSelectStart", event, params, num_params);
2903 screen->firstValidRow = 0;
2904 screen->lastValidRow = screen->max_row;
2905 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
2906
2907 #if OPT_READLINE
2908 ExtendingSelection = 0;
2909 #endif
2910
2911 do_select_start(xw, event, &cell);
2912 }
2913 }
2914
2915 /* ARGSUSED */
2916 void
2917 HandleKeyboardSelectStart(Widget w,
2918 XEvent *event, /* must be XButtonEvent* */
2919 String *params GCC_UNUSED,
2920 Cardinal *num_params GCC_UNUSED)
2921 {
2922 XtermWidget xw;
2923
2924 if ((xw = getXtermWidget(w)) != 0) {
2925 TScreen *screen = TScreenOf(xw);
2926
2927 TRACE_EVENT("HandleKeyboardSelectStart", event, params, num_params);
2928 do_select_start(xw, event, &screen->cursorp);
2929 }
2930 }
2931
2932 static void
2933 TrackDown(XtermWidget xw, XButtonEvent *event)
2934 {
2935 TScreen *screen = TScreenOf(xw);
2936 CELL cell;
2937
2938 screen->selectUnit = EvalSelectUnit(xw,
2939 event->time,
2940 Select_CHAR,
2941 event->button);
2942 if (screen->numberOfClicks > 1) {
2943 PointToCELL(screen, event->y, event->x, &cell);
2944 screen->replyToEmacs = True;
2945 StartSelect(xw, &cell);
2946 } else {
2947 screen->waitingForTrackInfo = True;
2948 EditorButton(xw, event);
2949 }
2950 }
2951
2952 #define boundsCheck(x) if (x < 0) \
2953 x = 0; \
2954 else if (x >= screen->max_row) \
2955 x = screen->max_row
2956
2957 void
2958 TrackMouse(XtermWidget xw,
2959 int func,
2960 CELL *start,
2961 int firstrow,
2962 int lastrow)
2963 {
2964 TScreen *screen = TScreenOf(xw);
2965
2966 if (screen->waitingForTrackInfo) { /* if Timed, ignore */
2967 screen->waitingForTrackInfo = False;
2968
2969 if (func != 0) {
2970 CELL first = *start;
2971
2972 boundsCheck(first.row);
2973 boundsCheck(firstrow);
2974 boundsCheck(lastrow);
2975 screen->firstValidRow = firstrow;
2976 screen->lastValidRow = lastrow;
2977 screen->replyToEmacs = True;
2978 StartSelect(xw, &first);
2979 }
2980 }
2981 }
2982
2983 static void
2984 StartSelect(XtermWidget xw, const CELL *cell)
2985 {
2986 TScreen *screen = TScreenOf(xw);
2987
2988 TRACE(("StartSelect row=%d, col=%d\n", cell->row, cell->col));
2989 if (screen->cursor_state)
2990 HideCursor(xw);
2991 if (screen->numberOfClicks == 1) {
2992 /* set start of selection */
2993 screen->rawPos = *cell;
2994 }
2995 /* else use old values in rawPos */
2996 screen->saveStartR = screen->startExt = screen->rawPos;
2997 screen->saveEndR = screen->endExt = screen->rawPos;
2998 if (Coordinate(screen, cell) < Coordinate(screen, &(screen->rawPos))) {
2999 screen->eventMode = LEFTEXTENSION;
3000 screen->startExt = *cell;
3001 } else {
3002 screen->eventMode = RIGHTEXTENSION;
3003 screen->endExt = *cell;
3004 }
3005 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3006 }
3007
3008 static void
3009 EndExtend(XtermWidget xw,
3010 XEvent *event, /* must be XButtonEvent */
3011 String *params, /* selections */
3012 Cardinal num_params,
3013 Bool use_cursor_loc)
3014 {
3015 CELL cell;
3016 TScreen *screen = TScreenOf(xw);
3017
3018 TRACE_EVENT("EndExtend", event, params, &num_params);
3019 if (use_cursor_loc) {
3020 cell = screen->cursorp;
3021 } else {
3022 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3023 }
3024 ExtendExtend(xw, &cell);
3025
3026 screen->lastButtonUpTime = event->xbutton.time;
3027 screen->lastButton = event->xbutton.button;
3028
3029 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3030 if (screen->replyToEmacs) {
3031 Char line[64];
3032 unsigned count = 0;
3033
3034 if (screen->control_eight_bits) {
3035 line[count++] = ANSI_CSI;
3036 } else {
3037 line[count++] = ANSI_ESC;
3038 line[count++] = '[';
3039 }
3040 if (isSameCELL(&(screen->rawPos), &(screen->startSel))
3041 && isSameCELL(&cell, &(screen->endSel))) {
3042 /* Use short-form emacs select */
3043
3044 switch (screen->extend_coords) {
3045 case 0:
3046 case SET_EXT_MODE_MOUSE:
3047 line[count++] = 't';
3048 break;
3049 case SET_SGR_EXT_MODE_MOUSE:
3050 case SET_PIXEL_POSITION_MOUSE:
3051 line[count++] = '<';
3052 break;
3053 }
3054
3055 count = EmitMousePosition(screen, line, count, screen->endSel.col);
3056 count = EmitMousePositionSeparator(screen, line, count);
3057 count = EmitMousePosition(screen, line, count, screen->endSel.row);
3058
3059 switch (screen->extend_coords) {
3060 case SET_SGR_EXT_MODE_MOUSE:
3061 case SET_URXVT_EXT_MODE_MOUSE:
3062 case SET_PIXEL_POSITION_MOUSE:
3063 line[count++] = 't';
3064 break;
3065 }
3066 } else {
3067 /* long-form, specify everything */
3068
3069 switch (screen->extend_coords) {
3070 case 0:
3071 case SET_EXT_MODE_MOUSE:
3072 line[count++] = 'T';
3073 break;
3074 case SET_SGR_EXT_MODE_MOUSE:
3075 case SET_PIXEL_POSITION_MOUSE:
3076 line[count++] = '<';
3077 break;
3078 }
3079
3080 count = EmitMousePosition(screen, line, count, screen->startSel.col);
3081 count = EmitMousePositionSeparator(screen, line, count);
3082 count = EmitMousePosition(screen, line, count, screen->startSel.row);
3083 count = EmitMousePositionSeparator(screen, line, count);
3084 count = EmitMousePosition(screen, line, count, screen->endSel.col);
3085 count = EmitMousePositionSeparator(screen, line, count);
3086 count = EmitMousePosition(screen, line, count, screen->endSel.row);
3087 count = EmitMousePositionSeparator(screen, line, count);
3088 count = EmitMousePosition(screen, line, count, cell.col);
3089 count = EmitMousePositionSeparator(screen, line, count);
3090 count = EmitMousePosition(screen, line, count, cell.row);
3091
3092 switch (screen->extend_coords) {
3093 case SET_SGR_EXT_MODE_MOUSE:
3094 case SET_URXVT_EXT_MODE_MOUSE:
3095 case SET_PIXEL_POSITION_MOUSE:
3096 line[count++] = 'T';
3097 break;
3098 }
3099 }
3100 v_write(screen->respond, line, (size_t) count);
3101 UnHiliteText(xw);
3102 }
3103 }
3104 SelectSet(xw, event, params, num_params);
3105 screen->eventMode = NORMAL;
3106 }
3107
3108 void
3109 HandleSelectSet(Widget w,
3110 XEvent *event,
3111 String *params,
3112 Cardinal *num_params)
3113 {
3114 XtermWidget xw;
3115
3116 if ((xw = getXtermWidget(w)) != 0) {
3117 TRACE_EVENT("HandleSelectSet", event, params, num_params);
3118 SelectSet(xw, event, params, *num_params);
3119 }
3120 }
3121
3122 /* ARGSUSED */
3123 static void
3124 SelectSet(XtermWidget xw,
3125 XEvent *event GCC_UNUSED,
3126 String *params,
3127 Cardinal num_params)
3128 {
3129 TScreen *screen = TScreenOf(xw);
3130
3131 TRACE(("SelectSet\n"));
3132 /* Only do select stuff if non-null select */
3133 if (!isSameCELL(&(screen->startSel), &(screen->endSel))) {
3134 Cardinal n;
3135 for (n = 0; n < num_params; ++n) {
3136 SaltTextAway(xw,
3137 TargetToSelection(screen, params[n]),
3138 &(screen->startSel), &(screen->endSel));
3139 }
3140 _OwnSelection(xw, params, num_params);
3141 } else {
3142 ScrnDisownSelection(xw);
3143 }
3144 }
3145
3146 #define Abs(x) ((x) < 0 ? -(x) : (x))
3147
3148 /* ARGSUSED */
3149 static void
3150 do_start_extend(XtermWidget xw,
3151 XEvent *event, /* must be XButtonEvent* */
3152 String *params GCC_UNUSED,
3153 Cardinal *num_params GCC_UNUSED,
3154 Bool use_cursor_loc)
3155 {
3156 TScreen *screen = TScreenOf(xw);
3157 int coord;
3158 CELL cell;
3159
3160 if (SendMousePosition(xw, event))
3161 return;
3162
3163 screen->firstValidRow = 0;
3164 screen->lastValidRow = screen->max_row;
3165 #if OPT_READLINE
3166 if (OverrideEvent(event)
3167 || event->xbutton.button != Button3
3168 || !(SCREEN_FLAG(screen, dclick3_deletes)))
3169 #endif
3170 screen->selectUnit = EvalSelectUnit(xw,
3171 event->xbutton.time,
3172 screen->selectUnit,
3173 event->xbutton.button);
3174 screen->replyToEmacs = False;
3175
3176 #if OPT_READLINE
3177 CheckSecondPress3(xw, screen, event);
3178 #endif
3179
3180 if (screen->numberOfClicks == 1
3181 || (SCREEN_FLAG(screen, dclick3_deletes)
3182 && !OverrideEvent(event))) {
3183 /* Save existing selection so we can reestablish it if the guy
3184 extends past the other end of the selection */
3185 screen->saveStartR = screen->startExt = screen->startRaw;
3186 screen->saveEndR = screen->endExt = screen->endRaw;
3187 } else {
3188 /* He just needed the selection mode changed, use old values. */
3189 screen->startExt = screen->startRaw = screen->saveStartR;
3190 screen->endExt = screen->endRaw = screen->saveEndR;
3191 }
3192 if (use_cursor_loc) {
3193 cell = screen->cursorp;
3194 } else {
3195 PointToCELL(screen, event->xbutton.y, event->xbutton.x, &cell);
3196 }
3197 coord = Coordinate(screen, &cell);
3198
3199 if (Abs(coord - Coordinate(screen, &(screen->startSel)))
3200 < Abs(coord - Coordinate(screen, &(screen->endSel)))
3201 || coord < Coordinate(screen, &(screen->startSel))) {
3202 /* point is close to left side of selection */
3203 screen->eventMode = LEFTEXTENSION;
3204 screen->startExt = cell;
3205 } else {
3206 /* point is close to left side of selection */
3207 screen->eventMode = RIGHTEXTENSION;
3208 screen->endExt = cell;
3209 }
3210 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), True, True);
3211
3212 #if OPT_READLINE
3213 if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3214 ExtendingSelection = 1;
3215 #endif
3216 }
3217
3218 static void
3219 ExtendExtend(XtermWidget xw, const CELL *cell)
3220 {
3221 TScreen *screen = TScreenOf(xw);
3222 int coord = Coordinate(screen, cell);
3223
3224 TRACE(("ExtendExtend row=%d, col=%d\n", cell->row, cell->col));
3225 if (screen->eventMode == LEFTEXTENSION
3226 && ((coord + (screen->selectUnit != Select_CHAR))
3227 > Coordinate(screen, &(screen->endSel)))) {
3228 /* Whoops, he's changed his mind. Do RIGHTEXTENSION */
3229 screen->eventMode = RIGHTEXTENSION;
3230 screen->startExt = screen->saveStartR;
3231 } else if (screen->eventMode == RIGHTEXTENSION
3232 && coord < Coordinate(screen, &(screen->startSel))) {
3233 /* Whoops, he's changed his mind. Do LEFTEXTENSION */
3234 screen->eventMode = LEFTEXTENSION;
3235 screen->endExt = screen->saveEndR;
3236 }
3237 if (screen->eventMode == LEFTEXTENSION) {
3238 screen->startExt = *cell;
3239 } else {
3240 screen->endExt = *cell;
3241 }
3242 ComputeSelect(xw, &(screen->startExt), &(screen->endExt), False, True);
3243
3244 #if OPT_READLINE
3245 if (!isSameCELL(&(screen->startSel), &(screen->endSel)))
3246 ExtendingSelection = 1;
3247 #endif
3248 }
3249
3250 void
3251 HandleStartExtend(Widget w,
3252 XEvent *event, /* must be XButtonEvent* */
3253 String *params, /* unused */
3254 Cardinal *num_params) /* unused */
3255 {
3256 XtermWidget xw;
3257
3258 if ((xw = getXtermWidget(w)) != 0) {
3259 TRACE_EVENT("HandleStartExtend", event, params, num_params);
3260 do_start_extend(xw, event, params, num_params, False);
3261 }
3262 }
3263
3264 void
3265 HandleKeyboardStartExtend(Widget w,
3266 XEvent *event, /* must be XButtonEvent* */
3267 String *params, /* unused */
3268 Cardinal *num_params) /* unused */
3269 {
3270 XtermWidget xw;
3271
3272 if ((xw = getXtermWidget(w)) != 0) {
3273 TRACE_EVENT("HandleKeyboardStartExtend", event, params, num_params);
3274 do_start_extend(xw, event, params, num_params, True);
3275 }
3276 }
3277
3278 void
3279 ScrollSelection(TScreen *screen, int amount, Bool always)
3280 {
3281 int minrow = INX2ROW(screen, -screen->savedlines);
3282 int maxrow = INX2ROW(screen, screen->max_row);
3283 int maxcol = screen->max_col;
3284
3285 #define scroll_update_one(cell) \
3286 (cell)->row += amount; \
3287 if ((cell)->row < minrow) { \
3288 (cell)->row = minrow; \
3289 (cell)->col = 0; \
3290 } \
3291 if ((cell)->row > maxrow) { \
3292 (cell)->row = maxrow; \
3293 (cell)->col = maxcol; \
3294 }
3295
3296 scroll_update_one(&(screen->startRaw));
3297 scroll_update_one(&(screen->endRaw));
3298 scroll_update_one(&(screen->startSel));
3299 scroll_update_one(&(screen->endSel));
3300
3301 scroll_update_one(&(screen->rawPos));
3302
3303 /*
3304 * If we are told to scroll the selection but it lies outside the scrolling
3305 * margins, then that could cause the selection to move (bad). It is not
3306 * simple to fix, because this function is called both for the scrollbar
3307 * actions as well as application scrolling. The 'always' flag is set in
3308 * the former case. The rest of the logic handles the latter.
3309 */
3310 if (ScrnHaveSelection(screen)) {
3311 int adjust;
3312
3313 adjust = ROW2INX(screen, screen->startH.row);
3314 if (always
3315 || !ScrnHaveRowMargins(screen)
3316 || ScrnIsRowInMargins(screen, adjust)) {
3317 scroll_update_one(&screen->startH);
3318 }
3319 adjust = ROW2INX(screen, screen->endH.row);
3320 if (always
3321 || !ScrnHaveRowMargins(screen)
3322 || ScrnIsRowInMargins(screen, adjust)) {
3323 scroll_update_one(&screen->endH);
3324 }
3325 }
3326
3327 screen->startHCoord = Coordinate(screen, &screen->startH);
3328 screen->endHCoord = Coordinate(screen, &screen->endH);
3329 }
3330
3331 /*ARGSUSED*/
3332 void
3333 ResizeSelection(TScreen *screen, int rows, int cols)
3334 {
3335 rows--; /* decr to get 0-max */
3336 cols--;
3337
3338 if (screen->startRaw.row > rows)
3339 screen->startRaw.row = rows;
3340 if (screen->startSel.row > rows)
3341 screen->startSel.row = rows;
3342 if (screen->endRaw.row > rows)
3343 screen->endRaw.row = rows;
3344 if (screen->endSel.row > rows)
3345 screen->endSel.row = rows;
3346 if (screen->rawPos.row > rows)
3347 screen->rawPos.row = rows;
3348
3349 if (screen->startRaw.col > cols)
3350 screen->startRaw.col = cols;
3351 if (screen->startSel.col > cols)
3352 screen->startSel.col = cols;
3353 if (screen->endRaw.col > cols)
3354 screen->endRaw.col = cols;
3355 if (screen->endSel.col > cols)
3356 screen->endSel.col = cols;
3357 if (screen->rawPos.col > cols)
3358 screen->rawPos.col = cols;
3359 }
3360
3361 #if OPT_WIDE_CHARS
3362 #define isWideCell(row, col) isWideFrg((int)XTERM_CELL(row, col))
3363 #endif
3364
3365 static void
3366 PointToCELL(TScreen *screen,
3367 int y,
3368 int x,
3369 CELL *cell)
3370 /* Convert pixel coordinates to character coordinates.
3371 Rows are clipped between firstValidRow and lastValidRow.
3372 Columns are clipped between to be 0 or greater, but are not clipped to some
3373 maximum value. */
3374 {
3375 cell->row = (y - screen->border) / FontHeight(screen);
3376 if (cell->row < screen->firstValidRow)
3377 cell->row = screen->firstValidRow;
3378 else if (cell->row > screen->lastValidRow)
3379 cell->row = screen->lastValidRow;
3380 cell->col = (x - OriginX(screen)) / FontWidth(screen);
3381 if (cell->col < 0)
3382 cell->col = 0;
3383 else if (cell->col > MaxCols(screen)) {
3384 cell->col = MaxCols(screen);
3385 }
3386 #if OPT_WIDE_CHARS
3387 /*
3388 * If we got a click on the right half of a doublewidth character,
3389 * pretend it happened on the left half.
3390 */
3391 if (cell->col > 0
3392 && isWideCell(cell->row, cell->col - 1)
3393 && (XTERM_CELL(cell->row, cell->col) == HIDDEN_CHAR)) {
3394 cell->col -= 1;
3395 }
3396 #endif
3397 }
3398
3399 /*
3400 * Find the last column at which text was drawn on the given row.
3401 */
3402 static int
3403 LastTextCol(TScreen *screen, CLineData *ld, int row)
3404 {
3405 int i = -1;
3406
3407 if (ld != 0) {
3408 if (okScrnRow(screen, row)) {
3409 const IAttr *ch;
3410 for (i = screen->max_col,
3411 ch = ld->attribs + i;
3412 i >= 0 && !(*ch & CHARDRAWN);
3413 ch--, i--) {
3414 ;
3415 }
3416 #if OPT_DEC_CHRSET
3417 if (CSET_DOUBLE(GetLineDblCS(ld))) {
3418 i *= 2;
3419 }
3420 #endif
3421 }
3422 }
3423 return (i);
3424 }
3425
3426 #if !OPT_WIDE_CHARS
3427 /*
3428 ** double click table for cut and paste in 8 bits
3429 **
3430 ** This table is divided in four parts :
3431 **
3432 ** - control characters [0,0x1f] U [0x80,0x9f]
3433 ** - separators [0x20,0x3f] U [0xa0,0xb9]
3434 ** - binding characters [0x40,0x7f] U [0xc0,0xff]
3435 ** - exceptions
3436 */
3437 /* *INDENT-OFF* */
3438 static int charClass[256] =
3439 {
3440 /* NUL SOH STX ETX EOT ENQ ACK BEL */
3441 32, 1, 1, 1, 1, 1, 1, 1,
3442 /* BS HT NL VT FF CR SO SI */
3443 1, 32, 1, 1, 1, 1, 1, 1,
3444 /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
3445 1, 1, 1, 1, 1, 1, 1, 1,
3446 /* CAN EM SUB ESC FS GS RS US */
3447 1, 1, 1, 1, 1, 1, 1, 1,
3448 /* SP ! " # $ % & ' */
3449 32, 33, 34, 35, 36, 37, 38, 39,
3450 /* ( ) * + , - . / */
3451 40, 41, 42, 43, 44, 45, 46, 47,
3452 /* 0 1 2 3 4 5 6 7 */
3453 48, 48, 48, 48, 48, 48, 48, 48,
3454 /* 8 9 : ; < = > ? */
3455 48, 48, 58, 59, 60, 61, 62, 63,
3456 /* @ A B C D E F G */
3457 64, 48, 48, 48, 48, 48, 48, 48,
3458 /* H I J K L M N O */
3459 48, 48, 48, 48, 48, 48, 48, 48,
3460 /* P Q R S T U V W */
3461 48, 48, 48, 48, 48, 48, 48, 48,
3462 /* X Y Z [ \ ] ^ _ */
3463 48, 48, 48, 91, 92, 93, 94, 48,
3464 /* ` a b c d e f g */
3465 96, 48, 48, 48, 48, 48, 48, 48,
3466 /* h i j k l m n o */
3467 48, 48, 48, 48, 48, 48, 48, 48,
3468 /* p q r s t u v w */
3469 48, 48, 48, 48, 48, 48, 48, 48,
3470 /* x y z { | } ~ DEL */
3471 48, 48, 48, 123, 124, 125, 126, 1,
3472 /* x80 x81 x82 x83 IND NEL SSA ESA */
3473 1, 1, 1, 1, 1, 1, 1, 1,
3474 /* HTS HTJ VTS PLD PLU RI SS2 SS3 */
3475 1, 1, 1, 1, 1, 1, 1, 1,
3476 /* DCS PU1 PU2 STS CCH MW SPA EPA */
3477 1, 1, 1, 1, 1, 1, 1, 1,
3478 /* x98 x99 x9A CSI ST OSC PM APC */
3479 1, 1, 1, 1, 1, 1, 1, 1,
3480 /* - i c/ L ox Y- | So */
3481 160, 161, 162, 163, 164, 165, 166, 167,
3482 /* .. c0 ip << _ R0 - */
3483 168, 169, 170, 171, 172, 173, 174, 175,
3484 /* o +- 2 3 ' u q| . */
3485 176, 177, 178, 179, 180, 181, 182, 183,
3486 /* , 1 2 >> 1/4 1/2 3/4 ? */
3487 184, 185, 186, 187, 188, 189, 190, 191,
3488 /* A` A' A^ A~ A: Ao AE C, */
3489 48, 48, 48, 48, 48, 48, 48, 48,
3490 /* E` E' E^ E: I` I' I^ I: */
3491 48, 48, 48, 48, 48, 48, 48, 48,
3492 /* D- N~ O` O' O^ O~ O: X */
3493 48, 48, 48, 48, 48, 48, 48, 215,
3494 /* O/ U` U' U^ U: Y' P B */
3495 48, 48, 48, 48, 48, 48, 48, 48,
3496 /* a` a' a^ a~ a: ao ae c, */
3497 48, 48, 48, 48, 48, 48, 48, 48,
3498 /* e` e' e^ e: i` i' i^ i: */
3499 48, 48, 48, 48, 48, 48, 48, 48,
3500 /* d n~ o` o' o^ o~ o: -: */
3501 48, 48, 48, 48, 48, 48, 48, 247,
3502 /* o/ u` u' u^ u: y' P y: */
3503 48, 48, 48, 48, 48, 48, 48, 48};
3504 /* *INDENT-ON* */
3505
3506 int
3507 SetCharacterClassRange(int low, /* in range of [0..255] */
3508 int high,
3509 int value) /* arbitrary */
3510 {
3511
3512 if (low < 0 || high > 255 || high < low)
3513 return (-1);
3514
3515 for (; low <= high; low++)
3516 charClass[low] = value;
3517
3518 return (0);
3519 }
3520 #endif
3521
3522 static int
3523 class_of(LineData *ld, CELL *cell)
3524 {
3525 CELL temp = *cell;
3526 int result = 0;
3527
3528 #if OPT_DEC_CHRSET
3529 if (CSET_DOUBLE(GetLineDblCS(ld))) {
3530 temp.col /= 2;
3531 }
3532 #endif
3533 if (temp.col < (int) ld->lineSize)
3534 result = CharacterClass((int) (ld->charData[temp.col]));
3535 return result;
3536 }
3537
3538 #if OPT_WIDE_CHARS
3539 #define CClassSelects(name, cclass) \
3540 (CClassOf(name) == cclass \
3541 || XTERM_CELL(screen->name.row, screen->name.col) == HIDDEN_CHAR)
3542 #else
3543 #define CClassSelects(name, cclass) \
3544 (class_of(ld.name, &((screen->name))) == cclass)
3545 #endif
3546
3547 #define CClassOf(name) class_of(ld.name, &((screen->name)))
3548
3549 #if OPT_REPORT_CCLASS
3550 static int
3551 show_cclass_range(int lo, int hi)
3552 {
3553 int cclass = CharacterClass(lo);
3554 int ident = (cclass == lo);
3555 int more = 0;
3556 if (ident) {
3557 int ch;
3558 for (ch = lo + 1; ch <= hi; ch++) {
3559 if (CharacterClass(ch) != ch) {
3560 ident = 0;
3561 break;
3562 }
3563 }
3564 if (ident && (hi < 255)) {
3565 ch = hi + 1;
3566 if (CharacterClass(ch) == ch) {
3567 if (ch >= 255 || CharacterClass(ch + 1) != ch) {
3568 more = 1;
3569 }
3570 }
3571 }
3572 }
3573 if (!more) {
3574 if (lo == hi) {
3575 printf("\t%d", lo);
3576 } else {
3577 printf("\t%d-%d", lo, hi);
3578 }
3579 if (!ident)
3580 printf(":%d", cclass);
3581 if (hi < 255)
3582 printf(", \\");
3583 printf("\n");
3584 }
3585 return !more;
3586 }
3587
3588 void
3589 report_char_class(XtermWidget xw)
3590 {
3591 /* simple table, to match documentation */
3592 static const char charnames[] =
3593 "NUL\0" "SOH\0" "STX\0" "ETX\0" "EOT\0" "ENQ\0" "ACK\0" "BEL\0"
3594 " BS\0" " HT\0" " NL\0" " VT\0" " NP\0" " CR\0" " SO\0" " SI\0"
3595 "DLE\0" "DC1\0" "DC2\0" "DC3\0" "DC4\0" "NAK\0" "SYN\0" "ETB\0"
3596 "CAN\0" " EM\0" "SUB\0" "ESC\0" " FS\0" " GS\0" " RS\0" " US\0"
3597 " SP\0" " !\0" " \"\0" " #\0" " $\0" " %\0" " &\0" " '\0"
3598 " (\0" " )\0" " *\0" " +\0" " ,\0" " -\0" " .\0" " /\0"
3599 " 0\0" " 1\0" " 2\0" " 3\0" " 4\0" " 5\0" " 6\0" " 7\0"
3600 " 8\0" " 9\0" " :\0" " ;\0" " <\0" " =\0" " >\0" " ?\0"
3601 " @\0" " A\0" " B\0" " C\0" " D\0" " E\0" " F\0" " G\0"
3602 " H\0" " I\0" " J\0" " K\0" " L\0" " M\0" " N\0" " O\0"
3603 " P\0" " Q\0" " R\0" " S\0" " T\0" " U\0" " V\0" " W\0"
3604 " X\0" " Y\0" " Z\0" " [\0" " \\\0" " ]\0" " ^\0" " _\0"
3605 " `\0" " a\0" " b\0" " c\0" " d\0" " e\0" " f\0" " g\0"
3606 " h\0" " i\0" " j\0" " k\0" " l\0" " m\0" " n\0" " o\0"
3607 " p\0" " q\0" " r\0" " s\0" " t\0" " u\0" " v\0" " w\0"
3608 " x\0" " y\0" " z\0" " {\0" " |\0" " }\0" " ~\0" "DEL\0"
3609 "x80\0" "x81\0" "x82\0" "x83\0" "IND\0" "NEL\0" "SSA\0" "ESA\0"
3610 "HTS\0" "HTJ\0" "VTS\0" "PLD\0" "PLU\0" " RI\0" "SS2\0" "SS3\0"
3611 "DCS\0" "PU1\0" "PU2\0" "STS\0" "CCH\0" " MW\0" "SPA\0" "EPA\0"
3612 "x98\0" "x99\0" "x9A\0" "CSI\0" " ST\0" "OSC\0" " PM\0" "APC\0"
3613 " -\0" " i\0" " c/\0" " L\0" " ox\0" " Y-\0" " |\0" " So\0"
3614 " ..\0" " c0\0" " ip\0" " <<\0" " _\0" " \0" " R0\0" " -\0"
3615 " o\0" " +-\0" " 2\0" " 3\0" " '\0" " u\0" " q|\0" " .\0"
3616 " ,\0" " 1\0" " 2\0" " >>\0" "1/4\0" "1/2\0" "3/4\0" " ?\0"
3617 " A`\0" " A'\0" " A^\0" " A~\0" " A:\0" " Ao\0" " AE\0" " C,\0"
3618 " E`\0" " E'\0" " E^\0" " E:\0" " I`\0" " I'\0" " I^\0" " I:\0"
3619 " D-\0" " N~\0" " O`\0" " O'\0" " O^\0" " O~\0" " O:\0" " X\0"
3620 " O/\0" " U`\0" " U'\0" " U^\0" " U:\0" " Y'\0" " P\0" " B\0"
3621 " a`\0" " a'\0" " a^\0" " a~\0" " a:\0" " ao\0" " ae\0" " c,\0"
3622 " e`\0" " e'\0" " e^\0" " e:\0" " i`\0" " i'\0" " i^\0" " i:\0"
3623 " d\0" " n~\0" " o`\0" " o'\0" " o^\0" " o~\0" " o:\0" " -:\0"
3624 " o/\0" " u`\0" " u'\0" " u^\0" " u:\0" " y'\0" " P\0" " y:\0";
3625 int ch, dh;
3626 int class_p;
3627
3628 (void) xw;
3629
3630 printf("static int charClass[256] = {\n");
3631 for (ch = 0; ch < 256; ++ch) {
3632 const char *s = charnames + (ch * 4);
3633 if ((ch & 7) == 0)
3634 printf("/*");
3635 printf(" %s ", s);
3636 if (((ch + 1) & 7) == 0) {
3637 printf("*/\n ");
3638 for (dh = ch - 7; dh <= ch; ++dh) {
3639 printf(" %3d%s", CharacterClass(dh), dh == 255 ? "};" : ",");
3640 }
3641 printf("\n");
3642 }
3643 }
3644
3645 /* print the table as if it were the charClass resource */
3646 printf("\n");
3647 printf("The table is equivalent to this \"charClass\" resource:\n");
3648 class_p = CharacterClass(dh = 0);
3649 for (ch = 0; ch < 256; ++ch) {
3650 int class_c = CharacterClass(ch);
3651 if (class_c != class_p) {
3652 if (show_cclass_range(dh, ch - 1)) {
3653 dh = ch;
3654 class_p = class_c;
3655 }
3656 }
3657 }
3658 if (dh < 255) {
3659 show_cclass_range(dh, 255);
3660 }
3661
3662 if_OPT_WIDE_CHARS(TScreenOf(xw), {
3663 /* if this is a wide-character configuration, print all intervals */
3664 report_wide_char_class();
3665 });
3666 }
3667 #endif
3668
3669 /*
3670 * If the given column is past the end of text on the given row, bump to the
3671 * beginning of the next line.
3672 */
3673 static Boolean
3674 okPosition(TScreen *screen,
3675 LineData **ld,
3676 CELL *cell)
3677 {
3678 Boolean result = True;
3679
3680 if (cell->row > screen->max_row) {
3681 result = False;
3682 TRACE(("okPosition cell row %d > screen max %d\n", cell->row, screen->max_row));
3683 } else if (cell->col > (LastTextCol(screen, *ld, cell->row) + 1)) {
3684 TRACE(("okPosition cell col %d > screen max %d\n", cell->col,
3685 (LastTextCol(screen, *ld, cell->row) + 1)));
3686 if (cell->row < screen->max_row) {
3687 TRACE(("okPosition cell row %d < screen max %d\n", cell->row, screen->max_row));
3688 cell->col = 0;
3689 *ld = GET_LINEDATA(screen, ++cell->row);
3690 result = False;
3691 }
3692 }
3693 return result;
3694 }
3695
3696 static void
3697 trimLastLine(TScreen *screen,
3698 LineData **ld,
3699 CELL *last)
3700 {
3701 if (screen->cutNewline && last->row < screen->max_row) {
3702 last->col = 0;
3703 *ld = GET_LINEDATA(screen, ++last->row);
3704 } else {
3705 last->col = LastTextCol(screen, *ld, last->row) + 1;
3706 }
3707 }
3708
3709 #if OPT_SELECT_REGEX
3710 /*
3711 * Returns the first row of a wrapped line.
3712 */
3713 static int
3714 firstRowOfLine(TScreen *screen, int row, Bool visible)
3715 {
3716 LineData *ld = 0;
3717 int limit = visible ? 0 : -screen->savedlines;
3718
3719 while (row > limit &&
3720 (ld = GET_LINEDATA(screen, row - 1)) != 0 &&
3721 LineTstWrapped(ld)) {
3722 --row;
3723 }
3724 return row;
3725 }
3726
3727 /*
3728 * Returns the last row of a wrapped line.
3729 */
3730 static int
3731 lastRowOfLine(TScreen *screen, int row)
3732 {
3733 LineData *ld;
3734
3735 while (row < screen->max_row &&
3736 (ld = GET_LINEDATA(screen, row)) != 0 &&
3737 LineTstWrapped(ld)) {
3738 ++row;
3739 }
3740 return row;
3741 }
3742
3743 /*
3744 * Returns the number of cells on the range of rows.
3745 */
3746 static unsigned
3747 lengthOfLines(TScreen *screen, int firstRow, int lastRow)
3748 {
3749 unsigned length = 0;
3750 int n;
3751
3752 for (n = firstRow; n <= lastRow; ++n) {
3753 LineData *ld = GET_LINEDATA(screen, n);
3754 int value = LastTextCol(screen, ld, n);
3755 if (value >= 0)
3756 length += (unsigned) (value + 1);
3757 }
3758 return length;
3759 }
3760
3761 /*
3762 * Make a copy of the wrapped-line which corresponds to the given row as a
3763 * string of bytes. Construct an index for the columns from the beginning of
3764 * the line.
3765 */
3766 static char *
3767 make_indexed_text(TScreen *screen, int row, unsigned length, int *indexed)
3768 {
3769 Char *result = 0;
3770 size_t need = (length + 1);
3771
3772 /*
3773 * Get a quick upper bound to the number of bytes needed, if the whole
3774 * string were UTF-8.
3775 */
3776 if_OPT_WIDE_CHARS(screen, {
3777 need *= ((screen->lineExtra + 1) * 6);
3778 });
3779
3780 if ((result = TypeCallocN(Char, need + 1)) != 0) {
3781 LineData *ld = GET_LINEDATA(screen, row);
3782 unsigned used = 0;
3783 Char *last = result;
3784
3785 do {
3786 int col = 0;
3787 int limit = LastTextCol(screen, ld, row);
3788
3789 while (col <= limit) {
3790 Char *next = last;
3791 unsigned data = ld->charData[col];
3792
3793 assert(col < (int) ld->lineSize);
3794 /* some internal points may not be drawn */
3795 if (data == 0)
3796 data = ' ';
3797
3798 if_WIDE_OR_NARROW(screen, {
3799 next = convertToUTF8(last, data);
3800 }
3801 , {
3802 *next++ = CharOf(data);
3803 });
3804
3805 if_OPT_WIDE_CHARS(screen, {
3806 size_t off;
3807 for_each_combData(off, ld) {
3808 data = ld->combData[off][col];
3809 if (data == 0)
3810 break;
3811 next = convertToUTF8(next, data);
3812 }
3813 });
3814
3815 indexed[used] = (int) (last - result);
3816 *next = 0;
3817 /* TRACE(("index[%d.%d] %d:%s\n", row, used, indexed[used], last)); */
3818 last = next;
3819 ++used;
3820 ++col;
3821 indexed[used] = (int) (next - result);
3822 }
3823 } while (used < length &&
3824 LineTstWrapped(ld) &&
3825 (ld = GET_LINEDATA(screen, ++row)) != 0 &&
3826 row < screen->max_row);
3827 }
3828 /* TRACE(("result:%s\n", result)); */
3829 return (char *) result;
3830 }
3831
3832 /*
3833 * Find the column given an offset into the character string by using the
3834 * index constructed in make_indexed_text().
3835 */
3836 static int
3837 indexToCol(int *indexed, int len, int off)
3838 {
3839 int col = 0;
3840 while (indexed[col] < len) {
3841 if (indexed[col] >= off)
3842 break;
3843 ++col;
3844 }
3845 return col;
3846 }
3847
3848 /*
3849 * Given a row number, and a column offset from that (which may be wrapped),
3850 * set the cell to the actual row/column values.
3851 */
3852 static void
3853 columnToCell(TScreen *screen, int row, int col, CELL *cell)
3854 {
3855 while (row < screen->max_row) {
3856 CLineData *ld = GET_LINEDATA(screen, row);
3857 int last = LastTextCol(screen, ld, row);
3858
3859 /* TRACE(("last(%d) = %d, have %d\n", row, last, col)); */
3860 if (col <= last) {
3861 break;
3862 }
3863 /*
3864 * Stop if the current row does not wrap (does not continue the current
3865 * line).
3866 */
3867 if (!LineTstWrapped(ld)) {
3868 col = last + 1;
3869 break;
3870 }
3871 col -= (last + 1);
3872 ++row;
3873 }
3874 if (col < 0)
3875 col = 0;
3876 cell->row = row;
3877 cell->col = col;
3878 }
3879
3880 /*
3881 * Given a cell, find the corresponding column offset.
3882 */
3883 static int
3884 cellToColumn(TScreen *screen, CELL *cell)
3885 {
3886 CLineData *ld = 0;
3887 int col = cell->col;
3888 int row = firstRowOfLine(screen, cell->row, False);
3889 while (row < cell->row) {
3890 ld = GET_LINEDATA(screen, row);
3891 col += LastTextCol(screen, ld, row++);
3892 }
3893 #if OPT_DEC_CHRSET
3894 if (ld == 0)
3895 ld = GET_LINEDATA(screen, row);
3896 if (CSET_DOUBLE(GetLineDblCS(ld)))
3897 col /= 2;
3898 #endif
3899 return col;
3900 }
3901
3902 static void
3903 do_select_regex(TScreen *screen, CELL *startc, CELL *endc)
3904 {
3905 LineData *ld = GET_LINEDATA(screen, startc->row);
3906 int inx = ((screen->numberOfClicks - 1) % screen->maxClicks);
3907 char *expr = screen->selectExpr[inx];
3908 regex_t preg;
3909 regmatch_t match;
3910
3911 TRACE(("Select_REGEX[%d]:%s\n", inx, NonNull(expr)));
3912 if (okPosition(screen, &ld, startc) && expr != 0) {
3913 if (regcomp(&preg, expr, REG_EXTENDED) == 0) {
3914 int firstRow = firstRowOfLine(screen, startc->row, True);
3915 int lastRow = lastRowOfLine(screen, firstRow);
3916 unsigned size = lengthOfLines(screen, firstRow, lastRow);
3917 int actual = cellToColumn(screen, startc);
3918 int *indexed;
3919
3920 TRACE(("regcomp ok rows %d..%d bytes %d\n",
3921 firstRow, lastRow, size));
3922
3923 if ((indexed = TypeCallocN(int, size + 1)) != 0) {
3924 char *search;
3925 if ((search = make_indexed_text(screen,
3926 firstRow,
3927 size,
3928 indexed)) != 0) {
3929 int len = (int) strlen(search);
3930 int col;
3931 int best_col = -1;
3932 int best_len = -1;
3933
3934 startc->row = 0;
3935 startc->col = 0;
3936 endc->row = 0;
3937 endc->col = 0;
3938
3939 for (col = 0; indexed[col] < len; ++col) {
3940 if (regexec(&preg,
3941 search + indexed[col],
3942 (size_t) 1, &match, 0) == 0) {
3943 int start_inx = (int) (match.rm_so + indexed[col]);
3944 int finis_inx = (int) (match.rm_eo + indexed[col]);
3945 int start_col = indexToCol(indexed, len, start_inx);
3946 int finis_col = indexToCol(indexed, len, finis_inx);
3947
3948 if (start_col <= actual &&
3949 actual <= finis_col) {
3950 int test = finis_col - start_col;
3951 if (best_len < test) {
3952 best_len = test;
3953 best_col = start_col;
3954 TRACE(("match column %d len %d\n",
3955 best_col,
3956 best_len));
3957 }
3958 }
3959 }
3960 }
3961 if (best_col >= 0) {
3962 int best_nxt = best_col + best_len;
3963 columnToCell(screen, firstRow, best_col, startc);
3964 columnToCell(screen, firstRow, best_nxt, endc);
3965 TRACE(("search::%s\n", search));
3966 TRACE(("indexed:%d..%d -> %d..%d\n",
3967 best_col, best_nxt,
3968 indexed[best_col],
3969 indexed[best_nxt]));
3970 TRACE(("matched:%d:%s\n",
3971 indexed[best_nxt] + 1 -
3972 indexed[best_col],
3973 visibleChars((Char *) (search + indexed[best_col]),
3974 (unsigned) (indexed[best_nxt] +
3975 1 -
3976 indexed[best_col]))));
3977 }
3978 free(search);
3979 }
3980 free(indexed);
3981 #if OPT_DEC_CHRSET
3982 if ((ld = GET_LINEDATA(screen, startc->row)) != 0) {
3983 if (CSET_DOUBLE(GetLineDblCS(ld)))
3984 startc->col *= 2;
3985 }
3986 if ((ld = GET_LINEDATA(screen, endc->row)) != 0) {
3987 if (CSET_DOUBLE(GetLineDblCS(ld)))
3988 endc->col *= 2;
3989 }
3990 #endif
3991 }
3992 regfree(&preg);
3993 }
3994 }
3995 }
3996 #endif /* OPT_SELECT_REGEX */
3997
3998 #define InitRow(name) \
3999 ld.name = GET_LINEDATA(screen, screen->name.row)
4000
4001 #define NextRow(name) \
4002 ld.name = GET_LINEDATA(screen, ++screen->name.row)
4003
4004 #define PrevRow(name) \
4005 ld.name = GET_LINEDATA(screen, --screen->name.row)
4006
4007 #define MoreRows(name) \
4008 (screen->name.row < screen->max_row)
4009
4010 #define isPrevWrapped(name) \
4011 (screen->name.row > 0 \
4012 && (ltmp = GET_LINEDATA(screen, screen->name.row - 1)) != 0 \
4013 && LineTstWrapped(ltmp))
4014
4015 /*
4016 * sets startSel endSel
4017 * ensuring that they have legal values
4018 */
4019 static void
4020 ComputeSelect(XtermWidget xw,
4021 CELL *startc,
4022 CELL *endc,
4023 Bool extend,
4024 Bool normal)
4025 {
4026 TScreen *screen = TScreenOf(xw);
4027
4028 int cclass;
4029 CELL first = *startc;
4030 CELL last = *endc;
4031 Boolean ignored = False;
4032
4033 struct {
4034 LineData *startSel;
4035 LineData *endSel;
4036 } ld;
4037 LineData *ltmp;
4038
4039 TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n",
4040 first.row, first.col,
4041 last.row, last.col,
4042 extend ? "" : "no"));
4043
4044 #if OPT_WIDE_CHARS
4045 if (first.col > 1
4046 && isWideCell(first.row, first.col - 1)
4047 && XTERM_CELL(first.row, first.col - 0) == HIDDEN_CHAR) {
4048 TRACE(("Adjusting start. Changing downwards from %i.\n", first.col));
4049 first.col -= 1;
4050 if (last.col == (first.col + 1))
4051 last.col--;
4052 }
4053
4054 if (last.col > 1
4055 && isWideCell(last.row, last.col - 1)
4056 && XTERM_CELL(last.row, last.col) == HIDDEN_CHAR) {
4057 last.col += 1;
4058 }
4059 #endif
4060
4061 if (Coordinate(screen, &first) <= Coordinate(screen, &last)) {
4062 screen->startSel = screen->startRaw = first;
4063 screen->endSel = screen->endRaw = last;
4064 } else { /* Swap them */
4065 screen->startSel = screen->startRaw = last;
4066 screen->endSel = screen->endRaw = first;
4067 }
4068
4069 InitRow(startSel);
4070 InitRow(endSel);
4071
4072 switch (screen->selectUnit) {
4073 case Select_CHAR:
4074 (void) okPosition(screen, &(ld.startSel), &(screen->startSel));
4075 (void) okPosition(screen, &(ld.endSel), &(screen->endSel));
4076 break;
4077
4078 case Select_WORD:
4079 TRACE(("Select_WORD\n"));
4080 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4081 CELL mark;
4082 cclass = CClassOf(startSel);
4083 TRACE(("...starting with class %d\n", cclass));
4084 do {
4085 mark = screen->startSel;
4086 --screen->startSel.col;
4087 if (screen->startSel.col < 0
4088 && isPrevWrapped(startSel)) {
4089 PrevRow(startSel);
4090 screen->startSel.col = LastTextCol(screen, ld.startSel, screen->startSel.row);
4091 }
4092 } while (screen->startSel.col >= 0
4093 && CClassSelects(startSel, cclass));
4094 if (normal)
4095 ++screen->startSel.col;
4096 else
4097 screen->startSel = mark;
4098 }
4099 #if OPT_WIDE_CHARS
4100 #define SkipHiddenCell(mark) \
4101 if (mark.col && XTERM_CELL(mark.row, mark.col) == HIDDEN_CHAR) \
4102 mark.col++
4103 #else
4104 #define SkipHiddenCell(mark) /* nothing */
4105 #endif
4106 SkipHiddenCell(screen->startSel);
4107
4108 if (!normal) {
4109 screen->endSel = screen->startSel;
4110 ld.endSel = ld.startSel;
4111 }
4112
4113 if (okPosition(screen, &(ld.endSel), &(screen->endSel))) {
4114 int length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4115 cclass = CClassOf(endSel);
4116 TRACE(("...ending with class %d\n", cclass));
4117 do {
4118 ++screen->endSel.col;
4119 if (screen->endSel.col > length
4120 && LineTstWrapped(ld.endSel)) {
4121 if (!MoreRows(endSel))
4122 break;
4123 screen->endSel.col = 0;
4124 NextRow(endSel);
4125 length = LastTextCol(screen, ld.endSel, screen->endSel.row);
4126 }
4127 } while (screen->endSel.col <= length
4128 && CClassSelects(endSel, cclass));
4129 if (normal
4130 && screen->endSel.col > length + 1
4131 && MoreRows(endSel)) {
4132 screen->endSel.col = 0;
4133 NextRow(endSel);
4134 }
4135 }
4136 SkipHiddenCell(screen->endSel);
4137
4138 screen->saveStartW = screen->startSel;
4139 break;
4140
4141 case Select_LINE:
4142 TRACE(("Select_LINE\n"));
4143 while (LineTstWrapped(ld.endSel)
4144 && MoreRows(endSel)) {
4145 NextRow(endSel);
4146 }
4147 if (screen->cutToBeginningOfLine
4148 || screen->startSel.row < screen->saveStartW.row) {
4149 screen->startSel.col = 0;
4150 while (isPrevWrapped(startSel)) {
4151 PrevRow(startSel);
4152 }
4153 } else if (!extend) {
4154 if ((first.row < screen->saveStartW.row)
4155 || (isSameRow(&first, &(screen->saveStartW))
4156 && first.col < screen->saveStartW.col)) {
4157 screen->startSel.col = 0;
4158 while (isPrevWrapped(startSel)) {
4159 PrevRow(startSel);
4160 }
4161 } else {
4162 screen->startSel = screen->saveStartW;
4163 }
4164 }
4165 trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4166 break;
4167
4168 case Select_GROUP: /* paragraph */
4169 TRACE(("Select_GROUP\n"));
4170 if (okPosition(screen, &(ld.startSel), &(screen->startSel))) {
4171 /* scan backward for beginning of group */
4172 while (screen->startSel.row > 0 &&
4173 (LastTextCol(screen, ld.startSel, screen->startSel.row -
4174 1) > 0 ||
4175 isPrevWrapped(startSel))) {
4176 PrevRow(startSel);
4177 }
4178 screen->startSel.col = 0;
4179 /* scan forward for end of group */
4180 while (MoreRows(endSel) &&
4181 (LastTextCol(screen, ld.endSel, screen->endSel.row + 1) >
4182 0 ||
4183 LineTstWrapped(ld.endSel))) {
4184 NextRow(endSel);
4185 }
4186 trimLastLine(screen, &(ld.endSel), &(screen->endSel));
4187 }
4188 break;
4189
4190 case Select_PAGE: /* everything one can see */
4191 TRACE(("Select_PAGE\n"));
4192 screen->startSel.row = 0;
4193 screen->startSel.col = 0;
4194 screen->endSel.row = MaxRows(screen);
4195 screen->endSel.col = 0;
4196 break;
4197
4198 case Select_ALL: /* counts scrollback if in normal screen */
4199 TRACE(("Select_ALL\n"));
4200 screen->startSel.row = -screen->savedlines;
4201 screen->startSel.col = 0;
4202 screen->endSel.row = MaxRows(screen);
4203 screen->endSel.col = 0;
4204 break;
4205
4206 #if OPT_SELECT_REGEX
4207 case Select_REGEX:
4208 do_select_regex(screen, &(screen->startSel), &(screen->endSel));
4209 break;
4210 #endif
4211
4212 case NSELECTUNITS: /* always ignore */
4213 ignored = True;
4214 break;
4215 }
4216
4217 if (!ignored) {
4218 /* check boundaries */
4219 ScrollSelection(screen, 0, False);
4220 TrackText(xw, &(screen->startSel), &(screen->endSel));
4221 }
4222
4223 return;
4224 }
4225
4226 /* Guaranteed (first.row, first.col) <= (last.row, last.col) */
4227 static void
4228 TrackText(XtermWidget xw,
4229 const CELL *firstp,
4230 const CELL *lastp)
4231 {
4232 TScreen *screen = TScreenOf(xw);
4233 int from, to;
4234 CELL old_start, old_end;
4235 CELL first = *firstp;
4236 CELL last = *lastp;
4237
4238 TRACE(("TrackText(first=%d,%d, last=%d,%d)\n",
4239 first.row, first.col, last.row, last.col));
4240
4241 old_start = screen->startH;
4242 old_end = screen->endH;
4243 TRACE(("...previous(first=%d,%d, last=%d,%d)\n",
4244 old_start.row, old_start.col,
4245 old_end.row, old_end.col));
4246 if (isSameCELL(&first, &old_start) &&
4247 isSameCELL(&last, &old_end)) {
4248 return;
4249 }
4250
4251 screen->startH = first;
4252 screen->endH = last;
4253 from = Coordinate(screen, &screen->startH);
4254 to = Coordinate(screen, &screen->endH);
4255 if (to <= screen->startHCoord || from > screen->endHCoord) {
4256 /* No overlap whatsoever between old and new hilite */
4257 ReHiliteText(xw, &old_start, &old_end);
4258 ReHiliteText(xw, &first, &last);
4259 } else {
4260 if (from < screen->startHCoord) {
4261 /* Extend left end */
4262 ReHiliteText(xw, &first, &old_start);
4263 } else if (from > screen->startHCoord) {
4264 /* Shorten left end */
4265 ReHiliteText(xw, &old_start, &first);
4266 }
4267 if (to > screen->endHCoord) {
4268 /* Extend right end */
4269 ReHiliteText(xw, &old_end, &last);
4270 } else if (to < screen->endHCoord) {
4271 /* Shorten right end */
4272 ReHiliteText(xw, &last, &old_end);
4273 }
4274 }
4275 screen->startHCoord = from;
4276 screen->endHCoord = to;
4277 }
4278
4279 static void
4280 UnHiliteText(XtermWidget xw)
4281 {
4282 TrackText(xw, &zeroCELL, &zeroCELL);
4283 }
4284
4285 /* Guaranteed that (first->row, first->col) <= (last->row, last->col) */
4286 static void
4287 ReHiliteText(XtermWidget xw,
4288 CELL *firstp,
4289 CELL *lastp)
4290 {
4291 TScreen *screen = TScreenOf(xw);
4292 CELL first = *firstp;
4293 CELL last = *lastp;
4294
4295 TRACE(("ReHiliteText from %d.%d to %d.%d\n",
4296 first.row, first.col, last.row, last.col));
4297
4298 if (first.row < 0)
4299 first.row = first.col = 0;
4300 else if (first.row > screen->max_row)
4301 return; /* nothing to do, since last.row >= first.row */
4302
4303 if (last.row < 0)
4304 return; /* nothing to do, since first.row <= last.row */
4305 else if (last.row > screen->max_row) {
4306 last.row = screen->max_row;
4307 last.col = MaxCols(screen);
4308 }
4309 if (isSameCELL(&first, &last))
4310 return;
4311
4312 if (!isSameRow(&first, &last)) { /* do multiple rows */
4313 int i;
4314 if ((i = screen->max_col - first.col + 1) > 0) { /* first row */
4315 ScrnRefresh(xw, first.row, first.col, 1, i, True);
4316 }
4317 if ((i = last.row - first.row - 1) > 0) { /* middle rows */
4318 ScrnRefresh(xw, first.row + 1, 0, i, MaxCols(screen), True);
4319 }
4320 if (last.col > 0 && last.row <= screen->max_row) { /* last row */
4321 ScrnRefresh(xw, last.row, 0, 1, last.col, True);
4322 }
4323 } else { /* do single row */
4324 ScrnRefresh(xw, first.row, first.col, 1, last.col - first.col, True);
4325 }
4326 }
4327
4328 /*
4329 * Guaranteed that (cellc->row, cellc->col) <= (cell->row, cell->col),
4330 * and that both points are valid
4331 * (may have cell->row = screen->max_row+1, cell->col = 0).
4332 */
4333 static void
4334 SaltTextAway(XtermWidget xw,
4335 int which,
4336 CELL *cellc,
4337 CELL *cell)
4338 {
4339 TScreen *screen = TScreenOf(xw);
4340 SelectedCells *scp;
4341 int i;
4342 int eol;
4343 int need = 0;
4344 size_t have = 0;
4345 Char *line;
4346 Char *lp;
4347 CELL first = *cellc;
4348 CELL last = *cell;
4349
4350 if (which < 0 || which >= MAX_SELECTIONS) {
4351 TRACE(("SaltTextAway - which selection?\n"));
4352 return;
4353 }
4354 scp = &(screen->selected_cells[which]);
4355
4356 TRACE(("SaltTextAway which=%d, first=%d,%d, last=%d,%d\n",
4357 which, first.row, first.col, last.row, last.col));
4358
4359 if (isSameRow(&first, &last) && first.col > last.col) {
4360 int tmp;
4361 EXCHANGE(first.col, last.col, tmp);
4362 }
4363
4364 --last.col;
4365 /* first we need to know how long the string is before we can save it */
4366
4367 if (isSameRow(&last, &first)) {
4368 need = Length(screen, first.row, first.col, last.col);
4369 } else { /* two cases, cut is on same line, cut spans multiple lines */
4370 need += Length(screen, first.row, first.col, screen->max_col) + 1;
4371 for (i = first.row + 1; i < last.row; i++)
4372 need += Length(screen, i, 0, screen->max_col) + 1;
4373 if (last.col >= 0)
4374 need += Length(screen, last.row, 0, last.col);
4375 }
4376
4377 /* UTF-8 may require more space */
4378 if_OPT_WIDE_CHARS(screen, {
4379 if (need > 0) {
4380 if (screen->max_combining > 0)
4381 need += screen->max_combining;
4382 need *= 6;
4383 }
4384 });
4385
4386 /* now get some memory to save it in */
4387 if (need < 0)
4388 return;
4389
4390 if (scp->data_limit <= (unsigned) need) {
4391 if ((line = (Char *) malloc((size_t) need + 1)) == 0)
4392 SysError(ERROR_BMALLOC2);
4393 free(scp->data_buffer);
4394 scp->data_buffer = line;
4395 scp->data_limit = (size_t) (need + 1);
4396 } else {
4397 line = scp->data_buffer;
4398 }
4399
4400 if (line == 0)
4401 return;
4402
4403 line[need] = '\0'; /* make sure it is null terminated */
4404 lp = line; /* lp points to where to save the text */
4405 if (isSameRow(&last, &first)) {
4406 lp = SaveText(screen, last.row, first.col, last.col, lp, &eol);
4407 } else {
4408 lp = SaveText(screen, first.row, first.col, screen->max_col, lp, &eol);
4409 if (eol)
4410 *lp++ = '\n'; /* put in newline at end of line */
4411 for (i = first.row + 1; i < last.row; i++) {
4412 lp = SaveText(screen, i, 0, screen->max_col, lp, &eol);
4413 if (eol)
4414 *lp++ = '\n';
4415 }
4416 if (last.col >= 0)
4417 lp = SaveText(screen, last.row, 0, last.col, lp, &eol);
4418 }
4419 *lp = '\0'; /* make sure we have end marked */
4420
4421 have = (size_t) (lp - line);
4422 /*
4423 * Scanning the buffer twice is unnecessary. Discard unwanted memory if
4424 * the estimate is too-far off.
4425 */
4426 if ((have * 2) < (size_t) need) {
4427 Char *next;
4428 scp->data_limit = have + 1;
4429 next = realloc(line, scp->data_limit);
4430 if (next == NULL) {
4431 free(line);
4432 scp->data_length = 0;
4433 scp->data_limit = 0;
4434 }
4435 scp->data_buffer = next;
4436 }
4437 scp->data_length = have;
4438
4439 TRACE(("Salted TEXT:%u:%s\n", (unsigned) have,
4440 visibleChars(scp->data_buffer, (unsigned) have)));
4441 }
4442
4443 #if OPT_PASTE64
4444 void
4445 ClearSelectionBuffer(TScreen *screen, String selection)
4446 {
4447 int which = TargetToSelection(screen, selection);
4448 SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4449 FreeAndNull(scp->data_buffer);
4450 scp->data_limit = 0;
4451 scp->data_length = 0;
4452 screen->base64_count = 0;
4453 }
4454
4455 static void
4456 AppendStrToSelectionBuffer(SelectedCells * scp, Char *text, size_t len)
4457 {
4458 if (len != 0) {
4459 size_t j = (scp->data_length + len);
4460 size_t k = j + (j >> 2) + 80;
4461 if (j + 1 >= scp->data_limit) {
4462 Char *line;
4463 if (!scp->data_length) {
4464 line = (Char *) malloc(k);
4465 } else {
4466 line = (Char *) realloc(scp->data_buffer, k);
4467 }
4468 if (line == 0)
4469 SysError(ERROR_BMALLOC2);
4470 scp->data_buffer = line;
4471 scp->data_limit = k;
4472 }
4473 if (scp->data_buffer != 0) {
4474 memcpy(scp->data_buffer + scp->data_length, text, len);
4475 scp->data_length += len;
4476 scp->data_buffer[scp->data_length] = 0;
4477 }
4478 }
4479 }
4480
4481 void
4482 AppendToSelectionBuffer(TScreen *screen, unsigned c, String selection)
4483 {
4484 int which = TargetToSelection(screen, selection);
4485 SelectedCells *scp = &(screen->selected_cells[okSelectionCode(which)]);
4486 unsigned six;
4487 Char ch;
4488
4489 /* Decode base64 character */
4490 if (c >= 'A' && c <= 'Z')
4491 six = c - 'A';
4492 else if (c >= 'a' && c <= 'z')
4493 six = c - 'a' + 26;
4494 else if (c >= '0' && c <= '9')
4495 six = c - '0' + 52;
4496 else if (c == '+')
4497 six = 62;
4498 else if (c == '/')
4499 six = 63;
4500 else
4501 return;
4502
4503 /* Accumulate bytes */
4504 switch (screen->base64_count) {
4505 case 0:
4506 screen->base64_accu = six;
4507 screen->base64_count = 6;
4508 break;
4509
4510 case 2:
4511 ch = CharOf((screen->base64_accu << 6) + six);
4512 screen->base64_count = 0;
4513 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4514 break;
4515
4516 case 4:
4517 ch = CharOf((screen->base64_accu << 4) + (six >> 2));
4518 screen->base64_accu = (six & 0x3);
4519 screen->base64_count = 2;
4520 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4521 break;
4522
4523 case 6:
4524 ch = CharOf((screen->base64_accu << 2) + (six >> 4));
4525 screen->base64_accu = (six & 0xF);
4526 screen->base64_count = 4;
4527 AppendStrToSelectionBuffer(scp, &ch, (size_t) 1);
4528 break;
4529 }
4530 }
4531
4532 void
4533 CompleteSelection(XtermWidget xw, String *args, Cardinal len)
4534 {
4535 TScreen *screen = TScreenOf(xw);
4536
4537 screen->base64_count = 0;
4538 screen->base64_accu = 0;
4539 _OwnSelection(xw, args, len);
4540 }
4541 #endif /* OPT_PASTE64 */
4542
4543 static Bool
4544 _ConvertSelectionHelper(Widget w,
4545 SelectedCells * scp,
4546 Atom *type,
4547 XtPointer *value,
4548 unsigned long *length,
4549 int *format,
4550 int (*conversion_function) (Display *,
4551 char **, int,
4552 XICCEncodingStyle,
4553 XTextProperty *),
4554 XICCEncodingStyle conversion_style)
4555 {
4556 *value = 0;
4557 *length = 0;
4558 *type = 0;
4559 *format = 0;
4560
4561 if (getXtermWidget(w) != 0) {
4562 Display *dpy = XtDisplay(w);
4563 XTextProperty textprop;
4564 int out_n = 0;
4565 char *result = 0;
4566 char *the_data = (char *) scp->data_buffer;
4567 char *the_next;
4568 unsigned long remaining = scp->data_length;
4569
4570 TRACE(("converting %ld:'%s'\n",
4571 (long) scp->data_length,
4572 visibleChars(scp->data_buffer, (unsigned) scp->data_length)));
4573 /*
4574 * For most selections, we can convert in one pass. It is possible
4575 * that some applications contain embedded nulls, e.g., using xterm's
4576 * paste64 feature. For those cases, we will build up the result in
4577 * parts.
4578 */
4579 if (memchr(the_data, 0, scp->data_length) != 0) {
4580 TRACE(("selection contains embedded nulls\n"));
4581 result = calloc(scp->data_length + 1, sizeof(char));
4582 }
4583
4584 next_try:
4585 memset(&textprop, 0, sizeof(textprop));
4586 if (conversion_function(dpy, &the_data, 1,
4587 conversion_style,
4588 &textprop) >= Success) {
4589 if ((result != 0)
4590 && (textprop.value != 0)
4591 && (textprop.format == 8)) {
4592 char *text_values = (char *) textprop.value;
4593 unsigned long in_n;
4594
4595 if (out_n == 0) {
4596 *value = result;
4597 *type = textprop.encoding;
4598 *format = textprop.format;
4599 }
4600 for (in_n = 0; in_n < textprop.nitems; ++in_n) {
4601 result[out_n++] = text_values[in_n];
4602 }
4603 *length += textprop.nitems;
4604 if ((the_next = memchr(the_data, 0, remaining)) != 0) {
4605 unsigned long this_was = (unsigned long) (the_next - the_data);
4606 this_was++;
4607 the_data += this_was;
4608 remaining -= this_was;
4609 result[out_n++] = 0;
4610 *length += 1;
4611 if (remaining)
4612 goto next_try;
4613 }
4614 return True;
4615 } else {
4616 free(result);
4617 *value = (XtPointer) textprop.value;
4618 *length = textprop.nitems;
4619 *type = textprop.encoding;
4620 *format = textprop.format;
4621 return True;
4622 }
4623 }
4624 free(result);
4625 }
4626 return False;
4627 }
4628
4629 static Boolean
4630 SaveConvertedLength(XtPointer *target, unsigned long source)
4631 {
4632 Boolean result = False;
4633
4634 *target = XtMalloc(4);
4635 if (*target != 0) {
4636 result = True;
4637 if (sizeof(unsigned long) == 4) {
4638 *(unsigned long *) *target = source;
4639 } else if (sizeof(unsigned) == 4) {
4640 *(unsigned *) *target = (unsigned) source;
4641 } else if (sizeof(unsigned short) == 4) {
4642 *(unsigned short *) *target = (unsigned short) source;
4643 } else {
4644 /* FIXME - does this depend on byte-order? */
4645 unsigned long temp = source;
4646 memcpy((char *) *target,
4647 ((char *) &temp) + sizeof(temp) - 4,
4648 (size_t) 4);
4649 }
4650 }
4651 return result;
4652 }
4653
4654 #define keepClipboard(d,atom) ((screen->keepClipboard) && \
4655 (atom == XA_CLIPBOARD(d)))
4656
4657 static Boolean
4658 ConvertSelection(Widget w,
4659 Atom *selection,
4660 Atom *target,
4661 Atom *type,
4662 XtPointer *value,
4663 unsigned long *length,
4664 int *format)
4665 {
4666 Display *dpy = XtDisplay(w);
4667 TScreen *screen;
4668 SelectedCells *scp;
4669 Bool result = False;
4670
4671 Char *data;
4672 unsigned long data_length;
4673
4674 XtermWidget xw;
4675
4676 if ((xw = getXtermWidget(w)) == 0)
4677 return False;
4678
4679 screen = TScreenOf(xw);
4680
4681 TRACE(("ConvertSelection %s -> %s\n",
4682 TraceAtomName(screen->display, *selection),
4683 visibleSelectionTarget(dpy, *target)));
4684
4685 if (keepClipboard(dpy, *selection)) {
4686 TRACE(("asked for clipboard\n"));
4687 scp = &(screen->clipboard_data);
4688 } else {
4689 TRACE(("asked for selection\n"));
4690 scp = &(screen->selected_cells[AtomToSelection(dpy, *selection)]);
4691 }
4692
4693 data = scp->data_buffer;
4694 data_length = scp->data_length;
4695 if (data == NULL) {
4696 TRACE(("...no selection-data\n"));
4697 return False;
4698 }
4699
4700 if (*target == XA_TARGETS(dpy)) {
4701 Atom *targetP;
4702 XPointer std_return = 0;
4703 unsigned long std_length;
4704
4705 if (XmuConvertStandardSelection(w, screen->selection_time, selection,
4706 target, type, &std_return,
4707 &std_length, format)) {
4708 Atom *my_targets = _SelectionTargets(w);
4709 Atom *allocP;
4710 Atom *std_targets;
4711
4712 TRACE(("XmuConvertStandardSelection - success\n"));
4713 std_targets = (Atom *) (void *) (std_return);
4714 *length = std_length + 6;
4715
4716 targetP = TypeXtMallocN(Atom, *length);
4717 allocP = targetP;
4718
4719 *value = (XtPointer) targetP;
4720
4721 if (my_targets != 0) {
4722 while (*my_targets != None) {
4723 *targetP++ = *my_targets++;
4724 }
4725 }
4726 *targetP++ = XA_LENGTH(dpy);
4727 *targetP++ = XA_LIST_LENGTH(dpy);
4728
4729 *length = std_length + (unsigned long) (targetP - allocP);
4730
4731 memcpy(targetP, std_targets, sizeof(Atom) * std_length);
4732 XtFree((char *) std_targets);
4733 *type = XA_ATOM;
4734 *format = 32;
4735 result = True;
4736 } else {
4737 TRACE(("XmuConvertStandardSelection - failed\n"));
4738 }
4739 }
4740 #if OPT_WIDE_CHARS
4741 else if (screen->wide_chars && *target == XA_STRING) {
4742 result =
4743 _ConvertSelectionHelper(w, scp,
4744 type, value, length, format,
4745 Xutf8TextListToTextProperty,
4746 XStringStyle);
4747 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4748 } else if (screen->wide_chars && *target == XA_UTF8_STRING(dpy)) {
4749 result =
4750 _ConvertSelectionHelper(w, scp,
4751 type, value, length, format,
4752 Xutf8TextListToTextProperty,
4753 XUTF8StringStyle);
4754 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4755 } else if (screen->wide_chars && *target == XA_TEXT(dpy)) {
4756 result =
4757 _ConvertSelectionHelper(w, scp,
4758 type, value, length, format,
4759 Xutf8TextListToTextProperty,
4760 XStdICCTextStyle);
4761 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4762 } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(dpy)) {
4763 result =
4764 _ConvertSelectionHelper(w, scp,
4765 type, value, length, format,
4766 Xutf8TextListToTextProperty,
4767 XCompoundTextStyle);
4768 TRACE(("...Xutf8TextListToTextProperty:%d\n", result));
4769 }
4770 #endif
4771
4772 else if (*target == XA_STRING) { /* not wide_chars */
4773 /* We can only reach this point if the selection requestor
4774 requested STRING before any of TEXT, COMPOUND_TEXT or
4775 UTF8_STRING. We therefore assume that the requestor is not
4776 properly internationalised, and dump raw eight-bit data
4777 with no conversion into the selection. Yes, this breaks
4778 the ICCCM in non-Latin-1 locales. */
4779 *type = XA_STRING;
4780 *value = (XtPointer) data;
4781 *length = data_length;
4782 *format = 8;
4783 result = True;
4784 TRACE(("...raw 8-bit data:%d\n", result));
4785 } else if (*target == XA_TEXT(dpy)) { /* not wide_chars */
4786 result =
4787 _ConvertSelectionHelper(w, scp,
4788 type, value, length, format,
4789 XmbTextListToTextProperty,
4790 XStdICCTextStyle);
4791 TRACE(("...XmbTextListToTextProperty(StdICC):%d\n", result));
4792 } else if (*target == XA_COMPOUND_TEXT(dpy)) { /* not wide_chars */
4793 result =
4794 _ConvertSelectionHelper(w, scp,
4795 type, value, length, format,
4796 XmbTextListToTextProperty,
4797 XCompoundTextStyle);
4798 TRACE(("...XmbTextListToTextProperty(Compound):%d\n", result));
4799 }
4800 #ifdef X_HAVE_UTF8_STRING
4801 else if (*target == XA_UTF8_STRING(dpy)) { /* not wide_chars */
4802 result =
4803 _ConvertSelectionHelper(w, scp,
4804 type, value, length, format,
4805 XmbTextListToTextProperty,
4806 XUTF8StringStyle);
4807 TRACE(("...XmbTextListToTextProperty(UTF8):%d\n", result));
4808 }
4809 #endif
4810 else if (*target == XA_LIST_LENGTH(dpy)) {
4811 result = SaveConvertedLength(value, (unsigned long) 1);
4812 *type = XA_INTEGER;
4813 *length = 1;
4814 *format = 32;
4815 TRACE(("...list of values:%d\n", result));
4816 } else if (*target == XA_LENGTH(dpy)) {
4817 /* This value is wrong if we have UTF-8 text */
4818 result = SaveConvertedLength(value, scp->data_length);
4819 *type = XA_INTEGER;
4820 *length = 1;
4821 *format = 32;
4822 TRACE(("...list of values:%d\n", result));
4823 } else if (XmuConvertStandardSelection(w,
4824 screen->selection_time, selection,
4825 target, type, (XPointer *) value,
4826 length, format)) {
4827 result = True;
4828 TRACE(("...XmuConvertStandardSelection:%d\n", result));
4829 }
4830
4831 /* else */
4832 return (Boolean) result;
4833 }
4834
4835 static void
4836 LoseSelection(Widget w, Atom *selection)
4837 {
4838 TScreen *screen;
4839 Atom *atomP;
4840 Cardinal i;
4841
4842 XtermWidget xw;
4843
4844 if ((xw = getXtermWidget(w)) == 0)
4845 return;
4846
4847 screen = TScreenOf(xw);
4848 TRACE(("LoseSelection %s\n", TraceAtomName(screen->display, *selection)));
4849
4850 for (i = 0, atomP = screen->selection_atoms;
4851 i < screen->selection_count; i++, atomP++) {
4852 if (*selection == *atomP)
4853 *atomP = (Atom) 0;
4854 if (CutBuffer(*atomP) >= 0) {
4855 *atomP = (Atom) 0;
4856 }
4857 }
4858
4859 for (i = screen->selection_count; i; i--) {
4860 if (screen->selection_atoms[i - 1] != 0)
4861 break;
4862 }
4863 screen->selection_count = i;
4864
4865 for (i = 0, atomP = screen->selection_atoms;
4866 i < screen->selection_count; i++, atomP++) {
4867 if (*atomP == (Atom) 0) {
4868 *atomP = screen->selection_atoms[--screen->selection_count];
4869 }
4870 }
4871
4872 if (screen->selection_count == 0)
4873 UnHiliteText(xw);
4874 }
4875
4876 /* ARGSUSED */
4877 static void
4878 SelectionDone(Widget w GCC_UNUSED,
4879 Atom *selection GCC_UNUSED,
4880 Atom *target GCC_UNUSED)
4881 {
4882 /* empty proc so Intrinsics know we want to keep storage */
4883 TRACE(("SelectionDone\n"));
4884 }
4885
4886 static void
4887 _OwnSelection(XtermWidget xw,
4888 String *selections,
4889 Cardinal count)
4890 {
4891 TScreen *screen = TScreenOf(xw);
4892 Display *dpy = screen->display;
4893 Atom *atoms = screen->selection_atoms;
4894 Cardinal i;
4895 Bool have_selection = False;
4896 SelectedCells *scp;
4897
4898 if (count == 0)
4899 return;
4900
4901 TRACE(("_OwnSelection count %d\n", count));
4902 selections = MapSelections(xw, selections, count);
4903
4904 if (count > screen->sel_atoms_size) {
4905 XtFree((char *) atoms);
4906 atoms = TypeXtMallocN(Atom, count);
4907 screen->selection_atoms = atoms;
4908 screen->sel_atoms_size = count;
4909 }
4910 XmuInternStrings(dpy, selections, count, atoms);
4911 for (i = 0; i < count; i++) {
4912 int cutbuffer = CutBuffer(atoms[i]);
4913 if (cutbuffer >= 0) {
4914 unsigned long limit =
4915 (unsigned long) (4 * XMaxRequestSize(dpy) - 32);
4916 scp = &(screen->selected_cells[CutBufferToCode(cutbuffer)]);
4917 if (scp->data_length > limit) {
4918 TRACE(("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4919 (unsigned long) scp->data_length, cutbuffer));
4920 xtermWarning("selection too big (%lu bytes), not storing in CUT_BUFFER%d\n",
4921 (unsigned long) scp->data_length, cutbuffer);
4922 } else {
4923 /* This used to just use the UTF-8 data, which was totally
4924 * broken as not even the corresponding paste code in xterm
4925 * understood this! So now it converts to Latin1 first.
4926 * Robert Brady, 2000-09-05
4927 */
4928 unsigned long length = scp->data_length;
4929 Char *data = scp->data_buffer;
4930 if_OPT_WIDE_CHARS((screen), {
4931 data = UTF8toLatin1(screen, data, length, &length);
4932 });
4933 TRACE(("XStoreBuffer(%d)\n", cutbuffer));
4934 XStoreBuffer(dpy,
4935 (char *) data,
4936 (int) length,
4937 cutbuffer);
4938 }
4939 } else {
4940 int which = AtomToSelection(dpy, atoms[i]);
4941 if (keepClipboard(dpy, atoms[i])) {
4942 Char *buf;
4943 SelectedCells *tcp = &(screen->clipboard_data);
4944 TRACE(("saving selection to clipboard buffer\n"));
4945 scp = &(screen->selected_cells[CLIPBOARD_CODE]);
4946 if ((buf = (Char *) malloc((size_t) scp->data_length)) == 0)
4947 SysError(ERROR_BMALLOC2);
4948
4949 free(tcp->data_buffer);
4950 memcpy(buf, scp->data_buffer, scp->data_length);
4951 tcp->data_buffer = buf;
4952 tcp->data_limit = scp->data_length;
4953 tcp->data_length = scp->data_length;
4954 }
4955 scp = &(screen->selected_cells[which]);
4956 if (scp->data_length == 0) {
4957 TRACE(("XtDisownSelection(%s, @%ld)\n",
4958 TraceAtomName(screen->display, atoms[i]),
4959 (long) screen->selection_time));
4960 XtDisownSelection((Widget) xw,
4961 atoms[i],
4962 screen->selection_time);
4963 } else if (!screen->replyToEmacs && atoms[i] != 0) {
4964 TRACE(("XtOwnSelection(%s, @%ld)\n",
4965 TraceAtomName(screen->display, atoms[i]),
4966 (long) screen->selection_time));
4967 have_selection |=
4968 XtOwnSelection((Widget) xw, atoms[i],
4969 screen->selection_time,
4970 ConvertSelection,
4971 LoseSelection,
4972 SelectionDone);
4973 }
4974 }
4975 TRACE(("... _OwnSelection used length %lu value %s\n",
4976 (unsigned long) scp->data_length,
4977 visibleChars(scp->data_buffer,
4978 (unsigned) scp->data_length)));
4979 }
4980 if (!screen->replyToEmacs)
4981 screen->selection_count = count;
4982 if (!have_selection)
4983 UnHiliteText(xw);
4984 }
4985
4986 static void
4987 ResetSelectionState(TScreen *screen)
4988 {
4989 screen->selection_count = 0;
4990 screen->startH = zeroCELL;
4991 screen->endH = zeroCELL;
4992 }
4993
4994 void
4995 DisownSelection(XtermWidget xw)
4996 {
4997 TScreen *screen = TScreenOf(xw);
4998 Atom *atoms = screen->selection_atoms;
4999 Cardinal count = screen->selection_count;
5000 Cardinal i;
5001
5002 TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n",
5003 count,
5004 screen->startH.row,
5005 screen->startH.col,
5006 screen->endH.row,
5007 screen->endH.col));
5008
5009 for (i = 0; i < count; i++) {
5010 int cutbuffer = CutBuffer(atoms[i]);
5011 if (cutbuffer < 0) {
5012 XtDisownSelection((Widget) xw, atoms[i],
5013 screen->selection_time);
5014 }
5015 }
5016 /*
5017 * If none of the callbacks via XtDisownSelection() reset highlighting
5018 * do it now.
5019 */
5020 if (ScrnHaveSelection(screen)) {
5021 /* save data which will be reset */
5022 CELL first = screen->startH;
5023 CELL last = screen->endH;
5024
5025 ResetSelectionState(screen);
5026 ReHiliteText(xw, &first, &last);
5027 } else {
5028 ResetSelectionState(screen);
5029 }
5030 }
5031
5032 void
5033 UnhiliteSelection(XtermWidget xw)
5034 {
5035 TScreen *screen = TScreenOf(xw);
5036
5037 if (ScrnHaveSelection(screen)) {
5038 CELL first = screen->startH;
5039 CELL last = screen->endH;
5040
5041 screen->startH = zeroCELL;
5042 screen->endH = zeroCELL;
5043 ReHiliteText(xw, &first, &last);
5044 }
5045 }
5046
5047 /* returns number of chars in line from scol to ecol out */
5048 /* ARGSUSED */
5049 static int
5050 Length(TScreen *screen,
5051 int row,
5052 int scol,
5053 int ecol)
5054 {
5055 CLineData *ld = GET_LINEDATA(screen, row);
5056 const int lastcol = LastTextCol(screen, ld, row);
5057
5058 if (ecol > lastcol)
5059 ecol = lastcol;
5060 return (ecol - scol + 1);
5061 }
5062
5063 /* copies text into line, preallocated */
5064 static Char *
5065 SaveText(TScreen *screen,
5066 int row,
5067 int scol,
5068 int ecol,
5069 Char *lp, /* pointer to where to put the text */
5070 int *eol)
5071 {
5072 LineData *ld;
5073 int i = 0;
5074 Char *result = lp;
5075 #if OPT_WIDE_CHARS
5076 unsigned previous = 0;
5077 #endif
5078
5079 ld = GET_LINEDATA(screen, row);
5080 i = Length(screen, row, scol, ecol);
5081 ecol = scol + i;
5082 #if OPT_DEC_CHRSET
5083 if (CSET_DOUBLE(GetLineDblCS(ld))) {
5084 scol = (scol + 0) / 2;
5085 ecol = (ecol + 1) / 2;
5086 }
5087 #endif
5088 *eol = !LineTstWrapped(ld);
5089 for (i = scol; i < ecol; i++) {
5090 unsigned c;
5091 assert(i < (int) ld->lineSize);
5092 c = E2A(ld->charData[i]);
5093 #if OPT_WIDE_CHARS
5094 /* We want to strip out every occurrence of HIDDEN_CHAR AFTER a
5095 * wide character.
5096 */
5097 if (c == HIDDEN_CHAR) {
5098 if (isWide((int) previous)) {
5099 previous = c;
5100 /* Combining characters attached to double-width characters
5101 are in memory attached to the HIDDEN_CHAR */
5102 if_OPT_WIDE_CHARS(screen, {
5103 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5104 size_t off;
5105 for_each_combData(off, ld) {
5106 unsigned ch = ld->combData[off][i];
5107 if (ch == 0)
5108 break;
5109 lp = convertToUTF8(lp, ch);
5110 }
5111 }
5112 });
5113 continue;
5114 } else {
5115 c = ' '; /* should not happen, but just in case... */
5116 }
5117 }
5118 previous = c;
5119 if ((screen->utf8_nrc_mode | screen->utf8_mode) != uFalse) {
5120 lp = convertToUTF8(lp, (c != 0) ? c : ' ');
5121 if_OPT_WIDE_CHARS(screen, {
5122 size_t off;
5123 for_each_combData(off, ld) {
5124 unsigned ch = ld->combData[off][i];
5125 if (ch == 0)
5126 break;
5127 lp = convertToUTF8(lp, ch);
5128 }
5129 });
5130 } else
5131 #endif
5132 {
5133 if (c == 0) {
5134 c = E2A(' ');
5135 } else if (c < E2A(' ')) {
5136 c = DECtoASCII(c);
5137 } else if (c == 0x7f) {
5138 c = 0x5f;
5139 }
5140 *lp++ = CharOf(A2E(c));
5141 }
5142 if (c != E2A(' '))
5143 result = lp;
5144 }
5145
5146 /*
5147 * If requested, trim trailing blanks from selected lines. Do not do this
5148 * if the line is wrapped.
5149 */
5150 if (!*eol || !screen->trim_selection)
5151 result = lp;
5152
5153 return (result);
5154 }
5155
5156 /*
5157 * This adds together the bits:
5158 * shift key -> 1
5159 * meta key -> 2
5160 * control key -> 4
5161 */
5162 static unsigned
5163 KeyState(XtermWidget xw, unsigned x)
5164 {
5165 return ((((x) & (ShiftMask | ControlMask)))
5166 + (((x) & MetaMask(xw)) ? 2 : 0));
5167 }
5168
5169 /* 32 + following 8-bit word:
5170
5171 1:0 Button no: 0, 1, 2. 3=release.
5172 2 shift
5173 3 meta
5174 4 ctrl
5175 5 set for motion notify
5176 6 set for wheel (and button 6 and 7)
5177 7 set for buttons 8 to 11
5178 */
5179
5180 /* Position: 32 - 255. */
5181 static int
5182 BtnCode(XtermWidget xw, XButtonEvent *event, int button)
5183 {
5184 int result = (int) (32 + (KeyState(xw, event->state) << 2));
5185
5186 if (event->type == MotionNotify)
5187 result += 32;
5188
5189 if (button < 0) {
5190 result += 3;
5191 } else {
5192 result += button & 3;
5193 if (button & 4)
5194 result += 64;
5195 if (button & 8)
5196 result += 128;
5197 }
5198 TRACE(("BtnCode button %d, %s state " FMT_MODIFIER_NAMES " ->%#x\n",
5199 button,
5200 visibleEventType(event->type),
5201 ARG_MODIFIER_NAMES(event->state),
5202 result));
5203 return result;
5204 }
5205
5206 static unsigned
5207 EmitButtonCode(XtermWidget xw,
5208 Char *line,
5209 unsigned count,
5210 XButtonEvent *event,
5211 int button)
5212 {
5213 TScreen *screen = TScreenOf(xw);
5214 int value;
5215
5216 if (okSendMousePos(xw) == X10_MOUSE) {
5217 value = CharOf(' ' + button);
5218 } else {
5219 value = BtnCode(xw, event, button);
5220 }
5221
5222 switch (screen->extend_coords) {
5223 default:
5224 line[count++] = CharOf(value);
5225 break;
5226 case SET_SGR_EXT_MODE_MOUSE:
5227 case SET_PIXEL_POSITION_MOUSE:
5228 value -= 32; /* encoding starts at zero */
5229 /* FALLTHRU */
5230 case SET_URXVT_EXT_MODE_MOUSE:
5231 count += (unsigned) sprintf((char *) line + count, "%d", value);
5232 break;
5233 case SET_EXT_MODE_MOUSE:
5234 if (value < 128