1 1.12 mlelstv /* $NetBSD: selection.c,v 1.12 2023/02/07 20:37:48 mlelstv Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.10 jmmv * Copyright (c) 2002, 2003, 2004, 2007 The NetBSD Foundation, Inc. 5 1.1 christos * All rights reserved. 6 1.1 christos * 7 1.1 christos * This code is derived from software contributed to The NetBSD Foundation 8 1.4 jmmv * by Julio M. Merino Vidal. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted provided that the following conditions 12 1.1 christos * are met: 13 1.1 christos * 1. Redistributions of source code must retain the above copyright 14 1.1 christos * notice, this list of conditions and the following disclaimer. 15 1.1 christos * 2. The name authors may not be used to endorse or promote products 16 1.1 christos * derived from this software without specific prior written 17 1.1 christos * permission. 18 1.1 christos * 19 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS 20 1.1 christos * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 1.1 christos * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 23 1.1 christos * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 25 1.1 christos * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 christos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 1.1 christos * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 1.1 christos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 1.1 christos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 1.1 christos */ 31 1.1 christos 32 1.1 christos #include <sys/cdefs.h> 33 1.1 christos 34 1.1 christos #ifndef lint 35 1.12 mlelstv __RCSID("$NetBSD: selection.c,v 1.12 2023/02/07 20:37:48 mlelstv Exp $"); 36 1.1 christos #endif /* not lint */ 37 1.1 christos 38 1.1 christos #include <sys/ioctl.h> 39 1.1 christos #include <sys/time.h> 40 1.1 christos #include <sys/types.h> 41 1.1 christos #include <sys/tty.h> 42 1.1 christos #include <dev/wscons/wsconsio.h> 43 1.2 christos 44 1.10 jmmv #include <assert.h> 45 1.1 christos #include <ctype.h> 46 1.4 jmmv #include <err.h> 47 1.1 christos #include <fcntl.h> 48 1.1 christos #include <stdio.h> 49 1.1 christos #include <stdlib.h> 50 1.1 christos #include <string.h> 51 1.1 christos #include <unistd.h> 52 1.1 christos 53 1.1 christos #include "pathnames.h" 54 1.1 christos #include "wsmoused.h" 55 1.1 christos 56 1.4 jmmv /* ---------------------------------------------------------------------- */ 57 1.4 jmmv 58 1.4 jmmv /* 59 1.4 jmmv * Public interface exported by the `selection' mode. 60 1.4 jmmv */ 61 1.4 jmmv 62 1.4 jmmv int selection_startup(struct mouse *m); 63 1.4 jmmv int selection_cleanup(void); 64 1.4 jmmv void selection_wsmouse_event(struct wscons_event); 65 1.8 matt void selection_wscons_event(struct wscons_event, int); 66 1.4 jmmv void selection_poll_timeout(void); 67 1.4 jmmv 68 1.4 jmmv struct mode_bootstrap Selection_Mode = { 69 1.4 jmmv "selection", 70 1.4 jmmv selection_startup, 71 1.4 jmmv selection_cleanup, 72 1.4 jmmv selection_wsmouse_event, 73 1.4 jmmv selection_wscons_event, 74 1.4 jmmv selection_poll_timeout 75 1.4 jmmv }; 76 1.4 jmmv 77 1.4 jmmv /* ---------------------------------------------------------------------- */ 78 1.4 jmmv 79 1.4 jmmv /* 80 1.4 jmmv * Structures used in this module only. 81 1.4 jmmv */ 82 1.4 jmmv 83 1.4 jmmv /* The `selarea' structure is used to describe a selection in the screen. 84 1.4 jmmv It also holds a copy of the selected text. */ 85 1.4 jmmv struct selarea { 86 1.4 jmmv size_t sa_x1; /* Start column */ 87 1.4 jmmv size_t sa_y1; /* Start row */ 88 1.4 jmmv size_t sa_x2; /* End column */ 89 1.4 jmmv size_t sa_y2; /* End row */ 90 1.4 jmmv size_t sa_startoff; /* Absolute offset of start position */ 91 1.4 jmmv size_t sa_endoff; /* Absolute offset of end position */ 92 1.4 jmmv size_t sa_buflen; /* Length of selected text */ 93 1.4 jmmv char *sa_buf; /* A copy of the selected text */ 94 1.4 jmmv }; 95 1.4 jmmv 96 1.4 jmmv /* The `selmouse' structure extends the `mouse' structure adding all fields 97 1.4 jmmv required for this module to work. */ 98 1.4 jmmv struct selmouse { 99 1.4 jmmv struct mouse *sm_mouse; /* Pointer to parent structure */ 100 1.4 jmmv 101 1.4 jmmv int sm_ttyfd; /* Active TTY file descriptor */ 102 1.4 jmmv 103 1.4 jmmv size_t sm_x; /* Mouse pointer column */ 104 1.4 jmmv size_t sm_y; /* Mouse pointer row */ 105 1.4 jmmv size_t sm_max_x; /* Maximun column allowed */ 106 1.4 jmmv size_t sm_max_y; /* Maximun row allowed */ 107 1.4 jmmv 108 1.4 jmmv size_t sm_slowdown_x; /* X axis slowdown */ 109 1.4 jmmv size_t sm_slowdown_y; /* Y axis slowdown */ 110 1.4 jmmv size_t sm_count_x; /* Number of X movements skipped */ 111 1.4 jmmv size_t sm_count_y; /* Number of Y movements skipped */ 112 1.4 jmmv 113 1.4 jmmv int sm_visible; /* Whether pointer is visible or not */ 114 1.4 jmmv int sm_selecting; /* Whether we are selecting or not */ 115 1.4 jmmv 116 1.4 jmmv int sm_but_select; /* Button number to select an area */ 117 1.4 jmmv int sm_but_paste; /* Button number to paste buffer */ 118 1.4 jmmv }; 119 1.4 jmmv 120 1.4 jmmv /* ---------------------------------------------------------------------- */ 121 1.4 jmmv 122 1.4 jmmv /* 123 1.4 jmmv * Global variables. 124 1.4 jmmv */ 125 1.4 jmmv 126 1.4 jmmv static struct selmouse Selmouse; 127 1.4 jmmv static struct selarea Selarea; 128 1.4 jmmv static int Initialized = 0; 129 1.4 jmmv 130 1.4 jmmv /* ---------------------------------------------------------------------- */ 131 1.4 jmmv 132 1.4 jmmv /* 133 1.4 jmmv * Prototypes for functions private to this module. 134 1.4 jmmv */ 135 1.4 jmmv 136 1.4 jmmv static void cursor_hide(void); 137 1.4 jmmv static void cursor_show(void); 138 1.4 jmmv static void open_tty(int); 139 1.4 jmmv static void char_invert(size_t, size_t); 140 1.4 jmmv static void *alloc_sel(size_t); 141 1.4 jmmv static char *fill_buf(char *, size_t, size_t, size_t); 142 1.4 jmmv static size_t row_length(size_t); 143 1.4 jmmv static void selarea_copy_text(void); 144 1.4 jmmv static void selarea_start(void); 145 1.4 jmmv static void selarea_end(void); 146 1.4 jmmv static void selarea_calculate(void); 147 1.12 mlelstv static void selarea_getrowcol(size_t, size_t *, size_t *); 148 1.4 jmmv static void selarea_hide(void); 149 1.4 jmmv static void selarea_show(void); 150 1.4 jmmv static void selarea_paste(void); 151 1.4 jmmv 152 1.4 jmmv /* ---------------------------------------------------------------------- */ 153 1.4 jmmv 154 1.4 jmmv /* Mode initialization. Reads configuration, checks if the kernel has 155 1.4 jmmv * support for mouse pointer and opens required files. */ 156 1.4 jmmv int 157 1.4 jmmv selection_startup(struct mouse *m) 158 1.4 jmmv { 159 1.4 jmmv int i; 160 1.4 jmmv struct wsdisplay_char ch; 161 1.4 jmmv struct block *conf; 162 1.4 jmmv 163 1.4 jmmv if (Initialized) { 164 1.5 jmmv log_warnx("selection mode already initialized"); 165 1.4 jmmv return 1; 166 1.4 jmmv } 167 1.4 jmmv 168 1.4 jmmv (void)memset(&Selmouse, 0, sizeof(struct selmouse)); 169 1.4 jmmv Selmouse.sm_mouse = m; 170 1.4 jmmv 171 1.4 jmmv conf = config_get_mode("selection"); 172 1.4 jmmv Selmouse.sm_slowdown_x = block_get_propval_int(conf, "slowdown_x", 0); 173 1.4 jmmv Selmouse.sm_slowdown_y = block_get_propval_int(conf, "slowdown_y", 3); 174 1.4 jmmv if (block_get_propval_int(conf, "lefthanded", 0)) { 175 1.4 jmmv Selmouse.sm_but_select = 2; 176 1.4 jmmv Selmouse.sm_but_paste = 0; 177 1.4 jmmv } else { 178 1.4 jmmv Selmouse.sm_but_select = 0; 179 1.4 jmmv Selmouse.sm_but_paste = 2; 180 1.4 jmmv } 181 1.4 jmmv 182 1.4 jmmv /* Open current tty */ 183 1.4 jmmv (void)ioctl(Selmouse.sm_mouse->m_statfd, WSDISPLAYIO_GETACTIVESCREEN, 184 1.4 jmmv &i); 185 1.4 jmmv Selmouse.sm_ttyfd = -1; 186 1.4 jmmv open_tty(i); 187 1.4 jmmv 188 1.4 jmmv /* Check if the kernel has character functions */ 189 1.4 jmmv ch.row = ch.col = 0; 190 1.4 jmmv if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR, &ch) < 0) { 191 1.4 jmmv (void)close(Selmouse.sm_ttyfd); 192 1.5 jmmv log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed"); 193 1.4 jmmv return 0; 194 1.4 jmmv } 195 1.1 christos 196 1.10 jmmv assert(Selmouse.sm_max_y != 0); /* Initialized by open_tty above. */ 197 1.10 jmmv assert(Selmouse.sm_max_x != 0); /* Initialized by open_tty above. */ 198 1.4 jmmv Selmouse.sm_y = Selmouse.sm_max_y / 2; 199 1.4 jmmv Selmouse.sm_x = Selmouse.sm_max_x / 2; 200 1.4 jmmv Selmouse.sm_count_y = 0; 201 1.4 jmmv Selmouse.sm_count_x = 0; 202 1.4 jmmv Selmouse.sm_visible = 0; 203 1.4 jmmv Selmouse.sm_selecting = 0; 204 1.4 jmmv Initialized = 1; 205 1.1 christos 206 1.4 jmmv return 1; 207 1.4 jmmv } 208 1.4 jmmv 209 1.4 jmmv /* ---------------------------------------------------------------------- */ 210 1.4 jmmv 211 1.4 jmmv /* Mode cleanup. */ 212 1.4 jmmv int 213 1.4 jmmv selection_cleanup(void) 214 1.4 jmmv { 215 1.4 jmmv 216 1.4 jmmv cursor_hide(); 217 1.4 jmmv if (Selmouse.sm_ttyfd >= 0) 218 1.4 jmmv (void)close(Selmouse.sm_ttyfd); 219 1.4 jmmv return 1; 220 1.4 jmmv } 221 1.4 jmmv 222 1.4 jmmv /* ---------------------------------------------------------------------- */ 223 1.4 jmmv 224 1.4 jmmv /* Parse wsmouse events. Both motion and button events are handled. The 225 1.4 jmmv * former move the mouse across the screen and the later create a new 226 1.4 jmmv * selection or paste the buffer. */ 227 1.4 jmmv void 228 1.4 jmmv selection_wsmouse_event(struct wscons_event evt) 229 1.4 jmmv { 230 1.11 uwe const struct wsmouse_calibcoords *abs = &Selmouse.sm_mouse->m_calib; 231 1.4 jmmv 232 1.4 jmmv if (IS_MOTION_EVENT(evt.type)) { 233 1.4 jmmv if (Selmouse.sm_selecting) 234 1.4 jmmv selarea_hide(); 235 1.4 jmmv cursor_hide(); 236 1.4 jmmv 237 1.4 jmmv switch (evt.type) { 238 1.4 jmmv case WSCONS_EVENT_MOUSE_DELTA_X: 239 1.4 jmmv if (Selmouse.sm_count_x >= Selmouse.sm_slowdown_x) { 240 1.4 jmmv Selmouse.sm_count_x = 0; 241 1.4 jmmv if (evt.value > 0) 242 1.4 jmmv Selmouse.sm_x++; 243 1.4 jmmv else if (Selmouse.sm_x != 0) 244 1.4 jmmv Selmouse.sm_x--; 245 1.4 jmmv if (Selmouse.sm_x > Selmouse.sm_max_x) 246 1.4 jmmv Selmouse.sm_x = Selmouse.sm_max_x; 247 1.4 jmmv } else 248 1.4 jmmv Selmouse.sm_count_x++; 249 1.4 jmmv break; 250 1.4 jmmv 251 1.4 jmmv case WSCONS_EVENT_MOUSE_DELTA_Y: 252 1.4 jmmv if (Selmouse.sm_count_y >= Selmouse.sm_slowdown_y) { 253 1.4 jmmv Selmouse.sm_count_y = 0; 254 1.4 jmmv if (evt.value < 0) 255 1.4 jmmv Selmouse.sm_y++; 256 1.4 jmmv else if (Selmouse.sm_y != 0) 257 1.4 jmmv Selmouse.sm_y--; 258 1.4 jmmv if (Selmouse.sm_y > Selmouse.sm_max_y) 259 1.4 jmmv Selmouse.sm_y = Selmouse.sm_max_y; 260 1.4 jmmv } else 261 1.4 jmmv Selmouse.sm_count_y++; 262 1.4 jmmv break; 263 1.4 jmmv 264 1.11 uwe case WSCONS_EVENT_MOUSE_DELTA_Z: /* FALLTHROUGH */ 265 1.11 uwe case WSCONS_EVENT_MOUSE_DELTA_W: 266 1.11 uwe break; 267 1.11 uwe 268 1.11 uwe case WSCONS_EVENT_MOUSE_ABSOLUTE_X: 269 1.11 uwe if (!Selmouse.sm_mouse->m_doabs) 270 1.11 uwe break; 271 1.11 uwe /* max x is inclusive in both selmouse and tpcalib */ 272 1.11 uwe Selmouse.sm_x 273 1.11 uwe = ((evt.value - abs->minx) * (Selmouse.sm_max_x + 1)) 274 1.11 uwe / (abs->maxx - abs->minx + 1); 275 1.11 uwe break; 276 1.11 uwe 277 1.11 uwe case WSCONS_EVENT_MOUSE_ABSOLUTE_Y: 278 1.11 uwe if (!Selmouse.sm_mouse->m_doabs) 279 1.11 uwe break; 280 1.11 uwe /* max y is inclusive in both selmouse and tpcalib */ 281 1.11 uwe Selmouse.sm_y 282 1.11 uwe = ((evt.value - abs->miny) * (Selmouse.sm_max_y + 1)) 283 1.11 uwe / (abs->maxy - abs->miny + 1); 284 1.11 uwe break; 285 1.11 uwe 286 1.11 uwe case WSCONS_EVENT_MOUSE_ABSOLUTE_Z: /* FALLTHROUGH */ 287 1.11 uwe case WSCONS_EVENT_MOUSE_ABSOLUTE_W: 288 1.4 jmmv break; 289 1.4 jmmv 290 1.4 jmmv default: 291 1.5 jmmv log_warnx("unknown event"); 292 1.4 jmmv } 293 1.4 jmmv 294 1.4 jmmv if (Selmouse.sm_selecting) 295 1.4 jmmv selarea_show(); 296 1.4 jmmv cursor_show(); 297 1.4 jmmv 298 1.4 jmmv } else if (IS_BUTTON_EVENT(evt.type)) { 299 1.4 jmmv switch (evt.type) { 300 1.4 jmmv case WSCONS_EVENT_MOUSE_UP: 301 1.4 jmmv if (evt.value == Selmouse.sm_but_select) { 302 1.4 jmmv /* End selection */ 303 1.4 jmmv selarea_end(); 304 1.4 jmmv selarea_hide(); 305 1.4 jmmv } 306 1.4 jmmv break; 307 1.4 jmmv 308 1.4 jmmv case WSCONS_EVENT_MOUSE_DOWN: 309 1.4 jmmv if (evt.value == Selmouse.sm_but_select) { 310 1.4 jmmv /* Start selection */ 311 1.4 jmmv selarea_start(); 312 1.4 jmmv cursor_hide(); 313 1.4 jmmv selarea_show(); 314 1.4 jmmv } else if (evt.value == Selmouse.sm_but_paste) { 315 1.4 jmmv /* Paste selection */ 316 1.4 jmmv selarea_paste(); 317 1.4 jmmv break; 318 1.4 jmmv } 319 1.4 jmmv break; 320 1.4 jmmv 321 1.4 jmmv default: 322 1.5 jmmv log_warnx("unknown button event"); 323 1.4 jmmv } 324 1.4 jmmv } 325 1.4 jmmv } 326 1.4 jmmv 327 1.4 jmmv /* ---------------------------------------------------------------------- */ 328 1.4 jmmv 329 1.4 jmmv /* Parse wscons status events. */ 330 1.4 jmmv void 331 1.8 matt selection_wscons_event(struct wscons_event evt, int preclose) 332 1.4 jmmv { 333 1.4 jmmv 334 1.4 jmmv switch (evt.type) { 335 1.4 jmmv case WSCONS_EVENT_SCREEN_SWITCH: 336 1.7 jmmv if (preclose) { 337 1.7 jmmv if (Selmouse.sm_selecting) 338 1.7 jmmv selarea_hide(); 339 1.7 jmmv cursor_hide(); 340 1.7 jmmv } else { 341 1.7 jmmv if (!Selmouse.sm_mouse->m_disabled) 342 1.7 jmmv open_tty(evt.value); 343 1.4 jmmv 344 1.7 jmmv cursor_show(); 345 1.7 jmmv if (Selmouse.sm_selecting) 346 1.7 jmmv selarea_show(); 347 1.7 jmmv } 348 1.4 jmmv 349 1.4 jmmv break; 350 1.4 jmmv } 351 1.4 jmmv } 352 1.4 jmmv 353 1.4 jmmv /* ---------------------------------------------------------------------- */ 354 1.4 jmmv 355 1.4 jmmv /* Device polling has timed out, so we hide the mouse to avoid further 356 1.4 jmmv * console pollution. */ 357 1.4 jmmv void 358 1.4 jmmv selection_poll_timeout(void) 359 1.4 jmmv { 360 1.4 jmmv 361 1.4 jmmv if (!Selmouse.sm_selecting) 362 1.4 jmmv cursor_hide(); 363 1.4 jmmv } 364 1.4 jmmv 365 1.4 jmmv /* ---------------------------------------------------------------------- */ 366 1.4 jmmv 367 1.4 jmmv /* Hides the mouse pointer, if visible. */ 368 1.4 jmmv static void 369 1.4 jmmv cursor_hide(void) 370 1.4 jmmv { 371 1.4 jmmv 372 1.4 jmmv if (Selmouse.sm_visible) { 373 1.4 jmmv char_invert(Selmouse.sm_y, Selmouse.sm_x); 374 1.4 jmmv Selmouse.sm_visible = 0; 375 1.4 jmmv } 376 1.4 jmmv } 377 1.4 jmmv 378 1.4 jmmv /* ---------------------------------------------------------------------- */ 379 1.4 jmmv 380 1.4 jmmv /* Shows the mouse pointer, if not visible. */ 381 1.4 jmmv static void 382 1.4 jmmv cursor_show(void) 383 1.4 jmmv { 384 1.4 jmmv 385 1.4 jmmv if (!Selmouse.sm_visible) { 386 1.4 jmmv char_invert(Selmouse.sm_y, Selmouse.sm_x); 387 1.4 jmmv Selmouse.sm_visible = 1; 388 1.4 jmmv } 389 1.4 jmmv } 390 1.4 jmmv 391 1.4 jmmv /* ---------------------------------------------------------------------- */ 392 1.4 jmmv 393 1.10 jmmv /* Opens the specified TTY device, used when switching consoles. 394 1.10 jmmv * Takes care to adjust the pointer status to make sense within the new 395 1.10 jmmv * console, whose dimensions may differ from the previous one. */ 396 1.4 jmmv static void 397 1.4 jmmv open_tty(int ttyno) 398 1.4 jmmv { 399 1.4 jmmv char buf[20]; 400 1.10 jmmv struct winsize ws; 401 1.4 jmmv 402 1.4 jmmv if (Selmouse.sm_ttyfd >= 0) 403 1.4 jmmv (void)close(Selmouse.sm_ttyfd); 404 1.4 jmmv 405 1.6 jmmv (void)snprintf(buf, sizeof(buf), _PATH_TTYPREFIX "%d", ttyno); 406 1.6 jmmv Selmouse.sm_ttyfd = open(buf, O_RDONLY | O_NONBLOCK); 407 1.6 jmmv if (Selmouse.sm_ttyfd < 0) 408 1.6 jmmv log_warnx("cannot open %s", buf); 409 1.10 jmmv 410 1.10 jmmv /* Get terminal size and update the maximum cursor coordinates. */ 411 1.10 jmmv if (ioctl(Selmouse.sm_ttyfd, TIOCGWINSZ, &ws) < 0) { 412 1.10 jmmv log_warn("cannot get terminal size"); 413 1.10 jmmv /* Some defaults; not guaranteed to be correct but we 414 1.10 jmmv * cannot do better. */ 415 1.10 jmmv Selmouse.sm_max_y = 24; 416 1.10 jmmv Selmouse.sm_max_x = 79; 417 1.10 jmmv } else { 418 1.10 jmmv Selmouse.sm_max_y = ws.ws_row - 1; 419 1.10 jmmv Selmouse.sm_max_x = ws.ws_col - 1; 420 1.10 jmmv } 421 1.10 jmmv 422 1.10 jmmv /* Adjust current mouse position in case the terminal's size has 423 1.10 jmmv * changed. */ 424 1.10 jmmv if (Selmouse.sm_x > Selmouse.sm_max_x) 425 1.10 jmmv Selmouse.sm_x = Selmouse.sm_max_x; 426 1.10 jmmv if (Selmouse.sm_y > Selmouse.sm_max_y) 427 1.10 jmmv Selmouse.sm_y = Selmouse.sm_max_y; 428 1.4 jmmv } 429 1.4 jmmv 430 1.4 jmmv /* ---------------------------------------------------------------------- */ 431 1.4 jmmv 432 1.4 jmmv /* Flips the background and foreground colors of the specified screen 433 1.4 jmmv * position. */ 434 1.4 jmmv static void 435 1.4 jmmv char_invert(size_t row, size_t col) 436 1.4 jmmv { 437 1.4 jmmv int t; 438 1.4 jmmv struct wsdisplay_char ch; 439 1.4 jmmv 440 1.4 jmmv ch.row = row; 441 1.4 jmmv ch.col = col; 442 1.4 jmmv 443 1.4 jmmv if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) { 444 1.5 jmmv log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed"); 445 1.4 jmmv return; 446 1.4 jmmv } 447 1.4 jmmv 448 1.4 jmmv t = ch.foreground; 449 1.4 jmmv ch.foreground = ch.background; 450 1.4 jmmv ch.background = t; 451 1.4 jmmv 452 1.4 jmmv if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_PUTWSCHAR, &ch) == -1) 453 1.5 jmmv log_warn("ioctl(WSDISPLAYIO_PUTWSCHAR) failed"); 454 1.4 jmmv } 455 1.4 jmmv 456 1.4 jmmv /* ---------------------------------------------------------------------- */ 457 1.4 jmmv 458 1.4 jmmv /* Allocates memory for a selection. This function is very simple but is 459 1.4 jmmv * used to get a consistent warning message. */ 460 1.1 christos static void * 461 1.1 christos alloc_sel(size_t len) 462 1.1 christos { 463 1.1 christos void *ptr; 464 1.4 jmmv 465 1.4 jmmv ptr = malloc(len); 466 1.4 jmmv if (ptr == NULL) 467 1.5 jmmv log_warn("cannot allocate memory for selection (%lu bytes)", 468 1.1 christos (unsigned long)len); 469 1.1 christos return ptr; 470 1.1 christos } 471 1.1 christos 472 1.4 jmmv /* ---------------------------------------------------------------------- */ 473 1.4 jmmv 474 1.4 jmmv /* Copies a region of a line inside the buffer pointed by `ptr'. */ 475 1.2 christos static char * 476 1.4 jmmv fill_buf(char *ptr, size_t row, size_t col, size_t end) 477 1.1 christos { 478 1.1 christos struct wsdisplay_char ch; 479 1.4 jmmv 480 1.2 christos ch.row = row; 481 1.1 christos for (ch.col = col; ch.col < end; ch.col++) { 482 1.4 jmmv if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR, 483 1.4 jmmv &ch) == -1) { 484 1.5 jmmv log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed"); 485 1.1 christos *ptr++ = ' '; 486 1.1 christos } else { 487 1.1 christos *ptr++ = ch.letter; 488 1.1 christos } 489 1.1 christos } 490 1.2 christos return ptr; 491 1.1 christos } 492 1.1 christos 493 1.4 jmmv /* ---------------------------------------------------------------------- */ 494 1.1 christos 495 1.4 jmmv /* Scans the specified line and checks its length. Characters at the end 496 1.4 jmmv * of it which match isspace() are discarded. */ 497 1.1 christos static size_t 498 1.4 jmmv row_length(size_t row) 499 1.1 christos { 500 1.1 christos struct wsdisplay_char ch; 501 1.1 christos 502 1.4 jmmv ch.col = Selmouse.sm_max_x; 503 1.1 christos ch.row = row; 504 1.1 christos do { 505 1.4 jmmv if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) 506 1.5 jmmv log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed"); 507 1.1 christos ch.col--; 508 1.3 jmmv } while (isspace((unsigned char)ch.letter) && ch.col >= 0); 509 1.1 christos return ch.col + 2; 510 1.1 christos } 511 1.1 christos 512 1.4 jmmv /* ---------------------------------------------------------------------- */ 513 1.4 jmmv 514 1.4 jmmv /* Copies all the text selected to the selection buffer. Whitespace at 515 1.4 jmmv * end of lines is trimmed. */ 516 1.1 christos static void 517 1.4 jmmv selarea_copy_text(void) 518 1.1 christos { 519 1.4 jmmv char *ptr, *str; 520 1.4 jmmv size_t l, r; 521 1.1 christos 522 1.9 lukem ptr = NULL; /* XXXGCC -Wuninitialized */ 523 1.9 lukem 524 1.4 jmmv if (Selarea.sa_y1 == Selarea.sa_y2) { 525 1.1 christos /* Selection is one row */ 526 1.4 jmmv l = row_length(Selarea.sa_y1); 527 1.4 jmmv if (Selarea.sa_x1 > l) 528 1.1 christos /* Selection is after last character, 529 1.1 christos * therefore it is empty */ 530 1.1 christos str = NULL; 531 1.1 christos else { 532 1.4 jmmv if (Selarea.sa_x2 > l) 533 1.4 jmmv Selarea.sa_x2 = l; 534 1.4 jmmv ptr = str = 535 1.4 jmmv alloc_sel(Selarea.sa_x2 - Selarea.sa_x1 + 1); 536 1.1 christos if (ptr == NULL) 537 1.1 christos return; 538 1.2 christos 539 1.4 jmmv ptr = fill_buf(ptr, Selarea.sa_y1, Selarea.sa_x1, 540 1.4 jmmv Selarea.sa_x2); 541 1.1 christos *ptr = '\0'; 542 1.1 christos } 543 1.1 christos } else { 544 1.1 christos /* Selection is multiple rows */ 545 1.4 jmmv ptr = str = 546 1.4 jmmv alloc_sel(Selarea.sa_endoff - Selarea.sa_startoff + 1); 547 1.1 christos if (ptr == NULL) 548 1.1 christos return; 549 1.1 christos 550 1.1 christos /* Calculate and copy first line */ 551 1.4 jmmv l = row_length(Selarea.sa_y1); 552 1.4 jmmv if (Selarea.sa_x1 < l) { 553 1.4 jmmv ptr = fill_buf(ptr, Selarea.sa_y1, Selarea.sa_x1, l); 554 1.1 christos *ptr++ = '\r'; 555 1.1 christos } 556 1.1 christos 557 1.1 christos /* Copy mid lines if there are any */ 558 1.4 jmmv if ((Selarea.sa_y2 - Selarea.sa_y1) > 1) { 559 1.4 jmmv for (r = Selarea.sa_y1 + 1; r <= Selarea.sa_y2 - 1; 560 1.4 jmmv r++) { 561 1.4 jmmv ptr = fill_buf(ptr, r, 0, row_length(r)); 562 1.1 christos *ptr++ = '\r'; 563 1.1 christos } 564 1.1 christos } 565 1.1 christos 566 1.1 christos /* Calculate and copy end line */ 567 1.4 jmmv l = row_length(Selarea.sa_y2); 568 1.4 jmmv if (Selarea.sa_x2 < l) 569 1.4 jmmv l = Selarea.sa_x2; 570 1.4 jmmv ptr = fill_buf(ptr, Selarea.sa_y2, 0, l); 571 1.1 christos *ptr = '\0'; 572 1.1 christos } 573 1.1 christos 574 1.4 jmmv if (Selarea.sa_buf != NULL) { 575 1.4 jmmv free(Selarea.sa_buf); 576 1.4 jmmv Selarea.sa_buf = NULL; 577 1.1 christos } 578 1.2 christos 579 1.1 christos if (str != NULL) { 580 1.4 jmmv Selarea.sa_buf = str; 581 1.4 jmmv Selarea.sa_buflen = ptr - str; 582 1.1 christos } 583 1.1 christos } 584 1.1 christos 585 1.4 jmmv /* ---------------------------------------------------------------------- */ 586 1.4 jmmv 587 1.4 jmmv /* Starts a selection. */ 588 1.4 jmmv static void 589 1.4 jmmv selarea_start(void) 590 1.1 christos { 591 1.1 christos 592 1.4 jmmv if (Selarea.sa_buf != NULL) { 593 1.4 jmmv free(Selarea.sa_buf); 594 1.4 jmmv Selarea.sa_buf = NULL; 595 1.1 christos } 596 1.2 christos 597 1.4 jmmv Selarea.sa_y1 = Selmouse.sm_y; 598 1.4 jmmv Selarea.sa_x1 = Selmouse.sm_x; 599 1.4 jmmv selarea_calculate(); 600 1.4 jmmv Selmouse.sm_selecting = 1; 601 1.1 christos } 602 1.1 christos 603 1.4 jmmv /* ---------------------------------------------------------------------- */ 604 1.4 jmmv 605 1.4 jmmv /* Ends a selection. Highlighted text is copied to the buffer. */ 606 1.4 jmmv static void 607 1.4 jmmv selarea_end(void) 608 1.1 christos { 609 1.1 christos size_t i; 610 1.1 christos 611 1.4 jmmv selarea_calculate(); 612 1.1 christos 613 1.1 christos /* Invert sel coordinates if needed */ 614 1.4 jmmv if (Selarea.sa_x1 > Selarea.sa_x2) { 615 1.4 jmmv i = Selarea.sa_x2; 616 1.4 jmmv Selarea.sa_x2 = Selarea.sa_x1; 617 1.4 jmmv Selarea.sa_x1 = i; 618 1.4 jmmv } 619 1.4 jmmv if (Selarea.sa_y1 > Selarea.sa_y2) { 620 1.4 jmmv i = Selarea.sa_y2; 621 1.4 jmmv Selarea.sa_y2 = Selarea.sa_y1; 622 1.4 jmmv Selarea.sa_y1 = i; 623 1.1 christos } 624 1.1 christos 625 1.4 jmmv selarea_copy_text(); 626 1.4 jmmv Selmouse.sm_selecting = 0; 627 1.1 christos } 628 1.1 christos 629 1.4 jmmv /* ---------------------------------------------------------------------- */ 630 1.4 jmmv 631 1.4 jmmv /* Calculates selection absolute positions in the screen buffer. */ 632 1.4 jmmv static void 633 1.4 jmmv selarea_calculate(void) 634 1.1 christos { 635 1.4 jmmv size_t i; 636 1.4 jmmv 637 1.4 jmmv i = Selmouse.sm_max_x + 1; 638 1.4 jmmv Selarea.sa_y2 = Selmouse.sm_y; 639 1.4 jmmv Selarea.sa_x2 = Selmouse.sm_x; 640 1.4 jmmv Selarea.sa_startoff = Selarea.sa_y1 * i + Selarea.sa_x1; 641 1.4 jmmv Selarea.sa_endoff = Selarea.sa_y2 * i + Selarea.sa_x2; 642 1.4 jmmv 643 1.4 jmmv if (Selarea.sa_startoff > Selarea.sa_endoff) { 644 1.4 jmmv i = Selarea.sa_endoff; 645 1.4 jmmv Selarea.sa_endoff = Selarea.sa_startoff; 646 1.4 jmmv Selarea.sa_startoff = i; 647 1.1 christos } 648 1.1 christos } 649 1.1 christos 650 1.4 jmmv /* ---------------------------------------------------------------------- */ 651 1.4 jmmv 652 1.12 mlelstv /* Turns selection absolute position in the screen buffer back into 653 1.12 mlelstv row, col co-ordinates */ 654 1.12 mlelstv static void 655 1.12 mlelstv selarea_getrowcol(size_t pos, size_t* row, size_t* col) 656 1.12 mlelstv { 657 1.12 mlelstv size_t xres = Selmouse.sm_max_x + 1; 658 1.12 mlelstv 659 1.12 mlelstv *row = pos / xres; 660 1.12 mlelstv *col = pos - (*row * xres); 661 1.12 mlelstv } 662 1.12 mlelstv 663 1.12 mlelstv /* ---------------------------------------------------------------------- */ 664 1.12 mlelstv 665 1.4 jmmv /* Hides the highlighted region, returning it to normal colors. */ 666 1.4 jmmv static void 667 1.4 jmmv selarea_hide(void) 668 1.1 christos { 669 1.12 mlelstv size_t i, row, col; 670 1.1 christos 671 1.12 mlelstv for (i = Selarea.sa_startoff; i <= Selarea.sa_endoff; i++) { 672 1.12 mlelstv selarea_getrowcol(i, &row, &col); 673 1.12 mlelstv char_invert(row, col); 674 1.12 mlelstv } 675 1.1 christos } 676 1.1 christos 677 1.4 jmmv /* ---------------------------------------------------------------------- */ 678 1.4 jmmv 679 1.4 jmmv /* Highlights the selected region. */ 680 1.4 jmmv static void 681 1.4 jmmv selarea_show(void) 682 1.1 christos { 683 1.12 mlelstv size_t i, row, col; 684 1.2 christos 685 1.4 jmmv selarea_calculate(); 686 1.12 mlelstv for (i = Selarea.sa_startoff; i <= Selarea.sa_endoff; i++) { 687 1.12 mlelstv selarea_getrowcol(i, &row, &col); 688 1.12 mlelstv char_invert(row, col); 689 1.12 mlelstv } 690 1.1 christos } 691 1.1 christos 692 1.4 jmmv /* ---------------------------------------------------------------------- */ 693 1.1 christos 694 1.4 jmmv /* Pastes selected text into the active console. */ 695 1.4 jmmv static void 696 1.4 jmmv selarea_paste(void) 697 1.1 christos { 698 1.1 christos size_t i; 699 1.2 christos 700 1.4 jmmv if (Selarea.sa_buf == NULL) 701 1.1 christos return; 702 1.4 jmmv for (i = 0; i < Selarea.sa_buflen; i++) 703 1.4 jmmv if (ioctl(Selmouse.sm_ttyfd, TIOCSTI, 704 1.4 jmmv &Selarea.sa_buf[i]) == -1) 705 1.5 jmmv log_warn("ioctl(TIOCSTI)"); 706 1.1 christos } 707