Home | History | Annotate | Line # | Download | only in wsmoused
      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