xref: /freebsd/sys/dev/syscons/scmouse.c (revision aad970f1fee9a2a3e5a0f880be9b87c6193b3bd1)
1 /*-
2  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer as
10  *    the first lines of this file unmodified.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include "opt_syscons.h"
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/conf.h>
34 #include <sys/consio.h>
35 #include <sys/fbio.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mouse.h>
40 #include <sys/mutex.h>
41 #include <sys/proc.h>
42 #include <sys/random.h>
43 #include <sys/signalvar.h>
44 #include <sys/tty.h>
45 
46 #include <dev/syscons/syscons.h>
47 
48 #ifdef SC_TWOBUTTON_MOUSE
49 #define SC_MOUSE_PASTEBUTTON	MOUSE_BUTTON3DOWN	/* right button */
50 #define SC_MOUSE_EXTENDBUTTON	MOUSE_BUTTON2DOWN	/* not really used */
51 #else
52 #define SC_MOUSE_PASTEBUTTON	MOUSE_BUTTON2DOWN	/* middle button */
53 #define SC_MOUSE_EXTENDBUTTON	MOUSE_BUTTON3DOWN	/* right button */
54 #endif /* SC_TWOBUTTON_MOUSE */
55 
56 #define SC_WAKEUP_DELTA		20
57 
58 /* for backward compatibility */
59 #define OLD_CONS_MOUSECTL	_IOWR('c', 10, old_mouse_info_t)
60 
61 typedef struct old_mouse_data {
62     int x;
63     int y;
64     int buttons;
65 } old_mouse_data_t;
66 
67 typedef struct old_mouse_info {
68     int operation;
69     union {
70 	struct old_mouse_data data;
71 	struct mouse_mode mode;
72     } u;
73 } old_mouse_info_t;
74 
75 #ifndef SC_NO_SYSMOUSE
76 
77 /* local variables */
78 #ifndef SC_NO_CUTPASTE
79 static int		cut_buffer_size;
80 static u_char		*cut_buffer;
81 #endif
82 
83 /* local functions */
84 static void set_mouse_pos(scr_stat *scp);
85 #ifndef SC_NO_CUTPASTE
86 static int skip_spc_right(scr_stat *scp, int p);
87 static int skip_spc_left(scr_stat *scp, int p);
88 static void mouse_cut(scr_stat *scp);
89 static void mouse_cut_start(scr_stat *scp);
90 static void mouse_cut_end(scr_stat *scp);
91 static void mouse_cut_word(scr_stat *scp);
92 static void mouse_cut_line(scr_stat *scp);
93 static void mouse_cut_extend(scr_stat *scp);
94 #endif /* SC_NO_CUTPASTE */
95 
96 #ifndef SC_NO_CUTPASTE
97 /* allocate a cut buffer */
98 void
99 sc_alloc_cut_buffer(scr_stat *scp, int wait)
100 {
101     u_char *p;
102 
103     if ((cut_buffer == NULL)
104 	|| (cut_buffer_size < scp->xsize * scp->ysize + 1)) {
105 	p = cut_buffer;
106 	cut_buffer = NULL;
107 	if (p != NULL)
108 	    free(p, M_DEVBUF);
109 	cut_buffer_size = scp->xsize * scp->ysize + 1;
110 	p = (u_char *)malloc(cut_buffer_size,
111 			     M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT);
112 	if (p != NULL)
113 	    p[0] = '\0';
114 	cut_buffer = p;
115     }
116 }
117 #endif /* SC_NO_CUTPASTE */
118 
119 /* move mouse */
120 void
121 sc_mouse_move(scr_stat *scp, int x, int y)
122 {
123     int s;
124 
125     s = spltty();
126     scp->mouse_xpos = scp->mouse_oldxpos = x;
127     scp->mouse_ypos = scp->mouse_oldypos = y;
128     if (scp->font_size <= 0)
129 	scp->mouse_pos = scp->mouse_oldpos = 0;
130     else
131 	scp->mouse_pos = scp->mouse_oldpos =
132 	    (y/scp->font_size - scp->yoff)*scp->xsize + x/scp->font_width -
133 	    scp->xoff;
134     scp->status |= MOUSE_MOVED;
135     splx(s);
136 }
137 
138 /* adjust mouse position */
139 static void
140 set_mouse_pos(scr_stat *scp)
141 {
142     if (scp->mouse_xpos < scp->xoff*scp->font_width)
143 	scp->mouse_xpos = scp->xoff*scp->font_width;
144     if (scp->mouse_ypos < scp->yoff*scp->font_size)
145 	scp->mouse_ypos = scp->yoff*scp->font_size;
146     if (ISGRAPHSC(scp)) {
147         if (scp->mouse_xpos > scp->xpixel-1)
148 	    scp->mouse_xpos = scp->xpixel-1;
149         if (scp->mouse_ypos > scp->ypixel-1)
150 	    scp->mouse_ypos = scp->ypixel-1;
151 	return;
152     } else {
153 	if (scp->mouse_xpos > (scp->xsize + scp->xoff)*scp->font_width - 1)
154 	    scp->mouse_xpos = (scp->xsize + scp->xoff)*scp->font_width - 1;
155 	if (scp->mouse_ypos > (scp->ysize + scp->yoff)*scp->font_size - 1)
156 	    scp->mouse_ypos = (scp->ysize + scp->yoff)*scp->font_size - 1;
157     }
158 
159     if (scp->mouse_xpos != scp->mouse_oldxpos || scp->mouse_ypos != scp->mouse_oldypos) {
160 	scp->status |= MOUSE_MOVED;
161     	scp->mouse_pos =
162 	    (scp->mouse_ypos/scp->font_size - scp->yoff)*scp->xsize
163 		+ scp->mouse_xpos/scp->font_width - scp->xoff;
164 #ifndef SC_NO_CUTPASTE
165 	if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING))
166 	    mouse_cut(scp);
167 #endif
168     }
169 }
170 
171 #ifndef SC_NO_CUTPASTE
172 
173 void
174 sc_draw_mouse_image(scr_stat *scp)
175 {
176     if (ISGRAPHSC(scp))
177 	return;
178 
179     ++scp->sc->videoio_in_progress;
180     (*scp->rndr->draw_mouse)(scp, scp->mouse_xpos, scp->mouse_ypos, TRUE);
181     scp->mouse_oldpos = scp->mouse_pos;
182     scp->mouse_oldxpos = scp->mouse_xpos;
183     scp->mouse_oldypos = scp->mouse_ypos;
184     scp->status |= MOUSE_VISIBLE;
185     --scp->sc->videoio_in_progress;
186 }
187 
188 void
189 sc_remove_mouse_image(scr_stat *scp)
190 {
191     int size;
192     int i;
193 
194     if (ISGRAPHSC(scp))
195 	return;
196 
197     ++scp->sc->videoio_in_progress;
198     (*scp->rndr->draw_mouse)(scp,
199 			     (scp->mouse_oldpos%scp->xsize + scp->xoff)
200 			         * scp->font_width,
201 			     (scp->mouse_oldpos/scp->xsize + scp->yoff)
202 				 * scp->font_size,
203 			     FALSE);
204     size = scp->xsize*scp->ysize;
205     i = scp->mouse_oldpos;
206     mark_for_update(scp, i);
207     mark_for_update(scp, i);
208 #ifndef PC98
209     if (i + scp->xsize + 1 < size) {
210 	mark_for_update(scp, i + scp->xsize + 1);
211     } else if (i + scp->xsize < size) {
212 	mark_for_update(scp, i + scp->xsize);
213     } else if (i + 1 < size) {
214 	mark_for_update(scp, i + 1);
215     }
216 #endif /* PC98 */
217     scp->status &= ~MOUSE_VISIBLE;
218     --scp->sc->videoio_in_progress;
219 }
220 
221 int
222 sc_inside_cutmark(scr_stat *scp, int pos)
223 {
224     int start;
225     int end;
226 
227     if (scp->mouse_cut_end < 0)
228 	return FALSE;
229     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
230 	start = scp->mouse_cut_start;
231 	end = scp->mouse_cut_end;
232     } else {
233 	start = scp->mouse_cut_end;
234 	end = scp->mouse_cut_start - 1;
235     }
236     return ((start <= pos) && (pos <= end));
237 }
238 
239 void
240 sc_remove_cutmarking(scr_stat *scp)
241 {
242     int s;
243 
244     s = spltty();
245     if (scp->mouse_cut_end >= 0) {
246 	mark_for_update(scp, scp->mouse_cut_start);
247 	mark_for_update(scp, scp->mouse_cut_end);
248     }
249     scp->mouse_cut_start = scp->xsize*scp->ysize;
250     scp->mouse_cut_end = -1;
251     splx(s);
252     scp->status &= ~MOUSE_CUTTING;
253 }
254 
255 void
256 sc_remove_all_cutmarkings(sc_softc_t *sc)
257 {
258     scr_stat *scp;
259     int i;
260 
261     /* delete cut markings in all vtys */
262     for (i = 0; i < sc->vtys; ++i) {
263 	scp = SC_STAT(sc->dev[i]);
264 	if (scp == NULL)
265 	    continue;
266 	sc_remove_cutmarking(scp);
267     }
268 }
269 
270 void
271 sc_remove_all_mouse(sc_softc_t *sc)
272 {
273     scr_stat *scp;
274     int i;
275 
276     for (i = 0; i < sc->vtys; ++i) {
277 	scp = SC_STAT(sc->dev[i]);
278 	if (scp == NULL)
279 	    continue;
280 	if (scp->status & MOUSE_VISIBLE) {
281 	    scp->status &= ~MOUSE_VISIBLE;
282 	    mark_all(scp);
283 	}
284     }
285 }
286 
287 #define IS_SPACE_CHAR(c)	(((c) & 0xff) == ' ')
288 
289 #ifdef SC_CUT_SPACES2TABS
290 #define IS_BLANK_CHAR(c)	(((c) & 0xff) == ' ' || ((c) & 0xff) == '\t')
291 #else
292 #define IS_BLANK_CHAR(c)	IS_SPACE_CHAR(c)
293 #endif /* SC_CUT_SPACES2TABS */
294 
295 #ifdef SC_CUT_SEPCHARS
296 #define IS_SEP_CHAR(c)		(index(SC_CUT_SEPCHARS, (c) & 0xff) != NULL)
297 #else
298 #define IS_SEP_CHAR(c)		IS_SPACE_CHAR(c)
299 #endif /* SC_CUT_SEPCHARS */
300 
301 /* skip spaces to right */
302 static int
303 skip_spc_right(scr_stat *scp, int p)
304 {
305     int c;
306     int i;
307 
308     for (i = p % scp->xsize; i < scp->xsize; ++i) {
309 	c = sc_vtb_getc(&scp->vtb, p);
310 	if (!IS_SPACE_CHAR(c))
311 	    break;
312 	++p;
313     }
314     return i;
315 }
316 
317 /* skip spaces to left */
318 static int
319 skip_spc_left(scr_stat *scp, int p)
320 {
321     int c;
322     int i;
323 
324     for (i = p-- % scp->xsize - 1; i >= 0; --i) {
325 	c = sc_vtb_getc(&scp->vtb, p);
326 	if (!IS_SPACE_CHAR(c))
327 	    break;
328 	--p;
329     }
330     return i;
331 }
332 
333 static void
334 mouse_do_cut(scr_stat *scp, int from, int to)
335 {
336     int blank;
337     int i;
338     int leadspaces;
339     int p;
340     int s;
341 
342     for (p = from, i = blank = leadspaces = 0; p <= to; ++p) {
343 	cut_buffer[i] = sc_vtb_getc(&scp->vtb, p);
344 	/* Be prepared that sc_vtb_getc() can return '\0' */
345 	if (cut_buffer[i] == '\0')
346 	    cut_buffer[i] = ' ';
347 #ifdef SC_CUT_SPACES2TABS
348 	if (leadspaces != -1) {
349 	    if (IS_SPACE_CHAR(cut_buffer[i])) {
350 		leadspaces++;
351 		/* Check that we are at tabstop position */
352 		if ((p % scp->xsize) % 8 == 7) {
353 		    i -= leadspaces - 1;
354 		    cut_buffer[i] = '\t';
355 		    leadspaces = 0;
356 		}
357 	    } else {
358 		leadspaces = -1;
359 	    }
360 	}
361 #endif /* SC_CUT_SPACES2TABS */
362 	/* remember the position of the last non-space char */
363 	if (!IS_BLANK_CHAR(cut_buffer[i]))
364 	    blank = i + 1;	/* the first space after the last non-space */
365 	++i;
366 	/* trim trailing blank when crossing lines */
367 	if ((p % scp->xsize) == (scp->xsize - 1)) {
368 	    cut_buffer[blank++] = '\r';
369 	    i = blank;
370 	    leadspaces = 0;
371 	}
372     }
373     cut_buffer[i] = '\0';
374 
375     /* remove the current marking */
376     s = spltty();
377     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
378 	mark_for_update(scp, scp->mouse_cut_start);
379 	mark_for_update(scp, scp->mouse_cut_end);
380     } else if (scp->mouse_cut_end >= 0) {
381 	mark_for_update(scp, scp->mouse_cut_end);
382 	mark_for_update(scp, scp->mouse_cut_start);
383     }
384 
385     /* mark the new region */
386     scp->mouse_cut_start = from;
387     scp->mouse_cut_end = to;
388     mark_for_update(scp, from);
389     mark_for_update(scp, to);
390     splx(s);
391 }
392 
393 /* copy marked region to the cut buffer */
394 static void
395 mouse_cut(scr_stat *scp)
396 {
397     int start;
398     int end;
399     int from;
400     int to;
401     int c;
402     int p;
403     int s;
404     int i;
405 
406     start = scp->mouse_cut_start;
407     end = scp->mouse_cut_end;
408     if (scp->mouse_pos >= start) {
409 	from = start;
410 	to = end = scp->mouse_pos;
411     } else {
412 	from = end = scp->mouse_pos;
413 	to = start - 1;
414     }
415     p = to;
416     for (i = p % scp->xsize; i < scp->xsize; ++i) {
417 	c = sc_vtb_getc(&scp->vtb, p);
418 	if (!IS_SPACE_CHAR(c))
419 	    break;
420 	++p;
421     }
422     /* if there is nothing but blank chars, trim them, but mark towards eol */
423     if (i == scp->xsize) {
424 	if (end >= start)
425 	    to = end = p - 1;
426 	else
427 	    to = start = p;
428     }
429     mouse_do_cut(scp, from, to);
430     s = spltty();
431     scp->mouse_cut_start = start;
432     scp->mouse_cut_end = end;
433     splx(s);
434 }
435 
436 /* a mouse button is pressed, start cut operation */
437 static void
438 mouse_cut_start(scr_stat *scp)
439 {
440     int i;
441     int s;
442 
443     if (scp->status & MOUSE_VISIBLE) {
444 	sc_remove_all_cutmarkings(scp->sc);
445 	if (scp->mouse_pos == scp->mouse_cut_start == scp->mouse_cut_end) {
446 	    cut_buffer[0] = '\0';
447 	    return;
448 	} else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) {
449 	    /* if the pointer is on trailing blank chars, mark towards eol */
450 	    i = skip_spc_left(scp, scp->mouse_pos) + 1;
451 	    s = spltty();
452 	    scp->mouse_cut_start =
453 	        (scp->mouse_pos / scp->xsize) * scp->xsize + i;
454 	    scp->mouse_cut_end =
455 	        (scp->mouse_pos / scp->xsize + 1) * scp->xsize - 1;
456 	    splx(s);
457 	    cut_buffer[0] = '\r';
458 	} else {
459 	    s = spltty();
460 	    scp->mouse_cut_start = scp->mouse_pos;
461 	    scp->mouse_cut_end = scp->mouse_cut_start;
462 	    splx(s);
463 	    cut_buffer[0] = sc_vtb_getc(&scp->vtb, scp->mouse_cut_start);
464 	}
465 	cut_buffer[1] = '\0';
466 	scp->status |= MOUSE_CUTTING;
467     	mark_all(scp);	/* this is probably overkill XXX */
468     }
469 }
470 
471 /* end of cut operation */
472 static void
473 mouse_cut_end(scr_stat *scp)
474 {
475     if (scp->status & MOUSE_VISIBLE)
476 	scp->status &= ~MOUSE_CUTTING;
477 }
478 
479 /* copy a word under the mouse pointer */
480 static void
481 mouse_cut_word(scr_stat *scp)
482 {
483     int start;
484     int end;
485     int sol;
486     int eol;
487     int c;
488     int j;
489     int len;
490 
491     /*
492      * Because we don't have locale information in the kernel,
493      * we only distinguish space char and non-space chars.  Punctuation
494      * chars, symbols and other regular chars are all treated alike
495      * unless user specified SC_CUT_SEPCHARS in his kernel config file.
496      */
497     if (scp->status & MOUSE_VISIBLE) {
498 	sol = (scp->mouse_pos / scp->xsize) * scp->xsize;
499 	eol = sol + scp->xsize;
500 	c = sc_vtb_getc(&scp->vtb, scp->mouse_pos);
501 	if (IS_SEP_CHAR(c)) {
502 	    /* blank space */
503 	    for (j = scp->mouse_pos; j >= sol; --j) {
504 		c = sc_vtb_getc(&scp->vtb, j);
505 	        if (!IS_SEP_CHAR(c))
506 		    break;
507 	    }
508 	    start = ++j;
509 	    for (j = scp->mouse_pos; j < eol; ++j) {
510 		c = sc_vtb_getc(&scp->vtb, j);
511 	        if (!IS_SEP_CHAR(c))
512 		    break;
513 	    }
514 	    end = j - 1;
515 	} else {
516 	    /* non-space word */
517 	    for (j = scp->mouse_pos; j >= sol; --j) {
518 		c = sc_vtb_getc(&scp->vtb, j);
519 	        if (IS_SEP_CHAR(c))
520 		    break;
521 	    }
522 	    start = ++j;
523 	    for (j = scp->mouse_pos; j < eol; ++j) {
524 		c = sc_vtb_getc(&scp->vtb, j);
525 	        if (IS_SEP_CHAR(c))
526 		    break;
527 	    }
528 	    end = j - 1;
529 	}
530 
531 	/* copy the found word */
532 	mouse_do_cut(scp, start, end);
533 	len = strlen(cut_buffer);
534 	if (cut_buffer[len - 1] == '\r')
535 	    cut_buffer[len - 1] = '\0';
536     }
537 }
538 
539 /* copy a line under the mouse pointer */
540 static void
541 mouse_cut_line(scr_stat *scp)
542 {
543     int len;
544     int from;
545 
546     if (scp->status & MOUSE_VISIBLE) {
547 	from = (scp->mouse_pos / scp->xsize) * scp->xsize;
548 	mouse_do_cut(scp, from, from + scp->xsize - 1);
549 	len = strlen(cut_buffer);
550 	if (cut_buffer[len - 1] == '\r')
551 	    cut_buffer[len - 1] = '\0';
552 	scp->status |= MOUSE_CUTTING;
553     }
554 }
555 
556 /* extend the marked region to the mouse pointer position */
557 static void
558 mouse_cut_extend(scr_stat *scp)
559 {
560     int start;
561     int end;
562     int s;
563 
564     if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING)
565 	&& (scp->mouse_cut_end >= 0)) {
566 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
567 	    start = scp->mouse_cut_start;
568 	    end = scp->mouse_cut_end;
569 	} else {
570 	    start = scp->mouse_cut_end;
571 	    end = scp->mouse_cut_start - 1;
572 	}
573 	s = spltty();
574 	if (scp->mouse_pos > end) {
575 	    scp->mouse_cut_start = start;
576 	    scp->mouse_cut_end = end;
577 	} else if (scp->mouse_pos < start) {
578 	    scp->mouse_cut_start = end + 1;
579 	    scp->mouse_cut_end = start;
580 	} else {
581 	    if (scp->mouse_pos - start > end + 1 - scp->mouse_pos) {
582 		scp->mouse_cut_start = start;
583 		scp->mouse_cut_end = end;
584 	    } else {
585 		scp->mouse_cut_start = end + 1;
586 		scp->mouse_cut_end = start;
587 	    }
588 	}
589 	splx(s);
590 	mouse_cut(scp);
591 	scp->status |= MOUSE_CUTTING;
592     }
593 }
594 
595 /* paste cut buffer contents into the current vty */
596 void
597 sc_mouse_paste(scr_stat *scp)
598 {
599     sc_paste(scp, cut_buffer, strlen(cut_buffer));
600 }
601 
602 #endif /* SC_NO_CUTPASTE */
603 
604 int
605 sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
606 	       struct thread *td)
607 {
608     mouse_info_t *mouse;
609     mouse_info_t buf;
610     scr_stat *cur_scp;
611     scr_stat *scp;
612     struct proc *p1;
613     int s;
614     int f;
615 
616     scp = SC_STAT(tp->t_dev);
617 
618     switch (cmd) {
619 
620     case CONS_MOUSECTL:		/* control mouse arrow */
621     case OLD_CONS_MOUSECTL:
622 
623 	mouse = (mouse_info_t*)data;
624 
625 	random_harvest(mouse, sizeof(mouse_info_t), 2, 0, RANDOM_MOUSE);
626 
627 	if (cmd == OLD_CONS_MOUSECTL) {
628 	    static u_char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
629 	    old_mouse_info_t *old_mouse = (old_mouse_info_t *)data;
630 
631 	    mouse = &buf;
632 	    mouse->operation = old_mouse->operation;
633 	    switch (mouse->operation) {
634 	    case MOUSE_MODE:
635 		mouse->u.mode = old_mouse->u.mode;
636 		break;
637 	    case MOUSE_SHOW:
638 	    case MOUSE_HIDE:
639 		break;
640 	    case MOUSE_MOVEABS:
641 	    case MOUSE_MOVEREL:
642 	    case MOUSE_ACTION:
643 		mouse->u.data.x = old_mouse->u.data.x;
644 		mouse->u.data.y = old_mouse->u.data.y;
645 		mouse->u.data.z = 0;
646 		mouse->u.data.buttons = swapb[old_mouse->u.data.buttons & 0x7];
647 		break;
648 	    case MOUSE_GETINFO:
649 		old_mouse->u.data.x = scp->mouse_xpos;
650 		old_mouse->u.data.y = scp->mouse_ypos;
651 		old_mouse->u.data.buttons = swapb[scp->mouse_buttons & 0x7];
652 		return 0;
653 	    default:
654 		return EINVAL;
655 	    }
656 	}
657 
658 	cur_scp = scp->sc->cur_scp;
659 
660 	switch (mouse->operation) {
661 	case MOUSE_MODE:
662 	    if (ISSIGVALID(mouse->u.mode.signal)) {
663 		scp->mouse_signal = mouse->u.mode.signal;
664 		scp->mouse_proc = td->td_proc;
665 		scp->mouse_pid = td->td_proc->p_pid;
666 	    }
667 	    else {
668 		scp->mouse_signal = 0;
669 		scp->mouse_proc = NULL;
670 		scp->mouse_pid = 0;
671 	    }
672 	    return 0;
673 
674 	case MOUSE_SHOW:
675 	    s = spltty();
676 	    if (!(scp->sc->flags & SC_MOUSE_ENABLED)) {
677 		scp->sc->flags |= SC_MOUSE_ENABLED;
678 		cur_scp->status &= ~MOUSE_HIDDEN;
679 		if (!ISGRAPHSC(cur_scp))
680 		    mark_all(cur_scp);
681 		splx(s);
682 		return 0;
683 	    } else {
684 		splx(s);
685 		return EINVAL;
686 	    }
687 	    break;
688 
689 	case MOUSE_HIDE:
690 	    s = spltty();
691 	    if (scp->sc->flags & SC_MOUSE_ENABLED) {
692 		scp->sc->flags &= ~SC_MOUSE_ENABLED;
693 		sc_remove_all_mouse(scp->sc);
694 		splx(s);
695 		return 0;
696 	    } else {
697 		splx(s);
698 		return EINVAL;
699 	    }
700 	    break;
701 
702 	case MOUSE_MOVEABS:
703 	    s = spltty();
704 	    scp->mouse_xpos = mouse->u.data.x;
705 	    scp->mouse_ypos = mouse->u.data.y;
706 	    set_mouse_pos(scp);
707 	    splx(s);
708 	    break;
709 
710 	case MOUSE_MOVEREL:
711 	    s = spltty();
712 	    scp->mouse_xpos += mouse->u.data.x;
713 	    scp->mouse_ypos += mouse->u.data.y;
714 	    set_mouse_pos(scp);
715 	    splx(s);
716 	    break;
717 
718 	case MOUSE_GETINFO:
719 	    mouse->u.data.x = scp->mouse_xpos;
720 	    mouse->u.data.y = scp->mouse_ypos;
721 	    mouse->u.data.z = 0;
722 	    mouse->u.data.buttons = scp->mouse_buttons;
723 	    return 0;
724 
725 	case MOUSE_ACTION:
726 	case MOUSE_MOTION_EVENT:
727 	    /* send out mouse event on /dev/sysmouse */
728 #if 0
729 	    /* this should maybe only be settable from /dev/consolectl SOS */
730 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
731 		return ENOTTY;
732 #endif
733 	    s = spltty();
734 	    if (mouse->u.data.x != 0 || mouse->u.data.y != 0) {
735 		cur_scp->mouse_xpos += mouse->u.data.x;
736 		cur_scp->mouse_ypos += mouse->u.data.y;
737 		set_mouse_pos(cur_scp);
738 	    }
739 	    f = 0;
740 	    if (mouse->operation == MOUSE_ACTION) {
741 		f = cur_scp->mouse_buttons ^ mouse->u.data.buttons;
742 		cur_scp->mouse_buttons = mouse->u.data.buttons;
743 	    }
744 	    splx(s);
745 
746 	    if (sysmouse_event(mouse) == 0)
747 		return 0;
748 
749 	    /*
750 	     * If any buttons are down or the mouse has moved a lot,
751 	     * stop the screen saver.
752 	     */
753 	    if (((mouse->operation == MOUSE_ACTION) && mouse->u.data.buttons)
754 		|| (mouse->u.data.x*mouse->u.data.x
755 			+ mouse->u.data.y*mouse->u.data.y
756 			>= SC_WAKEUP_DELTA*SC_WAKEUP_DELTA)) {
757 		sc_touch_scrn_saver();
758 	    }
759 
760 	    cur_scp->status &= ~MOUSE_HIDDEN;
761 
762 	    if (cur_scp->mouse_signal && cur_scp->mouse_proc) {
763     		/* has controlling process died? */
764 		if (cur_scp->mouse_proc != (p1 = pfind(cur_scp->mouse_pid))) {
765 		    	cur_scp->mouse_signal = 0;
766 			cur_scp->mouse_proc = NULL;
767 			cur_scp->mouse_pid = 0;
768 			if (p1)
769 			    PROC_UNLOCK(p1);
770 		} else {
771 		    psignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
772 		    PROC_UNLOCK(cur_scp->mouse_proc);
773 		    break;
774 		}
775 	    }
776 
777 #ifndef SC_NO_CUTPASTE
778 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
779 		break;
780 
781 	    if ((mouse->operation == MOUSE_ACTION) && f) {
782 		/* process button presses */
783 		if (cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN)
784 		    mouse_cut_start(cur_scp);
785 		else
786 		    mouse_cut_end(cur_scp);
787 		if (cur_scp->mouse_buttons & MOUSE_BUTTON2DOWN ||
788 		    cur_scp->mouse_buttons & MOUSE_BUTTON3DOWN)
789 		    sc_mouse_paste(cur_scp);
790 	    }
791 #endif /* SC_NO_CUTPASTE */
792 	    break;
793 
794 	case MOUSE_BUTTON_EVENT:
795 	    if ((mouse->u.event.id & MOUSE_BUTTONS) == 0)
796 		return EINVAL;
797 	    if (mouse->u.event.value < 0)
798 		return EINVAL;
799 #if 0
800 	    /* this should maybe only be settable from /dev/consolectl SOS */
801 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
802 		return ENOTTY;
803 #endif
804 	    if (mouse->u.event.value > 0)
805 		cur_scp->mouse_buttons |= mouse->u.event.id;
806 	    else
807 		cur_scp->mouse_buttons &= ~mouse->u.event.id;
808 
809 	    if (sysmouse_event(mouse) == 0)
810 		return 0;
811 
812 	    /* if a button is held down, stop the screen saver */
813 	    if (mouse->u.event.value > 0)
814 		sc_touch_scrn_saver();
815 
816 	    cur_scp->status &= ~MOUSE_HIDDEN;
817 
818 	    if (cur_scp->mouse_signal && cur_scp->mouse_proc) {
819 		if (cur_scp->mouse_proc != (p1 = pfind(cur_scp->mouse_pid))){
820 		    	cur_scp->mouse_signal = 0;
821 			cur_scp->mouse_proc = NULL;
822 			cur_scp->mouse_pid = 0;
823 			if (p1)
824 			    PROC_UNLOCK(p1);
825 		} else {
826 		    psignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
827 		    PROC_UNLOCK(cur_scp->mouse_proc);
828 		    break;
829 		}
830 	    }
831 
832 #ifndef SC_NO_CUTPASTE
833 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
834 		break;
835 
836 	    switch (mouse->u.event.id) {
837 	    case MOUSE_BUTTON1DOWN:
838 	        switch (mouse->u.event.value % 4) {
839 		case 0:	/* up */
840 		    mouse_cut_end(cur_scp);
841 		    break;
842 		case 1: /* single click: start cut operation */
843 		    mouse_cut_start(cur_scp);
844 		    break;
845 		case 2:	/* double click: cut a word */
846 		    mouse_cut_word(cur_scp);
847 		    mouse_cut_end(cur_scp);
848 		    break;
849 		case 3:	/* triple click: cut a line */
850 		    mouse_cut_line(cur_scp);
851 		    mouse_cut_end(cur_scp);
852 		    break;
853 		}
854 		break;
855 	    case SC_MOUSE_PASTEBUTTON:
856 	        switch (mouse->u.event.value) {
857 		case 0:	/* up */
858 		    break;
859 		default:
860 		    sc_mouse_paste(cur_scp);
861 		    break;
862 		}
863 		break;
864 	    case SC_MOUSE_EXTENDBUTTON:
865 	        switch (mouse->u.event.value) {
866 		case 0:	/* up */
867 		    if (!(cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN))
868 		        mouse_cut_end(cur_scp);
869 		    break;
870 		default:
871 		    mouse_cut_extend(cur_scp);
872 		    break;
873 		}
874 		break;
875 	    }
876 #endif /* SC_NO_CUTPASTE */
877 	    break;
878 
879 	case MOUSE_MOUSECHAR:
880 	    if (mouse->u.mouse_char < 0) {
881 		mouse->u.mouse_char = scp->sc->mouse_char;
882 	    } else {
883 		if (mouse->u.mouse_char > UCHAR_MAX - 3)
884 		    return EINVAL;
885 		s = spltty();
886 		sc_remove_all_mouse(scp->sc);
887 #ifndef SC_NO_FONT_LOADING
888 		if (ISTEXTSC(cur_scp) && (cur_scp->font != NULL))
889 		    sc_load_font(cur_scp, 0, cur_scp->font_size,
890 				 cur_scp->font + cur_scp->font_size
891 				 * cur_scp->sc->mouse_char,
892 				 cur_scp->sc->mouse_char, 4);
893 #endif
894 		scp->sc->mouse_char = mouse->u.mouse_char;
895 		splx(s);
896 	    }
897 	    break;
898 
899 	default:
900 	    return EINVAL;
901 	}
902 
903 	return 0;
904     }
905 
906     return ENOIOCTL;
907 }
908 
909 #endif /* SC_NO_SYSMOUSE */
910