xref: /freebsd/sys/dev/syscons/scmouse.c (revision 390e8cc2974df1888369c06339ef8e0e92b312b6)
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/8 - scp->xoff;
133     scp->status |= MOUSE_MOVED;
134     splx(s);
135 }
136 
137 /* adjust mouse position */
138 static void
139 set_mouse_pos(scr_stat *scp)
140 {
141     if (scp->mouse_xpos < scp->xoff*8)
142 	scp->mouse_xpos = scp->xoff*8;
143     if (scp->mouse_ypos < scp->yoff*scp->font_size)
144 	scp->mouse_ypos = scp->yoff*scp->font_size;
145     if (ISGRAPHSC(scp)) {
146         if (scp->mouse_xpos > scp->xpixel-1)
147 	    scp->mouse_xpos = scp->xpixel-1;
148         if (scp->mouse_ypos > scp->ypixel-1)
149 	    scp->mouse_ypos = scp->ypixel-1;
150 	return;
151     } else {
152 	if (scp->mouse_xpos > (scp->xsize + scp->xoff)*8 - 1)
153 	    scp->mouse_xpos = (scp->xsize + scp->xoff)*8 - 1;
154 	if (scp->mouse_ypos > (scp->ysize + scp->yoff)*scp->font_size - 1)
155 	    scp->mouse_ypos = (scp->ysize + scp->yoff)*scp->font_size - 1;
156     }
157 
158     if (scp->mouse_xpos != scp->mouse_oldxpos || scp->mouse_ypos != scp->mouse_oldypos) {
159 	scp->status |= MOUSE_MOVED;
160     	scp->mouse_pos =
161 	    (scp->mouse_ypos/scp->font_size - scp->yoff)*scp->xsize
162 		+ scp->mouse_xpos/8 - scp->xoff;
163 #ifndef SC_NO_CUTPASTE
164 	if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING))
165 	    mouse_cut(scp);
166 #endif
167     }
168 }
169 
170 #ifndef SC_NO_CUTPASTE
171 
172 void
173 sc_draw_mouse_image(scr_stat *scp)
174 {
175     if (ISGRAPHSC(scp))
176 	return;
177 
178     ++scp->sc->videoio_in_progress;
179     (*scp->rndr->draw_mouse)(scp, scp->mouse_xpos, scp->mouse_ypos, TRUE);
180     scp->mouse_oldpos = scp->mouse_pos;
181     scp->mouse_oldxpos = scp->mouse_xpos;
182     scp->mouse_oldypos = scp->mouse_ypos;
183     scp->status |= MOUSE_VISIBLE;
184     --scp->sc->videoio_in_progress;
185 }
186 
187 void
188 sc_remove_mouse_image(scr_stat *scp)
189 {
190     int size;
191     int i;
192 
193     if (ISGRAPHSC(scp))
194 	return;
195 
196     ++scp->sc->videoio_in_progress;
197     (*scp->rndr->draw_mouse)(scp,
198 			     (scp->mouse_oldpos%scp->xsize + scp->xoff)*8,
199 			     (scp->mouse_oldpos/scp->xsize + scp->yoff)
200 				 * scp->font_size,
201 			     FALSE);
202     size = scp->xsize*scp->ysize;
203     i = scp->mouse_oldpos;
204     mark_for_update(scp, i);
205     mark_for_update(scp, i);
206 #ifndef PC98
207     if (i + scp->xsize + 1 < size) {
208 	mark_for_update(scp, i + scp->xsize + 1);
209     } else if (i + scp->xsize < size) {
210 	mark_for_update(scp, i + scp->xsize);
211     } else if (i + 1 < size) {
212 	mark_for_update(scp, i + 1);
213     }
214 #endif /* PC98 */
215     scp->status &= ~MOUSE_VISIBLE;
216     --scp->sc->videoio_in_progress;
217 }
218 
219 int
220 sc_inside_cutmark(scr_stat *scp, int pos)
221 {
222     int start;
223     int end;
224 
225     if (scp->mouse_cut_end < 0)
226 	return FALSE;
227     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
228 	start = scp->mouse_cut_start;
229 	end = scp->mouse_cut_end;
230     } else {
231 	start = scp->mouse_cut_end;
232 	end = scp->mouse_cut_start - 1;
233     }
234     return ((start <= pos) && (pos <= end));
235 }
236 
237 void
238 sc_remove_cutmarking(scr_stat *scp)
239 {
240     int s;
241 
242     s = spltty();
243     if (scp->mouse_cut_end >= 0) {
244 	mark_for_update(scp, scp->mouse_cut_start);
245 	mark_for_update(scp, scp->mouse_cut_end);
246     }
247     scp->mouse_cut_start = scp->xsize*scp->ysize;
248     scp->mouse_cut_end = -1;
249     splx(s);
250     scp->status &= ~MOUSE_CUTTING;
251 }
252 
253 void
254 sc_remove_all_cutmarkings(sc_softc_t *sc)
255 {
256     scr_stat *scp;
257     int i;
258 
259     /* delete cut markings in all vtys */
260     for (i = 0; i < sc->vtys; ++i) {
261 	scp = SC_STAT(sc->dev[i]);
262 	if (scp == NULL)
263 	    continue;
264 	sc_remove_cutmarking(scp);
265     }
266 }
267 
268 void
269 sc_remove_all_mouse(sc_softc_t *sc)
270 {
271     scr_stat *scp;
272     int i;
273 
274     for (i = 0; i < sc->vtys; ++i) {
275 	scp = SC_STAT(sc->dev[i]);
276 	if (scp == NULL)
277 	    continue;
278 	if (scp->status & MOUSE_VISIBLE) {
279 	    scp->status &= ~MOUSE_VISIBLE;
280 	    mark_all(scp);
281 	}
282     }
283 }
284 
285 #define IS_SPACE_CHAR(c)	(((c) & 0xff) == ' ')
286 
287 #ifdef SC_CUT_SPACES2TABS
288 #define IS_BLANK_CHAR(c)	(((c) & 0xff) == ' ' || ((c) & 0xff) == '\t')
289 #else
290 #define IS_BLANK_CHAR(c)	IS_SPACE_CHAR(c)
291 #endif /* SC_CUT_SPACES2TABS */
292 
293 #ifdef SC_CUT_SEPCHARS
294 #define IS_SEP_CHAR(c)		(index(SC_CUT_SEPCHARS, (c) & 0xff) != NULL)
295 #else
296 #define IS_SEP_CHAR(c)		IS_SPACE_CHAR(c)
297 #endif /* SC_CUT_SEPCHARS */
298 
299 /* skip spaces to right */
300 static int
301 skip_spc_right(scr_stat *scp, int p)
302 {
303     int c;
304     int i;
305 
306     for (i = p % scp->xsize; i < scp->xsize; ++i) {
307 	c = sc_vtb_getc(&scp->vtb, p);
308 	if (!IS_SPACE_CHAR(c))
309 	    break;
310 	++p;
311     }
312     return i;
313 }
314 
315 /* skip spaces to left */
316 static int
317 skip_spc_left(scr_stat *scp, int p)
318 {
319     int c;
320     int i;
321 
322     for (i = p-- % scp->xsize - 1; i >= 0; --i) {
323 	c = sc_vtb_getc(&scp->vtb, p);
324 	if (!IS_SPACE_CHAR(c))
325 	    break;
326 	--p;
327     }
328     return i;
329 }
330 
331 static void
332 mouse_do_cut(scr_stat *scp, int from, int to)
333 {
334     int blank;
335     int i;
336     int leadspaces;
337     int p;
338     int s;
339 
340     for (p = from, i = blank = leadspaces = 0; p <= to; ++p) {
341 	cut_buffer[i] = sc_vtb_getc(&scp->vtb, p);
342 	/* Be prepared that sc_vtb_getc() can return '\0' */
343 	if (cut_buffer[i] == '\0')
344 	    cut_buffer[i] = ' ';
345 #ifdef SC_CUT_SPACES2TABS
346 	if (leadspaces != -1) {
347 	    if (IS_SPACE_CHAR(cut_buffer[i])) {
348 		leadspaces++;
349 		/* Check that we are at tabstop position */
350 		if ((p % scp->xsize) % 8 == 7) {
351 		    i -= leadspaces - 1;
352 		    cut_buffer[i] = '\t';
353 		    leadspaces = 0;
354 		}
355 	    } else {
356 		leadspaces = -1;
357 	    }
358 	}
359 #endif /* SC_CUT_SPACES2TABS */
360 	/* remember the position of the last non-space char */
361 	if (!IS_BLANK_CHAR(cut_buffer[i]))
362 	    blank = i + 1;	/* the first space after the last non-space */
363 	++i;
364 	/* trim trailing blank when crossing lines */
365 	if ((p % scp->xsize) == (scp->xsize - 1)) {
366 	    cut_buffer[blank++] = '\r';
367 	    i = blank;
368 	    leadspaces = 0;
369 	}
370     }
371     cut_buffer[i] = '\0';
372 
373     /* remove the current marking */
374     s = spltty();
375     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
376 	mark_for_update(scp, scp->mouse_cut_start);
377 	mark_for_update(scp, scp->mouse_cut_end);
378     } else if (scp->mouse_cut_end >= 0) {
379 	mark_for_update(scp, scp->mouse_cut_end);
380 	mark_for_update(scp, scp->mouse_cut_start);
381     }
382 
383     /* mark the new region */
384     scp->mouse_cut_start = from;
385     scp->mouse_cut_end = to;
386     mark_for_update(scp, from);
387     mark_for_update(scp, to);
388     splx(s);
389 }
390 
391 /* copy marked region to the cut buffer */
392 static void
393 mouse_cut(scr_stat *scp)
394 {
395     int start;
396     int end;
397     int from;
398     int to;
399     int c;
400     int p;
401     int s;
402     int i;
403 
404     start = scp->mouse_cut_start;
405     end = scp->mouse_cut_end;
406     if (scp->mouse_pos >= start) {
407 	from = start;
408 	to = end = scp->mouse_pos;
409     } else {
410 	from = end = scp->mouse_pos;
411 	to = start - 1;
412     }
413     p = to;
414     for (i = p % scp->xsize; i < scp->xsize; ++i) {
415 	c = sc_vtb_getc(&scp->vtb, p);
416 	if (!IS_SPACE_CHAR(c))
417 	    break;
418 	++p;
419     }
420     /* if there is nothing but blank chars, trim them, but mark towards eol */
421     if (i == scp->xsize) {
422 	if (end >= start)
423 	    to = end = p - 1;
424 	else
425 	    to = start = p;
426     }
427     mouse_do_cut(scp, from, to);
428     s = spltty();
429     scp->mouse_cut_start = start;
430     scp->mouse_cut_end = end;
431     splx(s);
432 }
433 
434 /* a mouse button is pressed, start cut operation */
435 static void
436 mouse_cut_start(scr_stat *scp)
437 {
438     int i;
439     int s;
440 
441     if (scp->status & MOUSE_VISIBLE) {
442 	sc_remove_all_cutmarkings(scp->sc);
443 	if (scp->mouse_pos == scp->mouse_cut_start == scp->mouse_cut_end) {
444 	    cut_buffer[0] = '\0';
445 	    return;
446 	} else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) {
447 	    /* if the pointer is on trailing blank chars, mark towards eol */
448 	    i = skip_spc_left(scp, scp->mouse_pos) + 1;
449 	    s = spltty();
450 	    scp->mouse_cut_start =
451 	        (scp->mouse_pos / scp->xsize) * scp->xsize + i;
452 	    scp->mouse_cut_end =
453 	        (scp->mouse_pos / scp->xsize + 1) * scp->xsize - 1;
454 	    splx(s);
455 	    cut_buffer[0] = '\r';
456 	} else {
457 	    s = spltty();
458 	    scp->mouse_cut_start = scp->mouse_pos;
459 	    scp->mouse_cut_end = scp->mouse_cut_start;
460 	    splx(s);
461 	    cut_buffer[0] = sc_vtb_getc(&scp->vtb, scp->mouse_cut_start);
462 	}
463 	cut_buffer[1] = '\0';
464 	scp->status |= MOUSE_CUTTING;
465     	mark_all(scp);	/* this is probably overkill XXX */
466     }
467 }
468 
469 /* end of cut operation */
470 static void
471 mouse_cut_end(scr_stat *scp)
472 {
473     if (scp->status & MOUSE_VISIBLE)
474 	scp->status &= ~MOUSE_CUTTING;
475 }
476 
477 /* copy a word under the mouse pointer */
478 static void
479 mouse_cut_word(scr_stat *scp)
480 {
481     int start;
482     int end;
483     int sol;
484     int eol;
485     int c;
486     int j;
487     int len;
488 
489     /*
490      * Because we don't have locale information in the kernel,
491      * we only distinguish space char and non-space chars.  Punctuation
492      * chars, symbols and other regular chars are all treated alike
493      * unless user specified SC_CUT_SEPCHARS in his kernel config file.
494      */
495     if (scp->status & MOUSE_VISIBLE) {
496 	sol = (scp->mouse_pos / scp->xsize) * scp->xsize;
497 	eol = sol + scp->xsize;
498 	c = sc_vtb_getc(&scp->vtb, scp->mouse_pos);
499 	if (IS_SEP_CHAR(c)) {
500 	    /* blank space */
501 	    for (j = scp->mouse_pos; j >= sol; --j) {
502 		c = sc_vtb_getc(&scp->vtb, j);
503 	        if (!IS_SEP_CHAR(c))
504 		    break;
505 	    }
506 	    start = ++j;
507 	    for (j = scp->mouse_pos; j < eol; ++j) {
508 		c = sc_vtb_getc(&scp->vtb, j);
509 	        if (!IS_SEP_CHAR(c))
510 		    break;
511 	    }
512 	    end = j - 1;
513 	} else {
514 	    /* non-space word */
515 	    for (j = scp->mouse_pos; j >= sol; --j) {
516 		c = sc_vtb_getc(&scp->vtb, j);
517 	        if (IS_SEP_CHAR(c))
518 		    break;
519 	    }
520 	    start = ++j;
521 	    for (j = scp->mouse_pos; j < eol; ++j) {
522 		c = sc_vtb_getc(&scp->vtb, j);
523 	        if (IS_SEP_CHAR(c))
524 		    break;
525 	    }
526 	    end = j - 1;
527 	}
528 
529 	/* copy the found word */
530 	mouse_do_cut(scp, start, end);
531 	len = strlen(cut_buffer);
532 	if (cut_buffer[len - 1] == '\r')
533 	    cut_buffer[len - 1] = '\0';
534     }
535 }
536 
537 /* copy a line under the mouse pointer */
538 static void
539 mouse_cut_line(scr_stat *scp)
540 {
541     int len;
542     int from;
543 
544     if (scp->status & MOUSE_VISIBLE) {
545 	from = (scp->mouse_pos / scp->xsize) * scp->xsize;
546 	mouse_do_cut(scp, from, from + scp->xsize - 1);
547 	len = strlen(cut_buffer);
548 	if (cut_buffer[len - 1] == '\r')
549 	    cut_buffer[len - 1] = '\0';
550 	scp->status |= MOUSE_CUTTING;
551     }
552 }
553 
554 /* extend the marked region to the mouse pointer position */
555 static void
556 mouse_cut_extend(scr_stat *scp)
557 {
558     int start;
559     int end;
560     int s;
561 
562     if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING)
563 	&& (scp->mouse_cut_end >= 0)) {
564 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
565 	    start = scp->mouse_cut_start;
566 	    end = scp->mouse_cut_end;
567 	} else {
568 	    start = scp->mouse_cut_end;
569 	    end = scp->mouse_cut_start - 1;
570 	}
571 	s = spltty();
572 	if (scp->mouse_pos > end) {
573 	    scp->mouse_cut_start = start;
574 	    scp->mouse_cut_end = end;
575 	} else if (scp->mouse_pos < start) {
576 	    scp->mouse_cut_start = end + 1;
577 	    scp->mouse_cut_end = start;
578 	} else {
579 	    if (scp->mouse_pos - start > end + 1 - scp->mouse_pos) {
580 		scp->mouse_cut_start = start;
581 		scp->mouse_cut_end = end;
582 	    } else {
583 		scp->mouse_cut_start = end + 1;
584 		scp->mouse_cut_end = start;
585 	    }
586 	}
587 	splx(s);
588 	mouse_cut(scp);
589 	scp->status |= MOUSE_CUTTING;
590     }
591 }
592 
593 /* paste cut buffer contents into the current vty */
594 void
595 sc_mouse_paste(scr_stat *scp)
596 {
597     sc_paste(scp, cut_buffer, strlen(cut_buffer));
598 }
599 
600 #endif /* SC_NO_CUTPASTE */
601 
602 int
603 sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
604 	       struct thread *td)
605 {
606     mouse_info_t *mouse;
607     mouse_info_t buf;
608     scr_stat *cur_scp;
609     scr_stat *scp;
610     struct proc *p1;
611     int s;
612     int f;
613 
614     scp = SC_STAT(tp->t_dev);
615 
616     switch (cmd) {
617 
618     case CONS_MOUSECTL:		/* control mouse arrow */
619     case OLD_CONS_MOUSECTL:
620 
621 	mouse = (mouse_info_t*)data;
622 
623 	random_harvest(mouse, sizeof(mouse_info_t), 2, 0, RANDOM_MOUSE);
624 
625 	if (cmd == OLD_CONS_MOUSECTL) {
626 	    static u_char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
627 	    old_mouse_info_t *old_mouse = (old_mouse_info_t *)data;
628 
629 	    mouse = &buf;
630 	    mouse->operation = old_mouse->operation;
631 	    switch (mouse->operation) {
632 	    case MOUSE_MODE:
633 		mouse->u.mode = old_mouse->u.mode;
634 		break;
635 	    case MOUSE_SHOW:
636 	    case MOUSE_HIDE:
637 		break;
638 	    case MOUSE_MOVEABS:
639 	    case MOUSE_MOVEREL:
640 	    case MOUSE_ACTION:
641 		mouse->u.data.x = old_mouse->u.data.x;
642 		mouse->u.data.y = old_mouse->u.data.y;
643 		mouse->u.data.z = 0;
644 		mouse->u.data.buttons = swapb[old_mouse->u.data.buttons & 0x7];
645 		break;
646 	    case MOUSE_GETINFO:
647 		old_mouse->u.data.x = scp->mouse_xpos;
648 		old_mouse->u.data.y = scp->mouse_ypos;
649 		old_mouse->u.data.buttons = swapb[scp->mouse_buttons & 0x7];
650 		return 0;
651 	    default:
652 		return EINVAL;
653 	    }
654 	}
655 
656 	cur_scp = scp->sc->cur_scp;
657 
658 	switch (mouse->operation) {
659 	case MOUSE_MODE:
660 	    if (ISSIGVALID(mouse->u.mode.signal)) {
661 		scp->mouse_signal = mouse->u.mode.signal;
662 		scp->mouse_proc = td->td_proc;
663 		scp->mouse_pid = td->td_proc->p_pid;
664 	    }
665 	    else {
666 		scp->mouse_signal = 0;
667 		scp->mouse_proc = NULL;
668 		scp->mouse_pid = 0;
669 	    }
670 	    return 0;
671 
672 	case MOUSE_SHOW:
673 	    s = spltty();
674 	    if (!(scp->sc->flags & SC_MOUSE_ENABLED)) {
675 		scp->sc->flags |= SC_MOUSE_ENABLED;
676 		cur_scp->status &= ~MOUSE_HIDDEN;
677 		if (!ISGRAPHSC(cur_scp))
678 		    mark_all(cur_scp);
679 		splx(s);
680 		return 0;
681 	    } else {
682 		splx(s);
683 		return EINVAL;
684 	    }
685 	    break;
686 
687 	case MOUSE_HIDE:
688 	    s = spltty();
689 	    if (scp->sc->flags & SC_MOUSE_ENABLED) {
690 		scp->sc->flags &= ~SC_MOUSE_ENABLED;
691 		sc_remove_all_mouse(scp->sc);
692 		splx(s);
693 		return 0;
694 	    } else {
695 		splx(s);
696 		return EINVAL;
697 	    }
698 	    break;
699 
700 	case MOUSE_MOVEABS:
701 	    s = spltty();
702 	    scp->mouse_xpos = mouse->u.data.x;
703 	    scp->mouse_ypos = mouse->u.data.y;
704 	    set_mouse_pos(scp);
705 	    splx(s);
706 	    break;
707 
708 	case MOUSE_MOVEREL:
709 	    s = spltty();
710 	    scp->mouse_xpos += mouse->u.data.x;
711 	    scp->mouse_ypos += mouse->u.data.y;
712 	    set_mouse_pos(scp);
713 	    splx(s);
714 	    break;
715 
716 	case MOUSE_GETINFO:
717 	    mouse->u.data.x = scp->mouse_xpos;
718 	    mouse->u.data.y = scp->mouse_ypos;
719 	    mouse->u.data.z = 0;
720 	    mouse->u.data.buttons = scp->mouse_buttons;
721 	    return 0;
722 
723 	case MOUSE_ACTION:
724 	case MOUSE_MOTION_EVENT:
725 	    /* send out mouse event on /dev/sysmouse */
726 #if 0
727 	    /* this should maybe only be settable from /dev/consolectl SOS */
728 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
729 		return ENOTTY;
730 #endif
731 	    s = spltty();
732 	    if (mouse->u.data.x != 0 || mouse->u.data.y != 0) {
733 		cur_scp->mouse_xpos += mouse->u.data.x;
734 		cur_scp->mouse_ypos += mouse->u.data.y;
735 		set_mouse_pos(cur_scp);
736 	    }
737 	    f = 0;
738 	    if (mouse->operation == MOUSE_ACTION) {
739 		f = cur_scp->mouse_buttons ^ mouse->u.data.buttons;
740 		cur_scp->mouse_buttons = mouse->u.data.buttons;
741 	    }
742 	    splx(s);
743 
744 	    if (sysmouse_event(mouse) == 0)
745 		return 0;
746 
747 	    /*
748 	     * If any buttons are down or the mouse has moved a lot,
749 	     * stop the screen saver.
750 	     */
751 	    if (((mouse->operation == MOUSE_ACTION) && mouse->u.data.buttons)
752 		|| (mouse->u.data.x*mouse->u.data.x
753 			+ mouse->u.data.y*mouse->u.data.y
754 			>= SC_WAKEUP_DELTA*SC_WAKEUP_DELTA)) {
755 		sc_touch_scrn_saver();
756 	    }
757 
758 	    cur_scp->status &= ~MOUSE_HIDDEN;
759 
760 	    if (cur_scp->mouse_signal && cur_scp->mouse_proc) {
761     		/* has controlling process died? */
762 		if (cur_scp->mouse_proc != (p1 = pfind(cur_scp->mouse_pid))) {
763 		    	cur_scp->mouse_signal = 0;
764 			cur_scp->mouse_proc = NULL;
765 			cur_scp->mouse_pid = 0;
766 			if (p1)
767 			    PROC_UNLOCK(p1);
768 		} else {
769 		    psignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
770 		    PROC_UNLOCK(cur_scp->mouse_proc);
771 		    break;
772 		}
773 	    }
774 
775 #ifndef SC_NO_CUTPASTE
776 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
777 		break;
778 
779 	    if ((mouse->operation == MOUSE_ACTION) && f) {
780 		/* process button presses */
781 		if (cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN)
782 		    mouse_cut_start(cur_scp);
783 		else
784 		    mouse_cut_end(cur_scp);
785 		if (cur_scp->mouse_buttons & MOUSE_BUTTON2DOWN ||
786 		    cur_scp->mouse_buttons & MOUSE_BUTTON3DOWN)
787 		    sc_mouse_paste(cur_scp);
788 	    }
789 #endif /* SC_NO_CUTPASTE */
790 	    break;
791 
792 	case MOUSE_BUTTON_EVENT:
793 	    if ((mouse->u.event.id & MOUSE_BUTTONS) == 0)
794 		return EINVAL;
795 	    if (mouse->u.event.value < 0)
796 		return EINVAL;
797 #if 0
798 	    /* this should maybe only be settable from /dev/consolectl SOS */
799 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
800 		return ENOTTY;
801 #endif
802 	    if (mouse->u.event.value > 0)
803 		cur_scp->mouse_buttons |= mouse->u.event.id;
804 	    else
805 		cur_scp->mouse_buttons &= ~mouse->u.event.id;
806 
807 	    if (sysmouse_event(mouse) == 0)
808 		return 0;
809 
810 	    /* if a button is held down, stop the screen saver */
811 	    if (mouse->u.event.value > 0)
812 		sc_touch_scrn_saver();
813 
814 	    cur_scp->status &= ~MOUSE_HIDDEN;
815 
816 	    if (cur_scp->mouse_signal && cur_scp->mouse_proc) {
817 		if (cur_scp->mouse_proc != (p1 = pfind(cur_scp->mouse_pid))){
818 		    	cur_scp->mouse_signal = 0;
819 			cur_scp->mouse_proc = NULL;
820 			cur_scp->mouse_pid = 0;
821 			if (p1)
822 			    PROC_UNLOCK(p1);
823 		} else {
824 		    psignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
825 		    PROC_UNLOCK(cur_scp->mouse_proc);
826 		    break;
827 		}
828 	    }
829 
830 #ifndef SC_NO_CUTPASTE
831 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
832 		break;
833 
834 	    switch (mouse->u.event.id) {
835 	    case MOUSE_BUTTON1DOWN:
836 	        switch (mouse->u.event.value % 4) {
837 		case 0:	/* up */
838 		    mouse_cut_end(cur_scp);
839 		    break;
840 		case 1: /* single click: start cut operation */
841 		    mouse_cut_start(cur_scp);
842 		    break;
843 		case 2:	/* double click: cut a word */
844 		    mouse_cut_word(cur_scp);
845 		    mouse_cut_end(cur_scp);
846 		    break;
847 		case 3:	/* triple click: cut a line */
848 		    mouse_cut_line(cur_scp);
849 		    mouse_cut_end(cur_scp);
850 		    break;
851 		}
852 		break;
853 	    case SC_MOUSE_PASTEBUTTON:
854 	        switch (mouse->u.event.value) {
855 		case 0:	/* up */
856 		    break;
857 		default:
858 		    sc_mouse_paste(cur_scp);
859 		    break;
860 		}
861 		break;
862 	    case SC_MOUSE_EXTENDBUTTON:
863 	        switch (mouse->u.event.value) {
864 		case 0:	/* up */
865 		    if (!(cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN))
866 		        mouse_cut_end(cur_scp);
867 		    break;
868 		default:
869 		    mouse_cut_extend(cur_scp);
870 		    break;
871 		}
872 		break;
873 	    }
874 #endif /* SC_NO_CUTPASTE */
875 	    break;
876 
877 	case MOUSE_MOUSECHAR:
878 	    if (mouse->u.mouse_char < 0) {
879 		mouse->u.mouse_char = scp->sc->mouse_char;
880 	    } else {
881 		if (mouse->u.mouse_char > UCHAR_MAX - 3)
882 		    return EINVAL;
883 		s = spltty();
884 		sc_remove_all_mouse(scp->sc);
885 #ifndef SC_NO_FONT_LOADING
886 		if (ISTEXTSC(cur_scp) && (cur_scp->font != NULL))
887 		    sc_load_font(cur_scp, 0, cur_scp->font_size,
888 				 cur_scp->font + cur_scp->font_size
889 				 * cur_scp->sc->mouse_char,
890 				 cur_scp->sc->mouse_char, 4);
891 #endif
892 		scp->sc->mouse_char = mouse->u.mouse_char;
893 		splx(s);
894 	    }
895 	    break;
896 
897 	default:
898 	    return EINVAL;
899 	}
900 
901 	return 0;
902     }
903 
904     return ENOIOCTL;
905 }
906 
907 #endif /* SC_NO_SYSMOUSE */
908