selection.c revision 1.3
1/* $NetBSD: selection.c,v 1.3 2002/12/25 19:04:27 jmmv 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.3 2002/12/25 19:04:27 jmmv 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. */ 60static 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 69static void * 70alloc_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 */ 86static char * 87fill_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 */ 108static size_t 109row_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) && ch.col >= 0); 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 */ 128static void 129sel_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 */ 196void 197mouse_sel_init() 198{ 199 memset(&sel, 0, sizeof(struct selection)); 200} 201 202/* 203 * Starts a sel (when mouse is pressed). 204 */ 205void 206mouse_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 */ 223void 224mouse_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 */ 249void 250mouse_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 */ 269void 270mouse_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 */ 281void 282mouse_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 */ 295void 296mouse_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