Home | History | Annotate | Line # | Download | only in wsmoused
selection.c revision 1.11
      1  1.11       uwe /* $NetBSD: selection.c,v 1.11 2021/11/24 14:34:51 uwe 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.11       uwe __RCSID("$NetBSD: selection.c,v 1.11 2021/11/24 14:34:51 uwe 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.4      jmmv static void selarea_hide(void);
    148   1.4      jmmv static void selarea_show(void);
    149   1.4      jmmv static void selarea_paste(void);
    150   1.4      jmmv 
    151   1.4      jmmv /* ---------------------------------------------------------------------- */
    152   1.4      jmmv 
    153   1.4      jmmv /* Mode initialization.  Reads configuration, checks if the kernel has
    154   1.4      jmmv  * support for mouse pointer and opens required files. */
    155   1.4      jmmv int
    156   1.4      jmmv selection_startup(struct mouse *m)
    157   1.4      jmmv {
    158   1.4      jmmv 	int i;
    159   1.4      jmmv 	struct wsdisplay_char ch;
    160   1.4      jmmv 	struct block *conf;
    161   1.4      jmmv 
    162   1.4      jmmv 	if (Initialized) {
    163   1.5      jmmv 		log_warnx("selection mode already initialized");
    164   1.4      jmmv 		return 1;
    165   1.4      jmmv 	}
    166   1.4      jmmv 
    167   1.4      jmmv 	(void)memset(&Selmouse, 0, sizeof(struct selmouse));
    168   1.4      jmmv 	Selmouse.sm_mouse = m;
    169   1.4      jmmv 
    170   1.4      jmmv 	conf = config_get_mode("selection");
    171   1.4      jmmv 	Selmouse.sm_slowdown_x = block_get_propval_int(conf, "slowdown_x", 0);
    172   1.4      jmmv 	Selmouse.sm_slowdown_y = block_get_propval_int(conf, "slowdown_y", 3);
    173   1.4      jmmv 	if (block_get_propval_int(conf, "lefthanded", 0)) {
    174   1.4      jmmv 		Selmouse.sm_but_select = 2;
    175   1.4      jmmv 		Selmouse.sm_but_paste = 0;
    176   1.4      jmmv 	} else {
    177   1.4      jmmv 		Selmouse.sm_but_select = 0;
    178   1.4      jmmv 		Selmouse.sm_but_paste = 2;
    179   1.4      jmmv 	}
    180   1.4      jmmv 
    181   1.4      jmmv 	/* Open current tty */
    182   1.4      jmmv 	(void)ioctl(Selmouse.sm_mouse->m_statfd, WSDISPLAYIO_GETACTIVESCREEN,
    183   1.4      jmmv 	    &i);
    184   1.4      jmmv 	Selmouse.sm_ttyfd = -1;
    185   1.4      jmmv 	open_tty(i);
    186   1.4      jmmv 
    187   1.4      jmmv 	/* Check if the kernel has character functions */
    188   1.4      jmmv 	ch.row = ch.col = 0;
    189   1.4      jmmv 	if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR, &ch) < 0) {
    190   1.4      jmmv 		(void)close(Selmouse.sm_ttyfd);
    191   1.5      jmmv 		log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
    192   1.4      jmmv 		return 0;
    193   1.4      jmmv 	}
    194   1.1  christos 
    195  1.10      jmmv 	assert(Selmouse.sm_max_y != 0); /* Initialized by open_tty above. */
    196  1.10      jmmv 	assert(Selmouse.sm_max_x != 0); /* Initialized by open_tty above. */
    197   1.4      jmmv 	Selmouse.sm_y = Selmouse.sm_max_y / 2;
    198   1.4      jmmv 	Selmouse.sm_x = Selmouse.sm_max_x / 2;
    199   1.4      jmmv 	Selmouse.sm_count_y = 0;
    200   1.4      jmmv 	Selmouse.sm_count_x = 0;
    201   1.4      jmmv 	Selmouse.sm_visible = 0;
    202   1.4      jmmv 	Selmouse.sm_selecting = 0;
    203   1.4      jmmv 	Initialized = 1;
    204   1.1  christos 
    205   1.4      jmmv 	return 1;
    206   1.4      jmmv }
    207   1.4      jmmv 
    208   1.4      jmmv /* ---------------------------------------------------------------------- */
    209   1.4      jmmv 
    210   1.4      jmmv /* Mode cleanup. */
    211   1.4      jmmv int
    212   1.4      jmmv selection_cleanup(void)
    213   1.4      jmmv {
    214   1.4      jmmv 
    215   1.4      jmmv 	cursor_hide();
    216   1.4      jmmv 	if (Selmouse.sm_ttyfd >= 0)
    217   1.4      jmmv 		(void)close(Selmouse.sm_ttyfd);
    218   1.4      jmmv 	return 1;
    219   1.4      jmmv }
    220   1.4      jmmv 
    221   1.4      jmmv /* ---------------------------------------------------------------------- */
    222   1.4      jmmv 
    223   1.4      jmmv /* Parse wsmouse events.  Both motion and button events are handled.  The
    224   1.4      jmmv  * former move the mouse across the screen and the later create a new
    225   1.4      jmmv  * selection or paste the buffer. */
    226   1.4      jmmv void
    227   1.4      jmmv selection_wsmouse_event(struct wscons_event evt)
    228   1.4      jmmv {
    229  1.11       uwe 	const struct wsmouse_calibcoords *abs = &Selmouse.sm_mouse->m_calib;
    230   1.4      jmmv 
    231   1.4      jmmv 	if (IS_MOTION_EVENT(evt.type)) {
    232   1.4      jmmv 		if (Selmouse.sm_selecting)
    233   1.4      jmmv 			selarea_hide();
    234   1.4      jmmv 		cursor_hide();
    235   1.4      jmmv 
    236   1.4      jmmv 		switch (evt.type) {
    237   1.4      jmmv 		case WSCONS_EVENT_MOUSE_DELTA_X:
    238   1.4      jmmv 			if (Selmouse.sm_count_x >= Selmouse.sm_slowdown_x) {
    239   1.4      jmmv 				Selmouse.sm_count_x = 0;
    240   1.4      jmmv 				if (evt.value > 0)
    241   1.4      jmmv 					Selmouse.sm_x++;
    242   1.4      jmmv 				else if (Selmouse.sm_x != 0)
    243   1.4      jmmv 					Selmouse.sm_x--;
    244   1.4      jmmv 				if (Selmouse.sm_x > Selmouse.sm_max_x)
    245   1.4      jmmv 					Selmouse.sm_x = Selmouse.sm_max_x;
    246   1.4      jmmv 			} else
    247   1.4      jmmv 				Selmouse.sm_count_x++;
    248   1.4      jmmv 			break;
    249   1.4      jmmv 
    250   1.4      jmmv 		case WSCONS_EVENT_MOUSE_DELTA_Y:
    251   1.4      jmmv 			if (Selmouse.sm_count_y >= Selmouse.sm_slowdown_y) {
    252   1.4      jmmv 				Selmouse.sm_count_y = 0;
    253   1.4      jmmv 				if (evt.value < 0)
    254   1.4      jmmv 					Selmouse.sm_y++;
    255   1.4      jmmv 				else if (Selmouse.sm_y != 0)
    256   1.4      jmmv 					Selmouse.sm_y--;
    257   1.4      jmmv 				if (Selmouse.sm_y > Selmouse.sm_max_y)
    258   1.4      jmmv 					Selmouse.sm_y = Selmouse.sm_max_y;
    259   1.4      jmmv 			} else
    260   1.4      jmmv 				Selmouse.sm_count_y++;
    261   1.4      jmmv 			break;
    262   1.4      jmmv 
    263  1.11       uwe 		case WSCONS_EVENT_MOUSE_DELTA_Z: /* FALLTHROUGH */
    264  1.11       uwe 		case WSCONS_EVENT_MOUSE_DELTA_W:
    265  1.11       uwe 			break;
    266  1.11       uwe 
    267  1.11       uwe 		case WSCONS_EVENT_MOUSE_ABSOLUTE_X:
    268  1.11       uwe 			if (!Selmouse.sm_mouse->m_doabs)
    269  1.11       uwe 				break;
    270  1.11       uwe 			/* max x is inclusive in both selmouse and tpcalib */
    271  1.11       uwe 			Selmouse.sm_x
    272  1.11       uwe 			    = ((evt.value - abs->minx) * (Selmouse.sm_max_x + 1))
    273  1.11       uwe 			      / (abs->maxx - abs->minx + 1);
    274  1.11       uwe 			break;
    275  1.11       uwe 
    276  1.11       uwe 		case WSCONS_EVENT_MOUSE_ABSOLUTE_Y:
    277  1.11       uwe 			if (!Selmouse.sm_mouse->m_doabs)
    278  1.11       uwe 				break;
    279  1.11       uwe 			/* max y is inclusive in both selmouse and tpcalib */
    280  1.11       uwe 			Selmouse.sm_y
    281  1.11       uwe 			    = ((evt.value - abs->miny) * (Selmouse.sm_max_y + 1))
    282  1.11       uwe 			      / (abs->maxy - abs->miny + 1);
    283  1.11       uwe 			break;
    284  1.11       uwe 
    285  1.11       uwe 		case WSCONS_EVENT_MOUSE_ABSOLUTE_Z: /* FALLTHROUGH */
    286  1.11       uwe 		case WSCONS_EVENT_MOUSE_ABSOLUTE_W:
    287   1.4      jmmv 			break;
    288   1.4      jmmv 
    289   1.4      jmmv 		default:
    290   1.5      jmmv 			log_warnx("unknown event");
    291   1.4      jmmv 		}
    292   1.4      jmmv 
    293   1.4      jmmv 		if (Selmouse.sm_selecting)
    294   1.4      jmmv 			selarea_show();
    295   1.4      jmmv 		cursor_show();
    296   1.4      jmmv 
    297   1.4      jmmv 	} else if (IS_BUTTON_EVENT(evt.type)) {
    298   1.4      jmmv 		switch (evt.type) {
    299   1.4      jmmv 		case WSCONS_EVENT_MOUSE_UP:
    300   1.4      jmmv 			if (evt.value == Selmouse.sm_but_select) {
    301   1.4      jmmv 				/* End selection */
    302   1.4      jmmv 				selarea_end();
    303   1.4      jmmv 				selarea_hide();
    304   1.4      jmmv 			}
    305   1.4      jmmv 			break;
    306   1.4      jmmv 
    307   1.4      jmmv 		case WSCONS_EVENT_MOUSE_DOWN:
    308   1.4      jmmv 			if (evt.value == Selmouse.sm_but_select) {
    309   1.4      jmmv 				/* Start selection */
    310   1.4      jmmv 				selarea_start();
    311   1.4      jmmv 				cursor_hide();
    312   1.4      jmmv 				selarea_show();
    313   1.4      jmmv 			} else if (evt.value == Selmouse.sm_but_paste) {
    314   1.4      jmmv 				/* Paste selection */
    315   1.4      jmmv 				selarea_paste();
    316   1.4      jmmv 				break;
    317   1.4      jmmv 			}
    318   1.4      jmmv 			break;
    319   1.4      jmmv 
    320   1.4      jmmv 		default:
    321   1.5      jmmv 			log_warnx("unknown button event");
    322   1.4      jmmv 		}
    323   1.4      jmmv 	}
    324   1.4      jmmv }
    325   1.4      jmmv 
    326   1.4      jmmv /* ---------------------------------------------------------------------- */
    327   1.4      jmmv 
    328   1.4      jmmv /* Parse wscons status events. */
    329   1.4      jmmv void
    330   1.8      matt selection_wscons_event(struct wscons_event evt, int preclose)
    331   1.4      jmmv {
    332   1.4      jmmv 
    333   1.4      jmmv 	switch (evt.type) {
    334   1.4      jmmv 	case WSCONS_EVENT_SCREEN_SWITCH:
    335   1.7      jmmv 		if (preclose) {
    336   1.7      jmmv 			if (Selmouse.sm_selecting)
    337   1.7      jmmv 				selarea_hide();
    338   1.7      jmmv 			cursor_hide();
    339   1.7      jmmv 		} else {
    340   1.7      jmmv 			if (!Selmouse.sm_mouse->m_disabled)
    341   1.7      jmmv 				open_tty(evt.value);
    342   1.4      jmmv 
    343   1.7      jmmv 			cursor_show();
    344   1.7      jmmv 			if (Selmouse.sm_selecting)
    345   1.7      jmmv 				selarea_show();
    346   1.7      jmmv 		}
    347   1.4      jmmv 
    348   1.4      jmmv 		break;
    349   1.4      jmmv 	}
    350   1.4      jmmv }
    351   1.4      jmmv 
    352   1.4      jmmv /* ---------------------------------------------------------------------- */
    353   1.4      jmmv 
    354   1.4      jmmv /* Device polling has timed out, so we hide the mouse to avoid further
    355   1.4      jmmv  * console pollution. */
    356   1.4      jmmv void
    357   1.4      jmmv selection_poll_timeout(void)
    358   1.4      jmmv {
    359   1.4      jmmv 
    360   1.4      jmmv 	if (!Selmouse.sm_selecting)
    361   1.4      jmmv 		cursor_hide();
    362   1.4      jmmv }
    363   1.4      jmmv 
    364   1.4      jmmv /* ---------------------------------------------------------------------- */
    365   1.4      jmmv 
    366   1.4      jmmv /* Hides the mouse pointer, if visible. */
    367   1.4      jmmv static void
    368   1.4      jmmv cursor_hide(void)
    369   1.4      jmmv {
    370   1.4      jmmv 
    371   1.4      jmmv 	if (Selmouse.sm_visible) {
    372   1.4      jmmv 		char_invert(Selmouse.sm_y, Selmouse.sm_x);
    373   1.4      jmmv 		Selmouse.sm_visible = 0;
    374   1.4      jmmv 	}
    375   1.4      jmmv }
    376   1.4      jmmv 
    377   1.4      jmmv /* ---------------------------------------------------------------------- */
    378   1.4      jmmv 
    379   1.4      jmmv /* Shows the mouse pointer, if not visible. */
    380   1.4      jmmv static void
    381   1.4      jmmv cursor_show(void)
    382   1.4      jmmv {
    383   1.4      jmmv 
    384   1.4      jmmv 	if (!Selmouse.sm_visible) {
    385   1.4      jmmv 		char_invert(Selmouse.sm_y, Selmouse.sm_x);
    386   1.4      jmmv 		Selmouse.sm_visible = 1;
    387   1.4      jmmv 	}
    388   1.4      jmmv }
    389   1.4      jmmv 
    390   1.4      jmmv /* ---------------------------------------------------------------------- */
    391   1.4      jmmv 
    392  1.10      jmmv /* Opens the specified TTY device, used when switching consoles.
    393  1.10      jmmv  * Takes care to adjust the pointer status to make sense within the new
    394  1.10      jmmv  * console, whose dimensions may differ from the previous one. */
    395   1.4      jmmv static void
    396   1.4      jmmv open_tty(int ttyno)
    397   1.4      jmmv {
    398   1.4      jmmv 	char buf[20];
    399  1.10      jmmv 	struct winsize ws;
    400   1.4      jmmv 
    401   1.4      jmmv 	if (Selmouse.sm_ttyfd >= 0)
    402   1.4      jmmv 		(void)close(Selmouse.sm_ttyfd);
    403   1.4      jmmv 
    404   1.6      jmmv 	(void)snprintf(buf, sizeof(buf), _PATH_TTYPREFIX "%d", ttyno);
    405   1.6      jmmv 	Selmouse.sm_ttyfd = open(buf, O_RDONLY | O_NONBLOCK);
    406   1.6      jmmv 	if (Selmouse.sm_ttyfd < 0)
    407   1.6      jmmv 		log_warnx("cannot open %s", buf);
    408  1.10      jmmv 
    409  1.10      jmmv 	/* Get terminal size and update the maximum cursor coordinates. */
    410  1.10      jmmv 	if (ioctl(Selmouse.sm_ttyfd, TIOCGWINSZ, &ws) < 0) {
    411  1.10      jmmv 		log_warn("cannot get terminal size");
    412  1.10      jmmv 		/* Some defaults; not guaranteed to be correct but we
    413  1.10      jmmv 		 * cannot do better. */
    414  1.10      jmmv 		Selmouse.sm_max_y = 24;
    415  1.10      jmmv 		Selmouse.sm_max_x = 79;
    416  1.10      jmmv 	} else {
    417  1.10      jmmv 		Selmouse.sm_max_y = ws.ws_row - 1;
    418  1.10      jmmv 		Selmouse.sm_max_x = ws.ws_col - 1;
    419  1.10      jmmv 	}
    420  1.10      jmmv 
    421  1.10      jmmv 	/* Adjust current mouse position in case the terminal's size has
    422  1.10      jmmv 	 * changed. */
    423  1.10      jmmv 	if (Selmouse.sm_x > Selmouse.sm_max_x)
    424  1.10      jmmv 		Selmouse.sm_x = Selmouse.sm_max_x;
    425  1.10      jmmv 	if (Selmouse.sm_y > Selmouse.sm_max_y)
    426  1.10      jmmv 		Selmouse.sm_y = Selmouse.sm_max_y;
    427   1.4      jmmv }
    428   1.4      jmmv 
    429   1.4      jmmv /* ---------------------------------------------------------------------- */
    430   1.4      jmmv 
    431   1.4      jmmv /* Flips the background and foreground colors of the specified screen
    432   1.4      jmmv  * position. */
    433   1.4      jmmv static void
    434   1.4      jmmv char_invert(size_t row, size_t col)
    435   1.4      jmmv {
    436   1.4      jmmv 	int t;
    437   1.4      jmmv 	struct wsdisplay_char ch;
    438   1.4      jmmv 
    439   1.4      jmmv 	ch.row = row;
    440   1.4      jmmv 	ch.col = col;
    441   1.4      jmmv 
    442   1.4      jmmv 	if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR, &ch) == -1) {
    443   1.5      jmmv 		log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
    444   1.4      jmmv 		return;
    445   1.4      jmmv 	}
    446   1.4      jmmv 
    447   1.4      jmmv 	t = ch.foreground;
    448   1.4      jmmv 	ch.foreground = ch.background;
    449   1.4      jmmv 	ch.background = t;
    450   1.4      jmmv 
    451   1.4      jmmv 	if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_PUTWSCHAR, &ch) == -1)
    452   1.5      jmmv 		log_warn("ioctl(WSDISPLAYIO_PUTWSCHAR) failed");
    453   1.4      jmmv }
    454   1.4      jmmv 
    455   1.4      jmmv /* ---------------------------------------------------------------------- */
    456   1.4      jmmv 
    457   1.4      jmmv /* Allocates memory for a selection.  This function is very simple but is
    458   1.4      jmmv  * used to get a consistent warning message. */
    459   1.1  christos static void *
    460   1.1  christos alloc_sel(size_t len)
    461   1.1  christos {
    462   1.1  christos 	void *ptr;
    463   1.4      jmmv 
    464   1.4      jmmv 	ptr = malloc(len);
    465   1.4      jmmv 	if (ptr == NULL)
    466   1.5      jmmv 		log_warn("cannot allocate memory for selection (%lu bytes)",
    467   1.1  christos 		    (unsigned long)len);
    468   1.1  christos 	return ptr;
    469   1.1  christos }
    470   1.1  christos 
    471   1.4      jmmv /* ---------------------------------------------------------------------- */
    472   1.4      jmmv 
    473   1.4      jmmv /* Copies a region of a line inside the buffer pointed by `ptr'. */
    474   1.2  christos static char *
    475   1.4      jmmv fill_buf(char *ptr, size_t row, size_t col, size_t end)
    476   1.1  christos {
    477   1.1  christos 	struct wsdisplay_char ch;
    478   1.4      jmmv 
    479   1.2  christos 	ch.row = row;
    480   1.1  christos 	for (ch.col = col; ch.col < end; ch.col++) {
    481   1.4      jmmv 		if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR,
    482   1.4      jmmv 		    &ch) == -1) {
    483   1.5      jmmv 			log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
    484   1.1  christos 			*ptr++ = ' ';
    485   1.1  christos 		} else {
    486   1.1  christos 			*ptr++ = ch.letter;
    487   1.1  christos 		}
    488   1.1  christos 	}
    489   1.2  christos 	return ptr;
    490   1.1  christos }
    491   1.1  christos 
    492   1.4      jmmv /* ---------------------------------------------------------------------- */
    493   1.1  christos 
    494   1.4      jmmv /* Scans the specified line and checks its length.  Characters at the end
    495   1.4      jmmv  * of it which match isspace() are discarded. */
    496   1.1  christos static size_t
    497   1.4      jmmv row_length(size_t row)
    498   1.1  christos {
    499   1.1  christos 	struct wsdisplay_char ch;
    500   1.1  christos 
    501   1.4      jmmv 	ch.col = Selmouse.sm_max_x;
    502   1.1  christos 	ch.row = row;
    503   1.1  christos 	do {
    504   1.4      jmmv 		if (ioctl(Selmouse.sm_ttyfd, WSDISPLAYIO_GETWSCHAR, &ch) == -1)
    505   1.5      jmmv 			log_warn("ioctl(WSDISPLAYIO_GETWSCHAR) failed");
    506   1.1  christos 		ch.col--;
    507   1.3      jmmv 	} while (isspace((unsigned char)ch.letter) && ch.col >= 0);
    508   1.1  christos 	return ch.col + 2;
    509   1.1  christos }
    510   1.1  christos 
    511   1.4      jmmv /* ---------------------------------------------------------------------- */
    512   1.4      jmmv 
    513   1.4      jmmv /* Copies all the text selected to the selection buffer.  Whitespace at
    514   1.4      jmmv  * end of lines is trimmed. */
    515   1.1  christos static void
    516   1.4      jmmv selarea_copy_text(void)
    517   1.1  christos {
    518   1.4      jmmv 	char *ptr, *str;
    519   1.4      jmmv 	size_t l, r;
    520   1.1  christos 
    521   1.9     lukem 	ptr = NULL;	/* XXXGCC -Wuninitialized */
    522   1.9     lukem 
    523   1.4      jmmv 	if (Selarea.sa_y1 == Selarea.sa_y2) {
    524   1.1  christos 		/* Selection is one row */
    525   1.4      jmmv 		l = row_length(Selarea.sa_y1);
    526   1.4      jmmv 		if (Selarea.sa_x1 > l)
    527   1.1  christos 			/* Selection is after last character,
    528   1.1  christos 			 * therefore it is empty */
    529   1.1  christos 			str = NULL;
    530   1.1  christos 		else {
    531   1.4      jmmv 			if (Selarea.sa_x2 > l)
    532   1.4      jmmv 				Selarea.sa_x2 = l;
    533   1.4      jmmv 			ptr = str =
    534   1.4      jmmv 			    alloc_sel(Selarea.sa_x2 - Selarea.sa_x1 + 1);
    535   1.1  christos 			if (ptr == NULL)
    536   1.1  christos 				return;
    537   1.2  christos 
    538   1.4      jmmv 			ptr = fill_buf(ptr, Selarea.sa_y1, Selarea.sa_x1,
    539   1.4      jmmv 			    Selarea.sa_x2);
    540   1.1  christos 			*ptr = '\0';
    541   1.1  christos 		}
    542   1.1  christos 	} else {
    543   1.1  christos 		/* Selection is multiple rows */
    544   1.4      jmmv 		ptr = str =
    545   1.4      jmmv 		    alloc_sel(Selarea.sa_endoff - Selarea.sa_startoff + 1);
    546   1.1  christos 		if (ptr == NULL)
    547   1.1  christos 			return;
    548   1.1  christos 
    549   1.1  christos 		/* Calculate and copy first line */
    550   1.4      jmmv 		l = row_length(Selarea.sa_y1);
    551   1.4      jmmv 		if (Selarea.sa_x1 < l) {
    552   1.4      jmmv 			ptr = fill_buf(ptr, Selarea.sa_y1, Selarea.sa_x1, l);
    553   1.1  christos 			*ptr++ = '\r';
    554   1.1  christos 		}
    555   1.1  christos 
    556   1.1  christos 		/* Copy mid lines if there are any */
    557   1.4      jmmv 		if ((Selarea.sa_y2 - Selarea.sa_y1) > 1) {
    558   1.4      jmmv 			for (r = Selarea.sa_y1 + 1; r <= Selarea.sa_y2 - 1;
    559   1.4      jmmv 			    r++) {
    560   1.4      jmmv 				ptr = fill_buf(ptr, r, 0, row_length(r));
    561   1.1  christos 				*ptr++ = '\r';
    562   1.1  christos 			}
    563   1.1  christos 		}
    564   1.1  christos 
    565   1.1  christos 		/* Calculate and copy end line */
    566   1.4      jmmv 		l = row_length(Selarea.sa_y2);
    567   1.4      jmmv 		if (Selarea.sa_x2 < l)
    568   1.4      jmmv 			l = Selarea.sa_x2;
    569   1.4      jmmv 		ptr = fill_buf(ptr, Selarea.sa_y2, 0, l);
    570   1.1  christos 		*ptr = '\0';
    571   1.1  christos 	}
    572   1.1  christos 
    573   1.4      jmmv 	if (Selarea.sa_buf != NULL) {
    574   1.4      jmmv 		free(Selarea.sa_buf);
    575   1.4      jmmv 		Selarea.sa_buf = NULL;
    576   1.1  christos 	}
    577   1.2  christos 
    578   1.1  christos 	if (str != NULL) {
    579   1.4      jmmv 		Selarea.sa_buf = str;
    580   1.4      jmmv 		Selarea.sa_buflen = ptr - str;
    581   1.1  christos 	}
    582   1.1  christos }
    583   1.1  christos 
    584   1.4      jmmv /* ---------------------------------------------------------------------- */
    585   1.4      jmmv 
    586   1.4      jmmv /* Starts a selection. */
    587   1.4      jmmv static void
    588   1.4      jmmv selarea_start(void)
    589   1.1  christos {
    590   1.1  christos 
    591   1.4      jmmv 	if (Selarea.sa_buf != NULL) {
    592   1.4      jmmv 		free(Selarea.sa_buf);
    593   1.4      jmmv 		Selarea.sa_buf = NULL;
    594   1.1  christos 	}
    595   1.2  christos 
    596   1.4      jmmv 	Selarea.sa_y1 = Selmouse.sm_y;
    597   1.4      jmmv 	Selarea.sa_x1 = Selmouse.sm_x;
    598   1.4      jmmv 	selarea_calculate();
    599   1.4      jmmv 	Selmouse.sm_selecting = 1;
    600   1.1  christos }
    601   1.1  christos 
    602   1.4      jmmv /* ---------------------------------------------------------------------- */
    603   1.4      jmmv 
    604   1.4      jmmv /* Ends a selection.  Highlighted text is copied to the buffer. */
    605   1.4      jmmv static void
    606   1.4      jmmv selarea_end(void)
    607   1.1  christos {
    608   1.1  christos 	size_t i;
    609   1.1  christos 
    610   1.4      jmmv 	selarea_calculate();
    611   1.1  christos 
    612   1.1  christos 	/* Invert sel coordinates if needed */
    613   1.4      jmmv 	if (Selarea.sa_x1 > Selarea.sa_x2) {
    614   1.4      jmmv 		i = Selarea.sa_x2;
    615   1.4      jmmv 		Selarea.sa_x2 = Selarea.sa_x1;
    616   1.4      jmmv 		Selarea.sa_x1 = i;
    617   1.4      jmmv 	}
    618   1.4      jmmv 	if (Selarea.sa_y1 > Selarea.sa_y2) {
    619   1.4      jmmv 		i = Selarea.sa_y2;
    620   1.4      jmmv 		Selarea.sa_y2 = Selarea.sa_y1;
    621   1.4      jmmv 		Selarea.sa_y1 = i;
    622   1.1  christos 	}
    623   1.1  christos 
    624   1.4      jmmv 	selarea_copy_text();
    625   1.4      jmmv 	Selmouse.sm_selecting = 0;
    626   1.1  christos }
    627   1.1  christos 
    628   1.4      jmmv /* ---------------------------------------------------------------------- */
    629   1.4      jmmv 
    630   1.4      jmmv /* Calculates selection absolute positions in the screen buffer. */
    631   1.4      jmmv static void
    632   1.4      jmmv selarea_calculate(void)
    633   1.1  christos {
    634   1.4      jmmv 	size_t i;
    635   1.4      jmmv 
    636   1.4      jmmv 	i = Selmouse.sm_max_x + 1;
    637   1.4      jmmv 	Selarea.sa_y2 = Selmouse.sm_y;
    638   1.4      jmmv 	Selarea.sa_x2 = Selmouse.sm_x;
    639   1.4      jmmv 	Selarea.sa_startoff = Selarea.sa_y1 * i + Selarea.sa_x1;
    640   1.4      jmmv 	Selarea.sa_endoff = Selarea.sa_y2 * i + Selarea.sa_x2;
    641   1.4      jmmv 
    642   1.4      jmmv 	if (Selarea.sa_startoff > Selarea.sa_endoff) {
    643   1.4      jmmv 		i = Selarea.sa_endoff;
    644   1.4      jmmv 		Selarea.sa_endoff = Selarea.sa_startoff;
    645   1.4      jmmv 		Selarea.sa_startoff = i;
    646   1.1  christos 	}
    647   1.1  christos }
    648   1.1  christos 
    649   1.4      jmmv /* ---------------------------------------------------------------------- */
    650   1.4      jmmv 
    651   1.4      jmmv /* Hides the highlighted region, returning it to normal colors. */
    652   1.4      jmmv static void
    653   1.4      jmmv selarea_hide(void)
    654   1.1  christos {
    655   1.1  christos 	size_t i;
    656   1.1  christos 
    657   1.4      jmmv 	for (i = Selarea.sa_startoff; i <= Selarea.sa_endoff; i++)
    658   1.4      jmmv 		char_invert(0, i);
    659   1.1  christos }
    660   1.1  christos 
    661   1.4      jmmv /* ---------------------------------------------------------------------- */
    662   1.4      jmmv 
    663   1.4      jmmv /* Highlights the selected region. */
    664   1.4      jmmv static void
    665   1.4      jmmv selarea_show(void)
    666   1.1  christos {
    667   1.1  christos 	size_t i;
    668   1.2  christos 
    669   1.4      jmmv 	selarea_calculate();
    670   1.4      jmmv 	for (i = Selarea.sa_startoff; i <= Selarea.sa_endoff; i++)
    671   1.4      jmmv 		char_invert(0, i);
    672   1.1  christos }
    673   1.1  christos 
    674   1.4      jmmv /* ---------------------------------------------------------------------- */
    675   1.1  christos 
    676   1.4      jmmv /* Pastes selected text into the active console. */
    677   1.4      jmmv static void
    678   1.4      jmmv selarea_paste(void)
    679   1.1  christos {
    680   1.1  christos 	size_t i;
    681   1.2  christos 
    682   1.4      jmmv 	if (Selarea.sa_buf == NULL)
    683   1.1  christos 		return;
    684   1.4      jmmv 	for (i = 0; i < Selarea.sa_buflen; i++)
    685   1.4      jmmv 		if (ioctl(Selmouse.sm_ttyfd, TIOCSTI,
    686   1.4      jmmv 		    &Selarea.sa_buf[i]) == -1)
    687   1.5      jmmv 			log_warn("ioctl(TIOCSTI)");
    688   1.1  christos }
    689