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