1 /*
2  * Copyright (c) 1993-2009, 2014-2016, 2018-2020 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *     * Redistributions of source code must retain the above copyright
9  *       notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above copyright
11  *       notice, this list of conditions and the following disclaimer in the
12  *       documentation and/or other materials provided with the distribution.
13  *     * Neither the name of Paul Mattes nor his contributors may be used
14  *       to endorse or promote products derived from this software without
15  *       specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT,
21  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  * Portions of this code were taken from xterm/button.c:
32  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
33  *
34  *                         All Rights Reserved
35  *
36  * Permission to use, copy, modify, and distribute this software and its
37  * documentation for any purpose and without fee is hereby granted,
38  * provided that the above copyright notice appear in all copies and that
39  * both that copyright notice and this permission notice appear in
40  * supporting documentation, and that the name of Digital Equipment
41  * Corporation not be used in advertising or publicity pertaining to
42  * distribution of the software without specific, written prior permission.
43  *
44  *
45  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
46  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
47  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
48  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
49  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
50  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51  * SOFTWARE.
52  */
53 
54 /*
55  *	select.c
56  *		This module handles selections.
57  */
58 
59 #include "globals.h"
60 #include "xglobals.h"
61 
62 #include <X11/Xatom.h>
63 #include <X11/Xmu/Atoms.h>
64 #include <X11/Xmu/StdSel.h>
65 #include "3270ds.h"
66 #include "appres.h"
67 #include "ctlr.h"
68 #include "resources.h"
69 #include "toggles.h"
70 
71 #include "codepage.h"
72 #include "ctlrc.h"
73 #include "kybd.h"
74 #include "popups.h"
75 #include "screen.h"
76 #include "selectc.h"
77 #include "tables.h"
78 #include "unicodec.h"
79 #include "utf8.h"
80 #include "utils.h"
81 #include "xactions.h"
82 #include "xscreen.h"
83 
84 #define Max(x, y)	(((x) > (y))? (x): (y))
85 #define Min(x, y)	(((x) < (y))? (x): (y))
86 
87 /*
88  * Mouse side.
89  */
90 
91 /* A button click establishes the boundaries of the 'fixed' area. */
92 static int      f_start = 0;	/* 'fixed' area */
93 static int      f_end = 0;
94 
95 /* Mouse motion moves the boundaries of the 'varying' area. */
96 static int      v_start = 0;	/* 'varying' area */
97 static int      v_end = 0;
98 
99 static unsigned long down_time = 0;
100 static unsigned long down1_time = 0;
101 static Dimension down1_x, down1_y;
102 static unsigned long up_time = 0;
103 static int      saw_motion = 0;
104 static int      num_clicks = 0;
105 static void grab_sel(int start, int end, bool really, Time t);
106 #define NS		5
107 static Atom     want_sel[NS];
108 static struct {			/* owned selections */
109     Atom atom;	/* atom */
110     char *buffer;	/* buffer contents (UTF-8) */
111 }               own_sel[NS];
112 static bool  cursor_moved = false;
113 static int      saved_cursor_addr;
114 static void own_sels(Time t);
115 static int	n_owned = -1;
116 static bool	any_selected = false;
117 
118 #define CLICK_INTERVAL	300
119 
120 #define event_x(event)		event->xbutton.x
121 #define event_y(event)		event->xbutton.y
122 #define event_time(event)	event->xbutton.time
123 
124 #define BOUNDED_XY(event, x, y) {	\
125     x = X_TO_COL(event_x(event));	\
126     if (x < 0) {			\
127 	x = 0;				\
128     }					\
129     if (x >= COLS) {			\
130 	x = COLS - 1;			\
131     }					\
132     if (flipped) {			\
133 	x = (COLS - x) - 1;		\
134     }					\
135     y = Y_TO_ROW(event_y(event) - *descent);	\
136     if (y <= 0) {			\
137 	y = 0;				\
138     }					\
139     if (y >= ROWS) {			\
140 	y = ROWS - 1;			\
141     }					\
142 }
143 
144 static int char_class[256] = {
145 /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si */
146     32,  1,  1,  1,  1,  1,  1,  1,  1, 32,  1,  1,  1,  1,  1,  1,
147 /* dle dc1 dc2 dc3 dc4 nak syn etb can  em sub esc  fs  gs  rs  us */
148      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
149 /*  sp   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   / */
150     32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
151 /*   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ? */
152     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 58, 59, 60, 61, 62, 63,
153 /*   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O */
154     64, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
155 /*   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _ */
156     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 91, 92, 93, 94, 48,
157 /*   `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o */
158     96, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
159 /*   p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  del */
160     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,123,124,125,126,   1,
161 /* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
162      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
163 /* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
164      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
165 /* nob exc cen ste cur yen bro sec dia cop ord gui not hyp reg mac */
166     32,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
167 /* deg plu two thr acu mu  par per ce  one mas gui one one thr que */
168    176,177,178,179,180,181,182,183,184,185,186,178,188,189,190,191,
169 /* Agr Aac Aci Ati Adi Ari AE  Cce Egr Eac Eci Edi Igr Iac Ici Idi */
170     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
171 /* ETH Nti Ogr Oac Oci Oti Odi mul Oob Ugr Uac Uci Udi Yac THO ssh */
172     48, 48, 48, 48, 48, 48, 48,215, 48, 48, 48, 48, 48, 48, 48, 48,
173 /* agr aac aci ati adi ari ae  cce egr eac eci edi igr iac ici idi */
174     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
175 /* eth nti ogr oac oci oti odi div osl ugr uac uci udi yac tho ydi */
176     48, 48, 48, 48, 48, 48, 48,247, 48, 48, 48, 48, 48, 48, 48, 48
177 };
178 
179 /* Parse a charClass string: [low-]high:value[,...] */
180 void
reclass(char * s)181 reclass(char *s)
182 {
183     int n;
184     int low, high, value;
185     int i;
186     char c;
187 
188     n = -1;
189     low = -1;
190     high = -1;
191     for (;;) {
192 	c = *s++;
193 	if (isdigit((unsigned char)c)) {
194 	    if (n == -1) {
195 		n = 0;
196 	    }
197 	    n = (n * 10) + (c - '0');
198 	    if (n > 255) {
199 		goto fail;
200 	    }
201 	} else if (c == '-') {
202 	    if (n == -1 || low != -1) {
203 		goto fail;
204 	    }
205 	    low = n;
206 	    n = -1;
207 	} else if (c == ':') {
208 	    if (n == -1) {
209 		goto fail;
210 	    }
211 	    high = n;
212 	    n = -1;
213 	} else if (c == ',' || c == '\0') {
214 	    if (n == -1) {
215 		goto fail;
216 	    }
217 	    value = n;
218 	    n = -1;
219 	    if (high == -1) {
220 		goto fail;
221 	    }
222 	    if (low == -1) {
223 		low = high;
224 	    }
225 	    if (high < low) {
226 		goto fail;
227 	    }
228 	    for (i = low; i <= high; i++) {
229 		char_class[i] = value;
230 	    }
231 	    low = -1;
232 	    high = -1;
233 	    if (c == '\0') {
234 		return;
235 	    }
236 	} else {
237 	    goto fail;
238 	}
239     }
240 
241 fail:
242     popup_an_error("Error in %s string", ResCharClass);
243 }
244 
245 static int
ucs4_class(ucs4_t u)246 ucs4_class(ucs4_t u)
247 {
248     return (u < 0x100)? char_class[u]: (int)u;
249 }
250 
251 static void
select_word(int baddr,Time t)252 select_word(int baddr, Time t)
253 {
254     unsigned char fa = get_field_attribute(baddr);
255     unsigned char ch;
256     int class;
257 
258     /* Find the initial character class */
259     if (ea_buf[baddr].ucs4) {
260 	class = ucs4_class(ea_buf[baddr].ucs4);
261     } else {
262 	if (FA_IS_ZERO(fa)) {
263 	    ch = EBC_space;
264 	} else {
265 	    ch = ea_buf[baddr].ec;
266 	}
267 	class = char_class[ebc2asc0[ch]];
268     }
269 
270     /* Find the beginning */
271     for (f_start = baddr; f_start % COLS; f_start--) {
272 	int xclass;
273 
274 	if (ea_buf[f_start].ucs4) {
275 	    xclass = ucs4_class(ea_buf[f_start].ucs4);
276 	} else {
277 	    fa = get_field_attribute(f_start);
278 	    if (FA_IS_ZERO(fa)) {
279 		ch = EBC_space;
280 	    } else {
281 		ch = ea_buf[f_start].ec;
282 	    }
283 	    xclass = char_class[ebc2asc0[ch]];
284 	}
285 	if (xclass != class) {
286 	    f_start++;
287 	    break;
288 	}
289     }
290 
291     /* Find the end */
292     for (f_end = baddr; (f_end+1) % COLS; f_end++) {
293 	int xclass;
294 
295 	if (ea_buf[f_start].ucs4) {
296 	    xclass = ucs4_class(ea_buf[f_end].ucs4);
297 	} else {
298 	    fa = get_field_attribute(f_end);
299 	    if (FA_IS_ZERO(fa)) {
300 		ch = EBC_space;
301 	    } else {
302 		ch = ea_buf[f_end].ec;
303 	    }
304 	    xclass = char_class[ebc2asc0[ch]];
305 	}
306 	if (xclass != class) {
307 	    f_end--;
308 	    break;
309 	}
310     }
311 
312     v_start = f_start;
313     v_end = f_end;
314     grab_sel(f_start, f_end, true, t);
315 }
316 
317 static void
select_line(int baddr,Time t)318 select_line(int baddr, Time t)
319 {
320     f_start = baddr - (baddr % COLS);
321     f_end = f_start + COLS - 1;
322     v_start = f_start;
323     v_end = f_end;
324     grab_sel(f_start, f_end, true, t);
325 }
326 
327 
328 /*
329  * Start a new selection.
330  * Usually bound to <Btn1Down>.
331  */
332 void
select_start_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)333 select_start_xaction(Widget w, XEvent *event, String *params,
334 	Cardinal *num_params)
335 {
336     int x, y;
337     int baddr;
338 
339     xaction_debug(select_start_xaction, event, params, num_params);
340     if (w != *screen) {
341 	return;
342     }
343     BOUNDED_XY(event, x, y);
344     baddr = ROWCOL_TO_BA(y, x);
345     f_start = f_end = v_start = v_end = baddr;
346     down1_time = down_time = event_time(event);
347     down1_x = event_x(event);
348     down1_y = event_y(event);
349     if (down_time - up_time > CLICK_INTERVAL) {
350 	num_clicks = 0;
351 	/* Commit any previous cursor move. */
352 	cursor_moved = false;
353     }
354     if (num_clicks == 0) {
355 	unselect(0, ROWS*COLS);
356     }
357 }
358 
359 /*
360  * Alternate form of select_start, which combines cursor motion with selection.
361  * Usually bound to <Btn1Down> in a user-specified keymap.
362  */
363 void
move_select_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)364 move_select_xaction(Widget w, XEvent *event, String *params,
365 	Cardinal *num_params)
366 {
367     int x, y;
368     int baddr;
369 
370     xaction_debug(move_select_xaction, event, params, num_params);
371     if (w != *screen) {
372 	return;
373     }
374     BOUNDED_XY(event, x, y);
375     baddr = ROWCOL_TO_BA(y, x);
376 
377     f_start = f_end = v_start = v_end = baddr;
378     down1_time = down_time = event_time(event);
379     down1_x = event_x(event);
380     down1_y = event_y(event);
381 
382     if (down_time - up_time > CLICK_INTERVAL) {
383 	num_clicks = 0;
384 	/* Commit any previous cursor move. */
385 	cursor_moved = false;
386     }
387     if (num_clicks == 0) {
388 	if (any_selected) {
389 	    unselect(0, ROWS*COLS);
390 	} else {
391 	    cursor_moved = true;
392 	    saved_cursor_addr = cursor_addr;
393 	    cursor_move(baddr);
394 	}
395     }
396 }
397 
398 /*
399  * Begin extending the current selection.
400  * Usually bound to <Btn3Down>.
401  */
402 void
start_extend_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)403 start_extend_xaction(Widget w, XEvent *event, String *params,
404 	Cardinal *num_params)
405 {
406     int x, y;
407     int baddr;
408     bool continuous = (!ever_3270 && !toggled(RECTANGLE_SELECT));
409 
410     xaction_debug(start_extend_xaction, event, params, num_params);
411     if (w != *screen) {
412 	return;
413     }
414 
415     down1_time = 0L;
416 
417     BOUNDED_XY(event, x, y);
418     baddr = ROWCOL_TO_BA(y, x);
419 
420     if (continuous) {
421 	/* Think linearly. */
422 	if (baddr < f_start) {
423 	    v_start = baddr;
424 	} else if (baddr > f_end) {
425 	    v_end = baddr;
426 	} else if (baddr - f_start > f_end - baddr) {
427 	    v_end = baddr;
428 	} else {
429 	    v_start = baddr;
430 	}
431     } else {
432 	/* Think rectangularly. */
433 	int nrow = baddr / COLS;
434 	int ncol = baddr % COLS;
435 	int vrow_ul = v_start / COLS;
436 	int vrow_lr = v_end / COLS;
437 	int vcol_ul = Min(v_start % COLS, v_end % COLS);
438 	int vcol_lr = Max(v_start % COLS, v_end % COLS);
439 
440 	/* Set up the row. */
441 	if (nrow <= vrow_ul) {
442 	    vrow_ul = nrow;
443 	} else if (nrow >= vrow_lr) {
444 	    vrow_lr = nrow;
445 	} else if (nrow - vrow_ul > vrow_lr - nrow) {
446 	    vrow_lr = nrow;
447 	} else {
448 	    vrow_ul = nrow;
449 	}
450 
451 	/* Set up the column. */
452 	if (ncol <= vcol_ul) {
453 	    vcol_ul = ncol;
454 	} else if (ncol >= vcol_lr) {
455 	    vcol_lr = ncol;
456 	} else if (ncol - vcol_ul > vcol_lr - ncol) {
457 	    vcol_lr = ncol;
458 	} else {
459 	    vcol_ul = ncol;
460 	}
461 
462 	v_start = (vrow_ul * COLS) + vcol_ul;
463 	v_end = (vrow_lr * COLS) + vcol_lr;
464     }
465 
466     grab_sel(v_start, v_end, true, event_time(event));
467     saw_motion = 1;
468     num_clicks = 0;
469 }
470 
471 /*
472  * Continuously extend the current selection.
473  * Usually bound to <Btn1Motion> and <Btn3Motion>.
474  */
475 void
select_extend_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)476 select_extend_xaction(Widget w, XEvent *event, String *params,
477 	Cardinal *num_params)
478 {
479     int x, y;
480     int baddr;
481 
482     xaction_debug(select_extend_xaction, event, params, num_params);
483     if (w != *screen) {
484 	return;
485     }
486 
487     /* Ignore initial drag events if are too near. */
488     if (down1_time != 0L &&
489 	abs((int) event_x(event) - (int) down1_x) < *char_width &&
490 	abs((int) event_y(event) - (int) down1_y) < *char_height) {
491 	return;
492     } else {
493 	down1_time = 0L;
494     }
495 
496     /* If we moved the 3270 cursor on the first click, put it back. */
497     if (cursor_moved) {
498 	cursor_move(saved_cursor_addr);
499 	cursor_moved = false;
500     }
501 
502     BOUNDED_XY(event, x, y);
503     baddr = ROWCOL_TO_BA(y, x);
504 
505     /*
506      * If baddr falls outside if the v range, open up the v range.  In
507      * addition, if we are extending one end of the v range, make sure the
508      * other end at least covers the f range.
509      */
510     if (baddr <= v_start) {
511 	v_start = baddr;
512 	v_end = f_end;
513     }
514     if (baddr >= v_end) {
515 	v_end = baddr;
516 	v_start = f_start;
517     }
518 
519     /*
520      * If baddr falls within the v range, narrow up the nearer end of the
521      * v range.
522      */
523     if (baddr > v_start && baddr < v_end) {
524 	if (baddr - v_start < v_end - baddr) {
525 	    v_start = baddr;
526 	} else {
527 	    v_end = baddr;
528 	}
529     }
530 
531     num_clicks = 0;
532     saw_motion = 1;
533     grab_sel(v_start, v_end, false, event_time(event));
534 }
535 
536 /*
537  * End the selection.
538  * Usually bound to <BtnUp>.
539  */
540 void
select_end_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)541 select_end_xaction(Widget w _is_unused, XEvent *event, String *params,
542 	Cardinal *num_params)
543 {
544     Cardinal i;
545     int x, y;
546 
547     xaction_debug(select_end_xaction, event, params, num_params);
548     if (w != *screen) {
549 	return;
550     }
551 
552     if (n_owned == -1) {
553 	for (i = 0; i < NS; i++) {
554 	    own_sel[i].atom = None;
555 	}
556 	n_owned = 0;
557     }
558     for (i = 0; i < NS; i++) {
559 	if (i < *num_params) {
560 	    want_sel[i] = XInternAtom(display, params[i], false);
561 	} else {
562 	    want_sel[i] = None;
563 	}
564     }
565     if (*num_params == 0) {
566 	want_sel[0] = XA_PRIMARY;
567     }
568 
569     BOUNDED_XY(event, x, y);
570     up_time = event_time(event);
571 
572     if (up_time - down_time > CLICK_INTERVAL) {
573 	num_clicks = 0;
574     }
575 
576     if (++num_clicks > 3) {
577 	num_clicks = 1;
578     }
579 
580     switch (num_clicks) {
581     case 1:
582 	if (saw_motion) {
583 	    f_start = v_start;
584 	    f_end = v_end;
585 	    grab_sel(f_start, f_end, true, event_time(event));
586 	}
587 	break;
588     case 2:
589 	/*
590 	 * If we moved the 3270 cursor on the first click, put it back.
591 	 */
592 	if (cursor_moved) {
593 	    cursor_move(saved_cursor_addr);
594 	    cursor_moved = false;
595 	}
596 	select_word(f_start, event_time(event));
597 	break;
598     case 3:
599 	select_line(f_start, event_time(event));
600 	break;
601     }
602     saw_motion = 0;
603 }
604 
605 /*
606  * New-style selection actions.
607  *
608  * These actions work a bit more intuitively in 3270 mode than the historic
609  * ones.
610  *  SelectDown is usually bound to Btn1Down.
611  *  SelectMotion is usually bound to Btn1Motion.
612  *  SelectUp is usually bound to Btn1Up.
613  *
614  * SelectDown deselects on the first click, and remembers the screen position.
615  *
616  * SelectMotion extends the selection from the location remembered by
617  *  SelectDown to the current location.
618  *
619  * SelectUp moves the cursor on the first click.  On clicks two and three, it
620  *  selects a word or line.  On click four, it deselects and resets the click
621  *  counter to one.
622  */
623 
624 void
SelectDown_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)625 SelectDown_xaction(Widget w _is_unused, XEvent *event, String *params,
626 	Cardinal *num_params)
627 {
628     int x, y;
629     int baddr;
630 
631     xaction_debug(SelectDown_xaction, event, params, num_params);
632     if (w != *screen) {
633 	return;
634     }
635     BOUNDED_XY(event, x, y);
636     baddr = ROWCOL_TO_BA(y, x);
637 
638     if (event_time(event) - down_time > CLICK_INTERVAL) {
639 	num_clicks = 0;
640     }
641 
642     down_time = event_time(event);
643     if (num_clicks == 0) {
644 	f_start = f_end = v_start = v_end = baddr;
645 	down1_time = down_time;
646 	if (any_selected) {
647 	    unselect(0, ROWS*COLS);
648 	}
649     }
650 }
651 
652 void
SelectMotion_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)653 SelectMotion_xaction(Widget w _is_unused, XEvent *event, String *params,
654 	Cardinal *num_params)
655 {
656     int x, y;
657     int baddr;
658 
659     xaction_debug(SelectMotion_xaction, event, params, num_params);
660     if (w != *screen) {
661 	return;
662     }
663 
664     /* Ignore initial drag events if are too near. */
665     if (down1_time != 0L &&
666 	abs((int) event_x(event) - (int) down1_x) < *char_width &&
667 	abs((int) event_y(event) - (int) down1_y) < *char_height) {
668 	return;
669     } else {
670 	down1_time = 0L;
671     }
672 
673     BOUNDED_XY(event, x, y);
674     baddr = ROWCOL_TO_BA(y, x);
675 
676     /*
677      * If baddr falls outside if the v range, open up the v range.  In
678      * addition, if we are extending one end of the v range, make sure the
679      * other end at least covers the f range.
680      */
681     if (baddr <= v_start) {
682 	v_start = baddr;
683 	v_end = f_end;
684     }
685     if (baddr >= v_end) {
686 	v_end = baddr;
687 	v_start = f_start;
688     }
689 
690     /*
691      * If baddr falls within the v range, narrow up the nearer end of the
692      * v range.
693      */
694     if (baddr > v_start && baddr < v_end) {
695 	if (baddr - v_start < v_end - baddr) {
696 	    v_start = baddr;
697 	} else {
698 	    v_end = baddr;
699 	}
700     }
701 
702     num_clicks = 0;
703     saw_motion = 1;
704     grab_sel(v_start, v_end, false, event_time(event));
705 }
706 
707 void
SelectUp_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)708 SelectUp_xaction(Widget w _is_unused, XEvent *event, String *params,
709 	Cardinal *num_params)
710 {
711     int x, y;
712     int baddr;
713     Cardinal i;
714 
715     xaction_debug(SelectUp_xaction, event, params, num_params);
716     if (w != *screen) {
717 	return;
718     }
719 
720     if (n_owned == -1) {
721 	for (i = 0; i < NS; i++) {
722 	    own_sel[i].atom = None;
723 	}
724 	n_owned = 0;
725     }
726     for (i = 0; i < NS; i++) {
727 	if (i < *num_params) {
728 	    want_sel[i] = XInternAtom(display, params[i], false);
729 	} else {
730 	    want_sel[i] = None;
731 	}
732     }
733     if (*num_params == 0) {
734 	want_sel[0] = XA_PRIMARY;
735     }
736 
737     BOUNDED_XY(event, x, y);
738     baddr = ROWCOL_TO_BA(y, x);
739 
740     if (event_time(event) - up_time > CLICK_INTERVAL) {
741 #if defined(DEBUG_CLICKS) /*[*/
742 	printf("too long, reset\n");
743 #endif /*]*/
744 	num_clicks = 0;
745     }
746     up_time = event_time(event);
747 
748     if (++num_clicks > 3) {
749 #if defined(DEBUG_CLICKS) /*[*/
750 	printf("wrap\n");
751 #endif /*]*/
752 	num_clicks = 1;
753     }
754 
755 #if defined(DEBUG_CLICKS) /*[*/
756     printf("%d clicks\n", num_clicks);
757 #endif /*]*/
758     switch (num_clicks) {
759     case 1:
760 	/*
761 	 * If we saw motion, then take the selection.
762 	 * Otherwise, if we're in 3270 mode, move the cursor.
763 	 */
764 	if (saw_motion) {
765 	    f_start = v_start;
766 	    f_end = v_end;
767 	    grab_sel(f_start, f_end, true, event_time(event));
768 	} else if (IN_3270) {
769 	    cursor_move(baddr);
770 	}
771 	break;
772     case 2:
773 	/*
774 	 * If we moved the 3270 cursor on the first click, put it back.
775 	 */
776 	select_word(f_start, event_time(event));
777 	break;
778     case 3:
779 	select_line(f_start, event_time(event));
780 	break;
781     }
782     saw_motion = 0;
783 }
784 
785 static void
set_select(XEvent * event,String * params,Cardinal * num_params)786 set_select(XEvent *event, String *params, Cardinal *num_params)
787 {
788     Cardinal i;
789 
790     if (!any_selected) {
791 	return;
792     }
793     if (n_owned == -1) {
794 	for (i = 0; i < NS; i++) {
795 	    own_sel[i].atom = None;
796 	}
797 	n_owned = 0;
798     }
799     for (i = 0; i < NS; i++)
800 	if (i < *num_params) {
801 	    want_sel[i] = XInternAtom(display, params[i], false);
802 	} else {
803 	    want_sel[i] = None;
804 	}
805     if (*num_params == 0) {
806 	want_sel[0] = XA_PRIMARY;
807     }
808     own_sels(event_time(event));
809 }
810 
811 /*
812  * Set the selection.
813  * Usually bound to the Copy key.
814  */
815 void
set_select_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)816 set_select_xaction(Widget w _is_unused, XEvent *event, String *params,
817     Cardinal *num_params)
818 {
819     xaction_debug(set_select_xaction, event, params, num_params);
820 
821     set_select(event, params, num_params);
822 }
823 
824 /*
825  * Translate the mouse position to a buffer address.
826  */
827 int
mouse_baddr(Widget w,XEvent * event)828 mouse_baddr(Widget w, XEvent *event)
829 {
830     int x, y;
831 
832     if (w != *screen) {
833 	return 0;
834     }
835     BOUNDED_XY(event, x, y);
836     return ROWCOL_TO_BA(y, x);
837 }
838 
839 /*
840  * Cut action.
841  */
842 #define ULS	sizeof(unsigned long)
843 #define ULBS	(ULS * 8)
844 
845 void
Cut_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)846 Cut_xaction(Widget w _is_unused, XEvent *event, String *params,
847 	Cardinal *num_params)
848 {
849     int baddr;
850     int ba2;
851     unsigned char fa = get_field_attribute(0);
852     unsigned long *target;
853 
854     xaction_debug(Cut_xaction, event, params, num_params);
855 
856     if (!any_selected) {
857 	return;
858     }
859 
860     set_select(event, params, num_params);
861 
862     target = (unsigned long *)XtCalloc(ULS, ((ROWS*COLS)+(ULBS-1)) / ULBS);
863 
864     /* Identify the positions to empty. */
865     for (baddr = 0; baddr < ROWS*COLS; baddr++) {
866 	if (ea_buf[baddr].fa) {
867 	    fa = ea_buf[baddr].fa;
868 	} else if ((IN_NVT || !FA_IS_PROTECTED(fa)) && screen_selected(baddr)) {
869 	    target[baddr/ULBS] |= 1L << (baddr%ULBS);
870 	}
871     }
872 
873     /* Erase them. */
874     for (baddr = 0; baddr < ROWS*COLS; baddr++) {
875 	if ((target[baddr/ULBS] & (1L << (baddr%ULBS)))
876 		    && ea_buf[baddr].ec != EBC_so
877 		    && ea_buf[baddr].ec != EBC_si) {
878 	    switch (ctlr_dbcs_state(baddr)) {
879 	    case DBCS_NONE:
880 	    case DBCS_SB:
881 		ctlr_add(baddr, EBC_space, ea_buf[baddr].cs);
882 		break;
883 	    case DBCS_LEFT:
884 		ctlr_add(baddr, EBC_space, ea_buf[baddr].cs);
885 		ba2 = baddr;
886 		INC_BA(ba2);
887 		ctlr_add(ba2, EBC_space, ea_buf[baddr].cs);
888 		break;
889 	    case DBCS_RIGHT:
890 		ba2 = baddr;
891 		DEC_BA(ba2);
892 		ctlr_add(ba2, EBC_space, ea_buf[baddr].cs);
893 		ctlr_add(baddr, EBC_space, ea_buf[baddr].cs);
894 		break;
895 	    default:
896 		break;
897 	    }
898 	    mdt_set(baddr);
899 	}
900     }
901 
902     Free(target);
903 }
904 
905 /*
906  * KybdSelect action.  Extends the selection area in the indicated direction.
907  */
908 void
KybdSelect_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)909 KybdSelect_xaction(Widget w _is_unused, XEvent *event, String *params,
910 	Cardinal *num_params)
911 {
912     enum { UP, DOWN, LEFT, RIGHT } direction;
913     int x_start, x_end;
914     Cardinal i;
915 
916     xaction_debug(KybdSelect_xaction, event, params, num_params);
917     if (w != *screen) {
918 	return;
919     }
920 
921     if (*num_params < 1) {
922 	popup_an_error("%s(): Requires one argument",
923 		action_name(KybdSelect_xaction));
924 	return;
925     }
926     if (!strcasecmp(params[0], "Up")) {
927 	direction = UP;
928     } else if (!strcasecmp(params[0], "Down")) {
929 	direction = DOWN;
930     } else if (!strcasecmp(params[0], "Left")) {
931 	direction = LEFT;
932     } else if (!strcasecmp(params[0], "Right")) {
933 	direction = RIGHT;
934     } else {
935 	popup_an_error("%s(): First argument must be Up, Down, Left, or "
936 		"Right", action_name(KybdSelect_xaction));
937 	return;
938     }
939 
940     if (!any_selected) {
941 	x_start = x_end = cursor_addr;
942     } else {
943 	if (f_start < f_end) {
944 	    x_start = f_start;
945 	    x_end = f_end;
946 	} else {
947 	    x_start = f_end;
948 	    x_end = f_start;
949 	}
950     }
951 
952     switch (direction) {
953     case UP:
954 	if (!(x_start / COLS)) {
955 	    return;
956 	}
957 	x_start -= COLS;
958 	break;
959     case DOWN:
960 	if ((x_end / COLS) == ROWS - 1) {
961 	    return;
962 	}
963 	x_end += COLS;
964 	break;
965     case LEFT:
966 	if (!(x_start % COLS)) {
967 	    return;
968 	}
969 	x_start--;
970 	break;
971     case RIGHT:
972 	if ((x_end % COLS) == COLS - 1) {
973 	    return;
974 	}
975 	x_end++;
976 	break;
977     }
978 
979     /* Figure out the atoms they want. */
980     if (n_owned == -1) {
981 	for (i = 0; i < NS; i++) {
982 	    own_sel[i].atom = None;
983 	}
984 	n_owned = 0;
985     }
986     for (i = 1; i < NS; i++) {
987 	if (i < *num_params) {
988 	    want_sel[i] = XInternAtom(display, params[i], false);
989 	} else {
990 	    want_sel[i] = None;
991 	}
992     }
993     if (*num_params == 1) {
994 	want_sel[0] = XA_PRIMARY;
995     }
996 
997     /* Grab the selection. */
998     f_start = v_start = x_start;
999     f_end = v_end = x_end;
1000     grab_sel(f_start, f_end, true, event_time(event));
1001 }
1002 
1003 /*
1004  * unselect action.  Removes a selection.
1005  */
1006 void
Unselect_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)1007 Unselect_xaction(Widget w _is_unused, XEvent *event, String *params,
1008 	Cardinal *num_params)
1009 {
1010     xaction_debug(Unselect_xaction, event, params, num_params);
1011 
1012     /* It's just cosmetic. */
1013     unselect(0, ROWS*COLS);
1014 }
1015 
1016 void
SelectAll_xaction(Widget w _is_unused,XEvent * event,String * params,Cardinal * num_params)1017 SelectAll_xaction(Widget w _is_unused, XEvent *event, String *params,
1018 	Cardinal *num_params)
1019 {
1020     Cardinal i;
1021 
1022     xaction_debug(SelectUp_xaction, event, params, num_params);
1023     if (w != *screen) {
1024 	return;
1025     }
1026 
1027     if (n_owned == -1) {
1028 	for (i = 0; i < NS; i++) {
1029 	    own_sel[i].atom = None;
1030 	}
1031 	n_owned = 0;
1032     }
1033     for (i = 0; i < NS; i++) {
1034 	if (i < *num_params) {
1035 	    want_sel[i] = XInternAtom(display, params[i], false);
1036 	} else {
1037 	    want_sel[i] = None;
1038 	}
1039     }
1040     if (*num_params == 0) {
1041 	want_sel[0] = XA_PRIMARY;
1042     }
1043 
1044     grab_sel(0, (ROWS * COLS) - 1, true, event_time(event));
1045 }
1046 
1047 /*
1048  * Screen side.
1049  */
1050 
1051 static char    *select_buf = NULL;
1052 static char    *sb_ptr = NULL;
1053 static int      sb_size = 0;
1054 #define SB_CHUNK	1024
1055 
1056 static Time     sel_time;
1057 
1058 static void
init_select_buf(void)1059 init_select_buf(void)
1060 {
1061     if (select_buf == NULL) {
1062 	select_buf = XtMalloc(sb_size = SB_CHUNK);
1063     }
1064     sb_ptr = select_buf;
1065 }
1066 
1067 static void
store_sel(char c)1068 store_sel(char c)
1069 {
1070     if (sb_ptr - select_buf >= sb_size) {
1071 	sb_size += SB_CHUNK;
1072 	select_buf = XtRealloc(select_buf, sb_size);
1073 	sb_ptr = select_buf + sb_size - SB_CHUNK;
1074     }
1075     *(sb_ptr++) = c;
1076 }
1077 
1078 /*
1079  * Convert the UTF-8 string to an ICCCM-defined STRING (ISO 8859-1 printable
1080  * plus tab and newline).
1081  *
1082  * Returns the length of the stored string.
1083  */
1084 static unsigned long
store_icccm_string(XtPointer value,const char * buf)1085 store_icccm_string(XtPointer value, const char *buf)
1086 {
1087     char *dst = (char *)value;
1088     unsigned long len = 0;
1089     bool skip = false;
1090 
1091     while (*buf) {
1092 	int nw;
1093 	ucs4_t ucs;
1094 
1095 	if (*buf == '\033') {
1096 	    /* Funky GE sequence.  Skip it. */
1097 	    *dst++ = ' ';
1098 	    len++;
1099 	    buf++;
1100 	    skip = true;
1101 	    continue;
1102 	}
1103 	nw = utf8_to_unicode(buf, strlen(buf), &ucs);
1104 	if (nw <= 0) {
1105 	    return len;
1106 	}
1107 	if (skip) {
1108 	    skip = false;
1109 	    continue;
1110 	}
1111 	if (ucs == '\n' ||
1112 	    (ucs >= 0x20 && ucs <= 0x7f) ||
1113 	    (ucs >= 0xa0 && ucs <= 0xff)) {
1114 	    *dst++ = ucs & 0xff;
1115 	    len++;
1116 	}
1117 	buf += nw;
1118     }
1119     return len;
1120 }
1121 
1122 static Boolean
convert_sel(Widget w,Atom * selection,Atom * target,Atom * type,XtPointer * value,unsigned long * length,int * format)1123 convert_sel(Widget w, Atom *selection, Atom *target, Atom *type,
1124 	XtPointer *value, unsigned long *length, int *format)
1125 {
1126     int i;
1127 
1128     /* Find the right selection. */
1129     for (i = 0; i < NS; i++) {
1130 	if (own_sel[i].atom == *selection) {
1131 	    break;
1132 	}
1133     }
1134     if (i >= NS) {	/* not my selection */
1135 	return False;
1136     }
1137 
1138     if (*target == XA_TARGETS(display)) {
1139 	Atom* targetP;
1140 	Atom* std_targets;
1141 	unsigned long std_length;
1142 
1143 	XmuConvertStandardSelection(w, sel_time, selection,
1144 		target, type, (caddr_t*) &std_targets, &std_length, format);
1145 #if defined(XA_UTF8_STRING) /*[*/
1146 	*length = std_length + 6;
1147 #else /*][*/
1148 	*length = std_length + 5;
1149 #endif /*]*/
1150 	*value = (XtPointer) XtMalloc(sizeof(Atom) * (*length));
1151 	targetP = *(Atom**)value;
1152 	*targetP++ = XA_STRING;
1153 	*targetP++ = XA_TEXT(display);
1154 	*targetP++ = XA_COMPOUND_TEXT(display);
1155 #if defined(XA_UTF8_STRING) /*[*/
1156 	*targetP++ = XA_UTF8_STRING(display);
1157 #endif /*]*/
1158 	*targetP++ = XA_LENGTH(display);
1159 	*targetP++ = XA_LIST_LENGTH(display);
1160 	memmove(targetP,  std_targets,
1161 		(int) (sizeof(Atom) * std_length));
1162 	XtFree((char *) std_targets);
1163 	*type = XA_ATOM;
1164 	*format = 32;
1165 	return True;
1166     }
1167 
1168     if (*target == XA_STRING ||
1169 	*target == XA_TEXT(display) ||
1170 	*target == XA_COMPOUND_TEXT(display)
1171 #if defined(XA_UTF8_STRING) /*[*/
1172 	|| *target == XA_UTF8_STRING(display)
1173 #endif /*]*/
1174 	) {
1175 	if (*target == XA_COMPOUND_TEXT(display)
1176 #if defined(XA_UTF8_STRING) /*[*/
1177 	    || *target == XA_UTF8_STRING(display)
1178 #endif /*]*/
1179 		) {
1180 		*type = *target;
1181 	} else {
1182 		*type = XA_STRING;
1183 	}
1184 	*length = strlen(own_sel[i].buffer);
1185 	*value = XtMalloc(*length);
1186 #if defined(XA_UTF8_STRING) /*[*/
1187 	if (*target == XA_UTF8_STRING(display)) {
1188 	    memmove(*value, own_sel[i].buffer, (int) *length);
1189 	} else {
1190 #endif /*]*/
1191 	    /*
1192 	     * N.B.: We return a STRING for COMPOUND_TEXT.
1193 	     * Someday we may do real ISO 2022, but not today.
1194 	     */
1195 	    *length = store_icccm_string(*value, own_sel[i].buffer);
1196 #if defined(XA_UTF8_STRING) /*[*/
1197 	}
1198 #endif /*]*/
1199 	*format = 8;
1200 	return True;
1201     }
1202     if (*target == XA_LIST_LENGTH(display)) {
1203 	*value = XtMalloc(4);
1204 	if (sizeof(long) == 4) {
1205 	    *(long *)*value = 1;
1206 	} else {
1207 	    long temp = 1;
1208 	    memmove(*value, ((char*) &temp) + sizeof(long) - 4, 4);
1209 	}
1210 	*type = XA_INTEGER;
1211 	*length = 1;
1212 	*format = 32;
1213 	return True;
1214     }
1215     if (*target == XA_LENGTH(display)) {
1216 	*value = XtMalloc(4);
1217 	if (sizeof(long) == 4) {
1218 	    *(long*)*value = strlen(own_sel[i].buffer);
1219 	} else {
1220 	    long temp = strlen(own_sel[i].buffer);
1221 	    memmove(*value, ((char *) &temp) + sizeof(long) - 4, 4);
1222 	}
1223 	*type = XA_INTEGER;
1224 	*length = 1;
1225 	*format = 32;
1226 	return True;
1227     }
1228 
1229     if (XmuConvertStandardSelection(w, sel_time, selection,
1230 	    target, type, (caddr_t *)value, length, format)) {
1231 	return True;
1232     }
1233 
1234     return False;
1235 }
1236 
1237 static void
lose_sel(Widget w _is_unused,Atom * selection)1238 lose_sel(Widget w _is_unused, Atom *selection)
1239 {
1240     int i;
1241 
1242     for (i = 0; i < NS; i++) {
1243 	if (own_sel[i].atom != None && own_sel[i].atom == *selection) {
1244 	    own_sel[i].atom = None;
1245 	    XtFree(own_sel[i].buffer);
1246 	    own_sel[i].buffer = NULL;
1247 	    n_owned--;
1248 	    break;
1249 	}
1250     }
1251     if (!n_owned) {
1252 	unselect(0, ROWS*COLS);
1253     }
1254 }
1255 
1256 /*
1257  * Somewhat convoluted logic to return an ASCII character for a given screen
1258  * position.
1259  *
1260  * The character has to be found indirectly from ea_buf and the field
1261  * attirbutes, so that zero-intensity fields become blanks.
1262  */
1263 static bool osc_valid = false;
1264 
1265 static void
osc_start(void)1266 osc_start(void)
1267 {
1268     osc_valid = false;
1269 }
1270 
1271 /*
1272  * Return a 'selection' version of a given character on the screen.
1273  * Returns a printable ASCII character, or 0 if the character is a NULL and
1274  * shouldn't be included in the selection.
1275  */
1276 static void
onscreen_char(int baddr,unsigned char * r,int * rlen)1277 onscreen_char(int baddr, unsigned char *r, int *rlen)
1278 {
1279     static int osc_baddr;
1280     static unsigned char fa;
1281     ucs4_t uc;
1282     int baddr2;
1283 
1284     *rlen = 1;
1285 
1286     /* If we aren't moving forward, all bets are off. */
1287     if (osc_valid && baddr < osc_baddr) {
1288 	osc_valid = false;
1289     }
1290 
1291     if (osc_valid) {
1292 	/*
1293 	 * Search for a new field attribute between the address we
1294 	 * want and the last address we searched.  If we found a new
1295 	 * field attribute, save the address for next time.
1296 	 */
1297 	get_bounded_field_attribute(baddr, osc_baddr, &fa);
1298 	osc_baddr = baddr;
1299     } else {
1300 	/*
1301 	 * Find the attribute the old way.
1302 	 */
1303 	fa = get_field_attribute(baddr);
1304 	osc_baddr = baddr;
1305 	osc_valid = true;
1306     }
1307 
1308     /* If it isn't visible, then make it a blank. */
1309     if (FA_IS_ZERO(fa)) {
1310 	*r = ' ';
1311 	return;
1312     }
1313 
1314     /* Handle DBCS. */
1315     switch (ctlr_dbcs_state(baddr)) {
1316     case DBCS_LEFT:
1317 	if (ea_buf[baddr].ucs4) {
1318 	    *rlen = unicode_to_utf8(ea_buf[baddr].ucs4, (char *)r);
1319 	} else {
1320 	    baddr2 = baddr;
1321 	    INC_BA(baddr2);
1322 	    uc = ebcdic_to_unicode((ea_buf[baddr].ec << 8) |
1323 			ea_buf[baddr2].ec,
1324 		    CS_BASE, EUO_NONE);
1325 	    *rlen = unicode_to_utf8(uc, (char *)r);
1326 	}
1327 	return;
1328     case DBCS_RIGHT:
1329 	/* Returned the entire character when the left half was read. */
1330 	*rlen = 0;
1331 	return;
1332     case DBCS_SI:
1333 	/* Suppress SI's altogether.  They'll expand back on paste. */
1334 	*rlen = 0;
1335 	return;
1336     case DBCS_SB:
1337 	/* Treat SB's as normal SBCS characters. */
1338 	break;
1339     default:
1340 	break;
1341     }
1342 
1343     switch (ea_buf[baddr].cs) {
1344     case CS_BASE:
1345     default:
1346 	if (ea_buf[baddr].ucs4) {
1347 	    *rlen = unicode_to_utf8(ea_buf[baddr].ucs4, (char *)r);
1348 	} else {
1349 	    switch (ea_buf[baddr].ec) {
1350 	    case EBC_so:
1351 		/*
1352 		 * Suppress SO's altogether.  They'll expand back on
1353 		 * paste.
1354 		 */
1355 		*rlen = 0;
1356 		return;
1357 	    case EBC_null:
1358 		*r = 0;
1359 		return;
1360 	    default:
1361 		/*
1362 		 * Note that we use the 'for_display' flavor of
1363 		 * ebcdic_base_to_unicode here, so DUP and FM are
1364 		 * translated to special private-use Unicode values.
1365 		 * These will (hopefully) be ignored by other
1366 		 * applications, but translated back to DUP and FM if
1367 		 * pasted back into x3270.
1368 		 */
1369 		uc = ebcdic_base_to_unicode(ea_buf[baddr].ec,
1370 			EUO_BLANK_UNDEF | EUO_UPRIV);
1371 		*rlen = unicode_to_utf8(uc, (char *)r);
1372 		if (*rlen < 0) {
1373 		    *rlen = 0;
1374 		}
1375 	    }
1376 	}
1377 	return;
1378     case CS_GE:
1379 	/* Translate APL to Unicode. */
1380 	uc = apl_to_unicode(ea_buf[baddr].ec, EUO_NONE);
1381 	if (uc == (ucs4_t)-1) {
1382 	    /* No translation. */
1383 	    uc = UPRIV_GE_00 + ea_buf[baddr].ec;
1384 	}
1385 	*rlen = unicode_to_utf8(uc, (char *)r);
1386 	if (*rlen < 0) {
1387 	    *rlen = 0;
1388 	}
1389 	return;
1390     case CS_LINEDRAW:
1391 	/* VT100 line-drawing character. */
1392 	*rlen = unicode_to_utf8(
1393 		linedraw_to_unicode(ea_buf[baddr].ucs4, false), (char *)r);
1394 	return;
1395     }
1396 }
1397 
1398 /*
1399  * Attempt to own the selections in want_sel[].
1400  */
1401 static void
own_sels(Time t)1402 own_sels(Time t)
1403 {
1404     int i, j;
1405 
1406     /*
1407      * Try to grab any new selections we may want.
1408      */
1409     for (i = 0; i < NS; i++) {
1410 	bool already_own = false;
1411 
1412 	if (want_sel[i] == None) {
1413 	    continue;
1414 	}
1415 
1416 	/* Check if we already own it. */
1417 	for (j = 0; j < NS; j++) {
1418 	    if (own_sel[j].atom == want_sel[i]) {
1419 		already_own = true;
1420 		break;
1421 	    }
1422 	}
1423 
1424 	/* Find the slot for it. */
1425 	if (!already_own) {
1426 	    for (j = 0; j < NS; j++) {
1427 		if (own_sel[j].atom == None) {
1428 		    break;
1429 		}
1430 	    }
1431 	    if (j >= NS) {
1432 		continue;
1433 	    }
1434 	}
1435 
1436 	if (XtOwnSelection(*screen, want_sel[i], t, convert_sel, lose_sel,
1437 		    NULL)) {
1438 	    if (!already_own) {
1439 		n_owned++;
1440 		own_sel[j].atom = want_sel[i];
1441 	    }
1442 	    Replace(own_sel[j].buffer, XtMalloc(strlen(select_buf) + 1));
1443 	    memmove(own_sel[j].buffer, select_buf, strlen(select_buf) + 1);
1444 	} else {
1445 	    XtWarning("Could not get selection");
1446 	    if (own_sel[j].atom != None) {
1447 		XtFree(own_sel[j].buffer);
1448 		own_sel[j].buffer = NULL;
1449 		own_sel[j].atom = None;
1450 		n_owned--;
1451 	    }
1452 	}
1453     }
1454     if (!n_owned) {
1455 	unselect(0, ROWS*COLS);
1456     }
1457     sel_time = t;
1458 }
1459 
1460 /*
1461  * Copy the selected area on the screen into a buffer and attempt to
1462  * own the selections in want_sel[].
1463  */
1464 #define VISUAL_LEFT(d)	((IS_LEFT(d)) || ((d) == DBCS_SI))
1465 static void
grab_sel(int start,int end,bool really,Time t)1466 grab_sel(int start, int end, bool really, Time t)
1467 {
1468     int i, j;
1469     int start_row, end_row;
1470     int nulls = 0;
1471     unsigned char osc[16];
1472     int len;
1473 
1474     unselect(0, ROWS*COLS);
1475 
1476     if (start > end) {
1477 	int exch = end;
1478 
1479 	end = start;
1480 	start = exch;
1481     }
1482 
1483     start_row = start / COLS;
1484     end_row = end / COLS;
1485 
1486     init_select_buf();	/* prime the store_sel() routine */
1487     osc_start();	/* prime the onscreen_char() routine */
1488 
1489     if (!ever_3270 && !toggled(RECTANGLE_SELECT)) {
1490 	/* Continuous selections */
1491 	if (IS_RIGHT(ctlr_dbcs_state(start))) {
1492 	    DEC_BA(start);
1493 	}
1494 	if (VISUAL_LEFT(ctlr_dbcs_state(end))) {
1495 	    INC_BA(end);
1496 	}
1497 	for (i = start; i <= end; i++) {
1498 	    screen_set_select(i);
1499 	    if (really) {
1500 		if (i != start && !(i % COLS)) {
1501 		    nulls = 0;
1502 		    store_sel('\n');
1503 		}
1504 		onscreen_char(i, osc, &len);
1505 		for (j = 0; j < len; j++) {
1506 		    if (osc[j]) {
1507 			while (nulls) {
1508 			    store_sel(' ');
1509 			    nulls--;
1510 			}
1511 			store_sel((char)osc[j]);
1512 		    } else {
1513 			nulls++;
1514 		    }
1515 		}
1516 	    }
1517 	}
1518 	/* Check for newline extension on the last line. */
1519 	if ((end % COLS) != (COLS - 1)) {
1520 	    bool all_blank = true;
1521 
1522 	    for (i = end; i < end + (COLS - (end % COLS)); i++) {
1523 		onscreen_char(i, osc, &len);
1524 		for (j = 0; j < len; j++) {
1525 		    if (osc[j]) {
1526 			all_blank = false;
1527 			    break;
1528 		    }
1529 		}
1530 	    }
1531 	    if (all_blank) {
1532 		for (i = end; i < end + (COLS - (end % COLS)); i++) {
1533 		    screen_set_select(i);
1534 		}
1535 		if (really) {
1536 		    store_sel('\n');
1537 		}
1538 	    }
1539 	}
1540     } else {
1541 	/* Rectangular selections */
1542 	if (start_row == end_row) {
1543 	    if (IS_RIGHT(ctlr_dbcs_state(start))) {
1544 		DEC_BA(start);
1545 	    }
1546 	    if (VISUAL_LEFT(ctlr_dbcs_state(end))) {
1547 		INC_BA(end);
1548 	    }
1549 	    for (i = start; i <= end; i++) {
1550 		screen_set_select(i);
1551 		if (really) {
1552 		    onscreen_char(i, osc, &len);
1553 		    for (j = 0; j < len; j++) {
1554 			if (osc[j]) {
1555 			    while (nulls) {
1556 				store_sel(' ');
1557 				nulls--;
1558 			    }
1559 			    store_sel((char)osc[j]);
1560 			} else {
1561 			    nulls++;
1562 			}
1563 		    }
1564 		}
1565 	    }
1566 	} else {
1567 	    int row, col;
1568 	    int start_col = start % COLS;
1569 	    int end_col = end % COLS;
1570 
1571 	    if (start_col > end_col) {
1572 		int exch = end_col;
1573 
1574 		end_col = start_col;
1575 		start_col = exch;
1576 	    }
1577 
1578 	    for (row = start_row; row <= end_row; row++) {
1579 		int sc = start_col;
1580 		int ec = end_col;
1581 
1582 		if (sc && IS_RIGHT(ctlr_dbcs_state(row*COLS + sc))) {
1583 		    sc = sc - 1;
1584 		}
1585 		if (ec < COLS-1 &&
1586 			VISUAL_LEFT(ctlr_dbcs_state(row*COLS + ec))) {
1587 		    ec = ec + 1;
1588 		}
1589 
1590 		for (col = sc; col <= ec; col++) {
1591 		    screen_set_select(row*COLS + col);
1592 		    if (really) {
1593 			onscreen_char(row*COLS + col, osc, &len);
1594 			for (j = 0; j < len; j++) {
1595 			    if (osc[j]) {
1596 				while (nulls) {
1597 				    store_sel(' ');
1598 				    nulls--;
1599 				}
1600 				store_sel((char)osc[j]);
1601 			    } else {
1602 				nulls++;
1603 			    }
1604 			}
1605 		    }
1606 		}
1607 		nulls = 0;
1608 		if (really && row < end_row) {
1609 		    store_sel('\n');
1610 		}
1611 	    }
1612 	}
1613     }
1614 
1615     /* Terminate the result. */
1616     if (really) {
1617 	store_sel('\0');
1618     }
1619 
1620     any_selected = true;
1621     ctlr_changed(0, ROWS*COLS);
1622     if (really) {
1623 	own_sels(t);
1624     }
1625 }
1626 
1627 /*
1628  * Check if any character in a given region is selected.
1629  */
1630 bool
area_is_selected(int baddr,int len)1631 area_is_selected(int baddr, int len)
1632 {
1633     int i;
1634 
1635     for (i = 0; i < len; i++) {
1636 	if (screen_selected(baddr+i)) {
1637 	    return true;
1638 	}
1639     }
1640     return false;
1641 }
1642 
1643 /*
1644  * Unhighlight the region of selected text -- but don't give up the selection.
1645  */
1646 void
unselect(int baddr _is_unused,int len _is_unused)1647 unselect(int baddr _is_unused, int len _is_unused)
1648 {
1649     if (any_selected) {
1650 	screen_unselect_all();
1651 	ctlr_changed(0, ROWS*COLS);
1652 	any_selected = false;
1653     }
1654 }
1655 
1656 /* Selection insertion. */
1657 #define NP	5
1658 static Atom	paste_atom[NP];
1659 static int	n_pasting = 0;
1660 static int	pix = 0;
1661 static Time	paste_time;
1662 #if defined(XA_UTF8_STRING) /*[*/
1663 static bool	paste_utf8;
1664 #endif /*]*/
1665 
1666 static void
paste_callback(Widget w,XtPointer client_data _is_unused,Atom * selection _is_unused,Atom * type _is_unused,XtPointer value,unsigned long * length,int * format _is_unused)1667 paste_callback(Widget w, XtPointer client_data _is_unused,
1668 	Atom *selection _is_unused, Atom *type _is_unused, XtPointer value,
1669 	unsigned long *length, int *format _is_unused)
1670 {
1671     char *s;
1672     unsigned long s_len;
1673     ucs4_t *u_buf;
1674     size_t u_len;
1675 
1676     if ((value == NULL) || (*length == 0)) {
1677 	XtFree(value);
1678 
1679 	/* Try the next one. */
1680 #if defined(XA_UTF8_STRING) /*[*/
1681 	if (paste_utf8) {
1682 	    paste_utf8 = false;
1683 	    XtGetSelectionValue(w, paste_atom[(pix - 1)], XA_STRING,
1684 		    paste_callback, NULL, paste_time);
1685 	} else
1686 #endif /*]*/
1687 	if (n_pasting > pix) {
1688 #if defined(XA_UTF8_STRING) /*[*/
1689 	    paste_utf8 = true;
1690 #endif /*]*/
1691 	    XtGetSelectionValue(w, paste_atom[pix++],
1692 #if defined(XA_UTF8_STRING) /*[*/
1693 		    XA_UTF8_STRING(display),
1694 #else /*][*/
1695 		    XA_STRING,
1696 #endif /*]*/
1697 		    paste_callback, NULL,
1698 		    paste_time);
1699 	}
1700 	return;
1701     }
1702 
1703     /* Convert the selection to Unicode. */
1704     s_len = *length;
1705     s = (char *)value;
1706     u_buf = (ucs4_t *)Malloc(*length * sizeof(ucs4_t));
1707     u_len = 0;
1708 
1709     while (s_len) {
1710 	ucs4_t uc;
1711 
1712 #if defined(XA_UTF8_STRING) /*[*/
1713 	if (paste_utf8) {
1714 	    int nu;
1715 
1716 	    nu = utf8_to_unicode(s, s_len, &uc);
1717 	    if (nu <= 0) {
1718 		break;
1719 	    }
1720 	    s += nu;
1721 	    s_len -= nu;
1722 	} else
1723 #endif /*]*/
1724 	{
1725 	    /* Assume it's ISO 8859-1. */
1726 	    uc = *s & 0xff;
1727 	    s++;
1728 	    s_len--;
1729 	}
1730 	u_buf[u_len++] = uc;
1731     }
1732     emulate_uinput(u_buf, u_len, true);
1733 
1734     Free(u_buf);
1735     XtFree(value);
1736 
1737     n_pasting = 0;
1738 }
1739 
1740 void
insert_selection_xaction(Widget w,XEvent * event,String * params,Cardinal * num_params)1741 insert_selection_xaction(Widget w, XEvent *event, String *params,
1742 	Cardinal *num_params)
1743 {
1744     Cardinal i;
1745     Atom a;
1746     XButtonEvent *be = (XButtonEvent *)event;
1747 
1748     xaction_debug(insert_selection_xaction, event, params, num_params);
1749 
1750     n_pasting = 0;
1751     for (i = 0; i < *num_params; i++) {
1752 	a = XInternAtom(display, params[i], true);
1753 	if (a == None) {
1754 	    popup_an_error("%s(): No atom for selection",
1755 		    action_name(insert_selection_xaction));
1756 	    continue;
1757 	}
1758 	if (n_pasting < NP) {
1759 	    paste_atom[n_pasting++] = a;
1760 	}
1761     }
1762     pix = 0;
1763 #if defined(XA_UTF8_STRING) /*[*/
1764     paste_utf8 = true;
1765 #endif /*]*/
1766     if (n_pasting > pix) {
1767 	paste_time = be->time;
1768 	XtGetSelectionValue(w, paste_atom[pix++],
1769 #if defined(XA_UTF8_STRING) /*[*/
1770 		XA_UTF8_STRING(display),
1771 #else /*][*/
1772 		XA_STRING,
1773 #endif /*]*/
1774 		paste_callback, NULL,
1775 		paste_time);
1776     }
1777 }
1778 
1779 /**
1780  * Select module registration.
1781  */
1782 void
select_register(void)1783 select_register(void)
1784 {
1785     static toggle_register_t toggles[] = {
1786 	{ RECTANGLE_SELECT,	NULL,	0 }
1787     };
1788 
1789     register_toggles(toggles, array_count(toggles));
1790 }
1791