xref: /netbsd/usr.sbin/wsmoused/selection.c (revision c4a72b64)
1 /* $NetBSD: selection.c,v 1.2 2002/07/04 20:50:29 christos Exp $ */
2 
3 /*
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Julio Merino.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. The name authors may not be used to endorse or promote products
16  *    derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 
34 #ifndef lint
35 __RCSID("$NetBSD: selection.c,v 1.2 2002/07/04 20:50:29 christos Exp $");
36 #endif /* not lint */
37 
38 #include <sys/ioctl.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <sys/tty.h>
42 #include <dev/wscons/wsconsio.h>
43 
44 #include <ctype.h>
45 #include <fcntl.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include "pathnames.h"
54 #include "wsmoused.h"
55 
56 /* This struct holds information about a sel. For now there is
57  * only one global instace, but using a structre gives us a place for
58  * maintaining all the variables together. Also, someone may want to
59  * allow multiple sels, so it is easier this way. */
60 static struct selection {
61 	size_t start_row, start_col;
62 	size_t end_row, end_col;
63 	size_t abs_start, abs_end;
64 	size_t text_size;
65 	char *text;
66 } sel;
67 
68 
69 static void *
70 alloc_sel(size_t len)
71 {
72 	void *ptr;
73 	if ((ptr = malloc(len)) == NULL) {
74 		warn("Cannot allocate memory for sel %lu",
75 		    (unsigned long)len);
76 		return NULL;
77 	}
78 	return ptr;
79 }
80 
81 /*
82  * Copies a region of a line inside the buffer pointed by ptr. We use
83  * a double pointer because we modify the pointer. When the function
84  * finishes, ptr points to the end of the buffer.
85  */
86 static char *
87 fill_buf(char *ptr, struct mouse *m, size_t row, size_t col, size_t end)
88 {
89 	struct wsdisplay_char ch;
90 	ch.row = row;
91 	for (ch.col = col; ch.col < end; ch.col++) {
92 		if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) {
93 			warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
94 			*ptr++ = ' ';
95 		} else {
96 			*ptr++ = ch.letter;
97 		}
98 	}
99 	return ptr;
100 }
101 
102 
103 /*
104  * This function scans the specified line and checks its
105  * length. Characters at the end of it which match isspace() are not
106  * counted.
107  */
108 static size_t
109 row_length(struct mouse *m, size_t row)
110 {
111 	struct wsdisplay_char ch;
112 
113 	ch.col = m->max_col;
114 	ch.row = row;
115 	do {
116 		if (ioctl(m->tty_fd, WSDISPLAYIO_GETWSCHAR, &ch) == -1)
117 			warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
118 		ch.col--;
119 	} while (isspace((unsigned char)ch.letter));
120 	return ch.col + 2;
121 }
122 
123 /*
124  * This (complex) function copies all the text englobed in the current
125  * sel to the sel buffer. It does space trimming at end of
126  * lines if it is selected.
127  */
128 static void
129 sel_copy_text(struct mouse *m)
130 {
131 	char *str, *ptr;
132 	size_t r, l;
133 
134 	if (sel.start_row == sel.end_row) {
135 		/* Selection is one row */
136 		l = row_length(m, sel.start_row);
137 		if (sel.start_col > l)
138 			/* Selection is after last character,
139 			 * therefore it is empty */
140 			str = NULL;
141 		else {
142 			if (sel.end_col > l)
143 				sel.end_col = l;
144 			ptr = str = alloc_sel(sel.end_col - sel.start_col + 1);
145 			if (ptr == NULL)
146 				return;
147 
148 			ptr = fill_buf(ptr, m, sel.start_row, sel.start_col,
149 			    sel.end_col);
150 			*ptr = '\0';
151 		}
152 	} else {
153 		/* Selection is multiple rows */
154 		ptr = str = alloc_sel(sel.abs_end - sel.abs_start + 1);
155 		if (ptr == NULL)
156 			return;
157 
158 		/* Calculate and copy first line */
159 		l = row_length(m, sel.start_row);
160 		if (sel.start_col < l) {
161 			ptr = fill_buf(ptr, m, sel.start_row, sel.start_col, l);
162 			*ptr++ = '\r';
163 		}
164 
165 		/* Copy mid lines if there are any */
166 		if ((sel.end_row - sel.start_row) > 1) {
167 			for (r = sel.start_row + 1; r <= sel.end_row - 1; r++) {
168 				ptr = fill_buf(ptr, m, r, 0, row_length(m, r));
169 				*ptr++ = '\r';
170 			}
171 		}
172 
173 		/* Calculate and copy end line */
174 		l = row_length(m, sel.end_row);
175 		if (sel.end_col < l)
176 			l = sel.end_col;
177 		ptr = fill_buf(ptr, m, sel.end_row, 0, l);
178 		*ptr = '\0';
179 	}
180 
181 	if (sel.text != NULL) {
182 		free(sel.text);
183 		sel.text = NULL;
184 	}
185 
186 	if (str != NULL) {
187 		sel.text = str;
188 		sel.text_size = ptr - str;
189 	}
190 }
191 
192 /*
193  * Initializes sel data. It should be called only once, at
194  * wsmoused startup to initialize pointers.
195  */
196 void
197 mouse_sel_init()
198 {
199 	memset(&sel, 0, sizeof(struct selection));
200 }
201 
202 /*
203  * Starts a sel (when mouse is pressed).
204  */
205 void
206 mouse_sel_start(struct mouse *m)
207 {
208 	if (sel.text != NULL) {
209 		free(sel.text);
210 		sel.text = NULL;
211 	}
212 
213 	sel.start_row = m->row;
214 	sel.start_col = m->col;
215 	mouse_sel_calculate(m);
216 	m->selecting = 1;
217 }
218 
219 /*
220  * Ends a sel. Text is copied to memory for future pasting and
221  * highlighted region is returned to normal state.
222  */
223 void
224 mouse_sel_end(struct mouse *m)
225 {
226 	size_t i;
227 
228 	mouse_sel_calculate(m);
229 
230 	/* Invert sel coordinates if needed */
231 	if (sel.start_col > sel.end_col) {
232 		i = sel.end_col;
233 		sel.end_col = sel.start_col;
234 		sel.start_col = i;
235 	}
236 	if (sel.start_row > sel.end_row) {
237 		i = sel.end_row;
238 		sel.end_row = sel.start_row;
239 		sel.start_row = i;
240 	}
241 
242 	sel_copy_text(m);
243 	m->selecting = 0;
244 }
245 
246 /*
247  * Calculates sel absolute postitions.
248  */
249 void
250 mouse_sel_calculate(struct mouse *m)
251 {
252 	size_t i = m->max_col + 1;
253 
254 	sel.end_row = m->row;
255 	sel.end_col = m->col;
256 	sel.abs_start = sel.start_row * i + sel.start_col;
257 	sel.abs_end = sel.end_row * i + sel.end_col;
258 
259 	if (sel.abs_start > sel.abs_end) {
260 		i = sel.abs_end;
261 		sel.abs_end = sel.abs_start;
262 		sel.abs_start = i;
263 	}
264 }
265 
266 /*
267  * Hides highlighted region, returning it to normal colors.
268  */
269 void
270 mouse_sel_hide(struct mouse *m)
271 {
272 	size_t i;
273 
274 	for (i = sel.abs_start; i <= sel.abs_end; i++)
275 		char_invert(m, 0, i);
276 }
277 
278 /*
279  * Highlights selected region.
280  */
281 void
282 mouse_sel_show(struct mouse *m)
283 {
284 	size_t i;
285 
286 	mouse_sel_calculate(m);
287 	for (i = sel.abs_start; i <= sel.abs_end; i++)
288 		char_invert(m, 0, i);
289 }
290 
291 
292 /*
293  * Pastes selected text into the active console.
294  */
295 void
296 mouse_sel_paste(struct mouse *m)
297 {
298 	size_t i;
299 
300 	if (sel.text == NULL)
301 		return;
302 	for (i = 0; i < sel.text_size; i++)
303 		if (ioctl(m->tty_fd, TIOCSTI, &sel.text[i]) == -1)
304 			warn("ioctl(TIOCSTI)");
305 }
306