xref: /freebsd/sys/dev/syscons/scmouse.c (revision 5129159789cc9d7bc514e4546b88e3427695002d)
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 "sc.h"
30 #include "opt_syscons.h"
31 
32 #if NSC > 0
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/conf.h>
37 #include <sys/signalvar.h>
38 #include <sys/proc.h>
39 #include <sys/tty.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 
43 #include <machine/console.h>
44 #include <machine/mouse.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 /* local variables */
76 #ifndef SC_NO_SYSMOUSE
77 static int		mouse_level;		/* sysmouse protocol level */
78 static mousestatus_t	mouse_status = { 0, 0, 0, 0, 0, 0 };
79 static int		cut_buffer_size;
80 static u_char		*cut_buffer;
81 #endif /* SC_NO_SYSMOUE */
82 
83 /* local functions */
84 #ifndef SC_NO_SYSMOUSE
85 static void set_mouse_pos(scr_stat *scp);
86 #ifndef SC_NO_CUTPASTE
87 static int skip_spc_right(scr_stat *scp, int p);
88 static int skip_spc_left(scr_stat *scp, int p);
89 static void mouse_cut(scr_stat *scp);
90 static void mouse_cut_start(scr_stat *scp);
91 static void mouse_cut_end(scr_stat *scp);
92 static void mouse_cut_word(scr_stat *scp);
93 static void mouse_cut_line(scr_stat *scp);
94 static void mouse_cut_extend(scr_stat *scp);
95 static void mouse_paste(scr_stat *scp);
96 #endif /* SC_NO_CUTPASTE */
97 #endif /* SC_NO_SYSMOUE */
98 
99 #ifndef SC_NO_CUTPASTE
100 /* allocate a cut buffer */
101 void
102 sc_alloc_cut_buffer(scr_stat *scp, int wait)
103 {
104     u_char *p;
105 
106     if ((cut_buffer == NULL)
107 	|| (cut_buffer_size < scp->xsize * scp->ysize + 1)) {
108 	p = cut_buffer;
109 	cut_buffer = NULL;
110 	if (p != NULL)
111 	    free(p, M_DEVBUF);
112 	cut_buffer_size = scp->xsize * scp->ysize + 1;
113 	p = (u_char *)malloc(cut_buffer_size,
114 			     M_DEVBUF, (wait) ? M_WAITOK : M_NOWAIT);
115 	if (p != NULL)
116 	    p[0] = '\0';
117 	cut_buffer = p;
118     }
119 }
120 #endif /* SC_NO_CUTPASTE */
121 
122 #ifndef SC_NO_SYSMOUSE
123 
124 /* modify the sysmouse software level */
125 void
126 sc_mouse_set_level(int level)
127 {
128     mouse_level = level;
129 }
130 
131 /* move mouse */
132 void
133 sc_mouse_move(scr_stat *scp, int x, int y)
134 {
135     int s;
136 
137     s = spltty();
138     scp->mouse_xpos = x;
139     scp->mouse_ypos = y;
140     scp->mouse_pos = scp->mouse_oldpos =
141 	(y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff;
142     splx(s);
143 }
144 
145 /* adjust mouse position */
146 static void
147 set_mouse_pos(scr_stat *scp)
148 {
149     static int last_xpos = -1, last_ypos = -1;
150 
151     if (scp->mouse_xpos < scp->xoff*8)
152 	scp->mouse_xpos = scp->xoff*8;
153     if (scp->mouse_ypos < scp->yoff*scp->font_size)
154 	scp->mouse_ypos = scp->yoff*scp->font_size;
155     if (ISGRAPHSC(scp)) {
156         if (scp->mouse_xpos > scp->xpixel-1)
157 	    scp->mouse_xpos = scp->xpixel-1;
158         if (scp->mouse_ypos > scp->ypixel-1)
159 	    scp->mouse_ypos = scp->ypixel-1;
160 	return;
161     } else {
162 	if (scp->mouse_xpos > (scp->xsize + scp->xoff)*8 - 1)
163 	    scp->mouse_xpos = (scp->xsize + scp->xoff)*8 - 1;
164 	if (scp->mouse_ypos > (scp->ysize + scp->yoff)*scp->font_size - 1)
165 	    scp->mouse_ypos = (scp->ysize + scp->yoff)*scp->font_size - 1;
166     }
167 
168     if (scp->mouse_xpos != last_xpos || scp->mouse_ypos != last_ypos) {
169 	scp->status |= MOUSE_MOVED;
170     	scp->mouse_pos =
171 	    (scp->mouse_ypos/scp->font_size - scp->yoff)*scp->xsize
172 		+ scp->mouse_xpos/8 - scp->xoff;
173 #ifndef SC_NO_CUTPASTE
174 	if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING))
175 	    mouse_cut(scp);
176 #endif
177     }
178 }
179 
180 #ifndef SC_NO_CUTPASTE
181 
182 void
183 sc_draw_mouse_image(scr_stat *scp)
184 {
185     if (ISGRAPHSC(scp))
186 	return;
187 
188     ++scp->sc->videoio_in_progress;
189     (*scp->rndr->draw_mouse)(scp, scp->mouse_xpos, scp->mouse_ypos, TRUE);
190     scp->mouse_oldpos = scp->mouse_pos;
191     --scp->sc->videoio_in_progress;
192 }
193 
194 void
195 sc_remove_mouse_image(scr_stat *scp)
196 {
197     int size;
198     int i;
199 
200     if (ISGRAPHSC(scp))
201 	return;
202 
203     ++scp->sc->videoio_in_progress;
204     (*scp->rndr->draw_mouse)(scp,
205 			     (scp->mouse_oldpos%scp->xsize + scp->xoff)*8,
206 			     (scp->mouse_oldpos/scp->xsize + scp->yoff)
207 				 * scp->font_size,
208 			     FALSE);
209     size = scp->xsize*scp->ysize;
210     i = scp->mouse_oldpos;
211     mark_for_update(scp, i);
212     mark_for_update(scp, i);
213 #ifndef PC98
214     if (i + scp->xsize + 1 < size) {
215 	mark_for_update(scp, i + scp->xsize + 1);
216     } else if (i + scp->xsize < size) {
217 	mark_for_update(scp, i + scp->xsize);
218     } else if (i + 1 < size) {
219 	mark_for_update(scp, i + 1);
220     }
221 #endif /* PC98 */
222     --scp->sc->videoio_in_progress;
223 }
224 
225 int
226 sc_inside_cutmark(scr_stat *scp, int pos)
227 {
228     int start;
229     int end;
230 
231     if (scp->mouse_cut_end < 0)
232 	return FALSE;
233     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
234 	start = scp->mouse_cut_start;
235 	end = scp->mouse_cut_end;
236     } else {
237 	start = scp->mouse_cut_end;
238 	end = scp->mouse_cut_start - 1;
239     }
240     return ((start <= pos) && (pos <= end));
241 }
242 
243 void
244 sc_remove_cutmarking(scr_stat *scp)
245 {
246     int s;
247 
248     s = spltty();
249     if (scp->mouse_cut_end >= 0) {
250 	mark_for_update(scp, scp->mouse_cut_start);
251 	mark_for_update(scp, scp->mouse_cut_end);
252     }
253     scp->mouse_cut_start = scp->xsize*scp->ysize;
254     scp->mouse_cut_end = -1;
255     splx(s);
256     scp->status &= ~MOUSE_CUTTING;
257 }
258 
259 void
260 sc_remove_all_cutmarkings(sc_softc_t *sc)
261 {
262     scr_stat *scp;
263     int i;
264 
265     /* delete cut markings in all vtys */
266     for (i = 0; i < sc->vtys; ++i) {
267 	scp = SC_STAT(sc->dev[i]);
268 	if (scp == NULL)
269 	    continue;
270 	sc_remove_cutmarking(scp);
271     }
272 }
273 
274 void
275 sc_remove_all_mouse(sc_softc_t *sc)
276 {
277     scr_stat *scp;
278     int i;
279 
280     for (i = 0; i < sc->vtys; ++i) {
281 	scp = SC_STAT(sc->dev[i]);
282 	if (scp == NULL)
283 	    continue;
284 	if (scp->status & MOUSE_VISIBLE) {
285 	    scp->status &= ~MOUSE_VISIBLE;
286 	    mark_all(scp);
287 	}
288     }
289 }
290 
291 #define IS_SPACE_CHAR(c)	(((c) & 0xff) == ' ')
292 
293 /* skip spaces to right */
294 static int
295 skip_spc_right(scr_stat *scp, int p)
296 {
297     int c;
298     int i;
299 
300     for (i = p % scp->xsize; i < scp->xsize; ++i) {
301 	c = sc_vtb_getc(&scp->vtb, p);
302 	if (!IS_SPACE_CHAR(c))
303 	    break;
304 	++p;
305     }
306     return i;
307 }
308 
309 /* skip spaces to left */
310 static int
311 skip_spc_left(scr_stat *scp, int p)
312 {
313     int c;
314     int i;
315 
316     for (i = p-- % scp->xsize - 1; i >= 0; --i) {
317 	c = sc_vtb_getc(&scp->vtb, p);
318 	if (!IS_SPACE_CHAR(c))
319 	    break;
320 	--p;
321     }
322     return i;
323 }
324 
325 /* copy marked region to the cut buffer */
326 static void
327 mouse_cut(scr_stat *scp)
328 {
329     int start;
330     int end;
331     int from;
332     int to;
333     int blank;
334     int c;
335     int p;
336     int s;
337     int i;
338 
339     start = scp->mouse_cut_start;
340     end = scp->mouse_cut_end;
341     if (scp->mouse_pos >= start) {
342 	from = start;
343 	to = end = scp->mouse_pos;
344     } else {
345 	from = end = scp->mouse_pos;
346 	to = start - 1;
347     }
348     for (p = from, i = blank = 0; p <= to; ++p) {
349 	cut_buffer[i] = sc_vtb_getc(&scp->vtb, p);
350 	/* remember the position of the last non-space char */
351 	if (!IS_SPACE_CHAR(cut_buffer[i++]))
352 	    blank = i;		/* the first space after the last non-space */
353 	/* trim trailing blank when crossing lines */
354 	if ((p % scp->xsize) == (scp->xsize - 1)) {
355 	    cut_buffer[blank] = '\r';
356 	    i = blank + 1;
357 	}
358     }
359     cut_buffer[i] = '\0';
360 
361     /* scan towards the end of the last line */
362     --p;
363     for (i = p % scp->xsize; i < scp->xsize; ++i) {
364 	c = sc_vtb_getc(&scp->vtb, p);
365 	if (!IS_SPACE_CHAR(c))
366 	    break;
367 	++p;
368     }
369     /* if there is nothing but blank chars, trim them, but mark towards eol */
370     if (i >= scp->xsize) {
371 	if (end >= start)
372 	    to = end = p - 1;
373 	else
374 	    to = start = p;
375 	cut_buffer[blank++] = '\r';
376 	cut_buffer[blank] = '\0';
377     }
378 
379     /* remove the current marking */
380     s = spltty();
381     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
382 	mark_for_update(scp, scp->mouse_cut_start);
383 	mark_for_update(scp, scp->mouse_cut_end);
384     } else if (scp->mouse_cut_end >= 0) {
385 	mark_for_update(scp, scp->mouse_cut_end);
386 	mark_for_update(scp, scp->mouse_cut_start);
387     }
388 
389     /* mark the new region */
390     scp->mouse_cut_start = start;
391     scp->mouse_cut_end = end;
392     mark_for_update(scp, from);
393     mark_for_update(scp, to);
394     splx(s);
395 }
396 
397 /* a mouse button is pressed, start cut operation */
398 static void
399 mouse_cut_start(scr_stat *scp)
400 {
401     int i;
402     int j;
403     int s;
404 
405     if (scp->status & MOUSE_VISIBLE) {
406 	i = scp->mouse_cut_start;
407 	j = scp->mouse_cut_end;
408 	sc_remove_all_cutmarkings(scp->sc);
409 	if (scp->mouse_pos == i && i == j) {
410 	    cut_buffer[0] = '\0';
411 	} else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) {
412 	    /* if the pointer is on trailing blank chars, mark towards eol */
413 	    i = skip_spc_left(scp, scp->mouse_pos) + 1;
414 	    s = spltty();
415 	    scp->mouse_cut_start =
416 	        (scp->mouse_pos / scp->xsize) * scp->xsize + i;
417 	    scp->mouse_cut_end =
418 	        (scp->mouse_pos / scp->xsize + 1) * scp->xsize - 1;
419 	    splx(s);
420 	    cut_buffer[0] = '\r';
421 	    cut_buffer[1] = '\0';
422 	    scp->status |= MOUSE_CUTTING;
423 	} else {
424 	    s = spltty();
425 	    scp->mouse_cut_start = scp->mouse_pos;
426 	    scp->mouse_cut_end = scp->mouse_cut_start;
427 	    splx(s);
428 	    cut_buffer[0] = sc_vtb_getc(&scp->vtb, scp->mouse_cut_start);
429 	    cut_buffer[1] = '\0';
430 	    scp->status |= MOUSE_CUTTING;
431 	}
432     	mark_all(scp);	/* this is probably overkill XXX */
433     }
434 }
435 
436 /* end of cut operation */
437 static void
438 mouse_cut_end(scr_stat *scp)
439 {
440     if (scp->status & MOUSE_VISIBLE)
441 	scp->status &= ~MOUSE_CUTTING;
442 }
443 
444 /* copy a word under the mouse pointer */
445 static void
446 mouse_cut_word(scr_stat *scp)
447 {
448     int start;
449     int end;
450     int sol;
451     int eol;
452     int c;
453     int s;
454     int i;
455     int j;
456 
457     /*
458      * Because we don't have locale information in the kernel,
459      * we only distinguish space char and non-space chars.  Punctuation
460      * chars, symbols and other regular chars are all treated alike.
461      */
462     if (scp->status & MOUSE_VISIBLE) {
463 	/* remove the current cut mark */
464 	s = spltty();
465 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
466 	    mark_for_update(scp, scp->mouse_cut_start);
467 	    mark_for_update(scp, scp->mouse_cut_end);
468 	} else if (scp->mouse_cut_end >= 0) {
469 	    mark_for_update(scp, scp->mouse_cut_end);
470 	    mark_for_update(scp, scp->mouse_cut_start);
471 	}
472 	scp->mouse_cut_start = scp->xsize*scp->ysize;
473 	scp->mouse_cut_end = -1;
474 	splx(s);
475 
476 	sol = (scp->mouse_pos / scp->xsize) * scp->xsize;
477 	eol = sol + scp->xsize;
478 	c = sc_vtb_getc(&scp->vtb, scp->mouse_pos);
479 	if (IS_SPACE_CHAR(c)) {
480 	    /* blank space */
481 	    for (j = scp->mouse_pos; j >= sol; --j) {
482 		c = sc_vtb_getc(&scp->vtb, j);
483 	        if (!IS_SPACE_CHAR(c))
484 		    break;
485 	    }
486 	    start = ++j;
487 	    for (j = scp->mouse_pos; j < eol; ++j) {
488 		c = sc_vtb_getc(&scp->vtb, j);
489 	        if (!IS_SPACE_CHAR(c))
490 		    break;
491 	    }
492 	    end = j - 1;
493 	} else {
494 	    /* non-space word */
495 	    for (j = scp->mouse_pos; j >= sol; --j) {
496 		c = sc_vtb_getc(&scp->vtb, j);
497 	        if (IS_SPACE_CHAR(c))
498 		    break;
499 	    }
500 	    start = ++j;
501 	    for (j = scp->mouse_pos; j < eol; ++j) {
502 		c = sc_vtb_getc(&scp->vtb, j);
503 	        if (IS_SPACE_CHAR(c))
504 		    break;
505 	    }
506 	    end = j - 1;
507 	}
508 
509 	/* copy the found word */
510 	for (i = 0, j = start; j <= end; ++j)
511 	    cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j);
512 	cut_buffer[i] = '\0';
513 	scp->status |= MOUSE_CUTTING;
514 
515 	/* mark the region */
516 	s = spltty();
517 	scp->mouse_cut_start = start;
518 	scp->mouse_cut_end = end;
519 	mark_for_update(scp, start);
520 	mark_for_update(scp, end);
521 	splx(s);
522     }
523 }
524 
525 /* copy a line under the mouse pointer */
526 static void
527 mouse_cut_line(scr_stat *scp)
528 {
529     int s;
530     int i;
531     int j;
532 
533     if (scp->status & MOUSE_VISIBLE) {
534 	/* remove the current cut mark */
535 	s = spltty();
536 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
537 	    mark_for_update(scp, scp->mouse_cut_start);
538 	    mark_for_update(scp, scp->mouse_cut_end);
539 	} else if (scp->mouse_cut_end >= 0) {
540 	    mark_for_update(scp, scp->mouse_cut_end);
541 	    mark_for_update(scp, scp->mouse_cut_start);
542 	}
543 
544 	/* mark the entire line */
545 	scp->mouse_cut_start =
546 	    (scp->mouse_pos / scp->xsize) * scp->xsize;
547 	scp->mouse_cut_end = scp->mouse_cut_start + scp->xsize - 1;
548 	mark_for_update(scp, scp->mouse_cut_start);
549 	mark_for_update(scp, scp->mouse_cut_end);
550 	splx(s);
551 
552 	/* copy the line into the cut buffer */
553 	for (i = 0, j = scp->mouse_cut_start; j <= scp->mouse_cut_end; ++j)
554 	    cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j);
555 	cut_buffer[i++] = '\r';
556 	cut_buffer[i] = '\0';
557 	scp->status |= MOUSE_CUTTING;
558     }
559 }
560 
561 /* extend the marked region to the mouse pointer position */
562 static void
563 mouse_cut_extend(scr_stat *scp)
564 {
565     int start;
566     int end;
567     int s;
568 
569     if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING)
570 	&& (scp->mouse_cut_end >= 0)) {
571 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
572 	    start = scp->mouse_cut_start;
573 	    end = scp->mouse_cut_end;
574 	} else {
575 	    start = scp->mouse_cut_end;
576 	    end = scp->mouse_cut_start - 1;
577 	}
578 	s = spltty();
579 	if (scp->mouse_pos > end) {
580 	    scp->mouse_cut_start = start;
581 	    scp->mouse_cut_end = end;
582 	} else if (scp->mouse_pos < start) {
583 	    scp->mouse_cut_start = end + 1;
584 	    scp->mouse_cut_end = start;
585 	} else {
586 	    if (scp->mouse_pos - start > end + 1 - scp->mouse_pos) {
587 		scp->mouse_cut_start = start;
588 		scp->mouse_cut_end = end;
589 	    } else {
590 		scp->mouse_cut_start = end + 1;
591 		scp->mouse_cut_end = start;
592 	    }
593 	}
594 	splx(s);
595 	mouse_cut(scp);
596 	scp->status |= MOUSE_CUTTING;
597     }
598 }
599 
600 /* paste cut buffer contents into the current vty */
601 static void
602 mouse_paste(scr_stat *scp)
603 {
604     if (scp->status & MOUSE_VISIBLE)
605 	sc_paste(scp, cut_buffer, strlen(cut_buffer));
606 }
607 
608 #endif /* SC_NO_CUTPASTE */
609 
610 int
611 sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag,
612 	       struct proc *p)
613 {
614 
615     scr_stat *scp;
616     int s;
617     int i;
618 
619     /* scp == NULL, if tp == sc_get_mouse_tty() (/dev/sysmouse) */
620     scp = SC_STAT(tp->t_dev);
621 
622     switch (cmd) {
623 
624     case CONS_MOUSECTL:		/* control mouse arrow */
625     case OLD_CONS_MOUSECTL:
626     {
627 	/* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */
628 	static int butmap[8] = {
629             MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
630             MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
631             MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
632             MOUSE_MSC_BUTTON3UP,
633             MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
634             MOUSE_MSC_BUTTON2UP,
635             MOUSE_MSC_BUTTON1UP,
636             0,
637 	};
638 	mouse_info_t *mouse = (mouse_info_t*)data;
639 	mouse_info_t buf;
640 	scr_stat *cur_scp;
641 	struct tty *mtty;
642 	int f;
643 
644 	if (scp == NULL)
645 	    return ENOTTY;
646 
647 	if (cmd == OLD_CONS_MOUSECTL) {
648 	    static u_char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
649 	    old_mouse_info_t *old_mouse = (old_mouse_info_t *)data;
650 
651 	    mouse = &buf;
652 	    mouse->operation = old_mouse->operation;
653 	    switch (mouse->operation) {
654 	    case MOUSE_MODE:
655 		mouse->u.mode = old_mouse->u.mode;
656 		break;
657 	    case MOUSE_SHOW:
658 	    case MOUSE_HIDE:
659 		break;
660 	    case MOUSE_MOVEABS:
661 	    case MOUSE_MOVEREL:
662 	    case MOUSE_ACTION:
663 		mouse->u.data.x = old_mouse->u.data.x;
664 		mouse->u.data.y = old_mouse->u.data.y;
665 		mouse->u.data.z = 0;
666 		mouse->u.data.buttons = swapb[old_mouse->u.data.buttons & 0x7];
667 		break;
668 	    case MOUSE_GETINFO:
669 		old_mouse->u.data.x = scp->mouse_xpos;
670 		old_mouse->u.data.y = scp->mouse_ypos;
671 		old_mouse->u.data.buttons = swapb[scp->mouse_buttons & 0x7];
672 		break;
673 	    default:
674 		return EINVAL;
675 	    }
676 	}
677 
678 	cur_scp = scp->sc->cur_scp;
679 
680 	switch (mouse->operation) {
681 	case MOUSE_MODE:
682 	    if (ISSIGVALID(mouse->u.mode.signal)) {
683 		scp->mouse_signal = mouse->u.mode.signal;
684 		scp->mouse_proc = p;
685 		scp->mouse_pid = p->p_pid;
686 	    }
687 	    else {
688 		scp->mouse_signal = 0;
689 		scp->mouse_proc = NULL;
690 		scp->mouse_pid = 0;
691 	    }
692 	    return 0;
693 
694 	case MOUSE_SHOW:
695 	    if (!ISMOUSEAVAIL(scp->sc->adp->va_flags))
696 		return EINVAL;
697 	    s = spltty();
698 	    if (!(scp->sc->flags & SC_MOUSE_ENABLED)) {
699 		scp->sc->flags |= SC_MOUSE_ENABLED;
700 		if (!ISGRAPHSC(cur_scp)) {
701 		    cur_scp->status |= MOUSE_VISIBLE;
702 		    mark_all(cur_scp);
703 		}
704 		splx(s);
705 		return 0;
706 	    } else {
707 		splx(s);
708 		return EINVAL;
709 	    }
710 	    break;
711 
712 	case MOUSE_HIDE:
713 	    s = spltty();
714 	    if (scp->sc->flags & SC_MOUSE_ENABLED) {
715 		scp->sc->flags &= ~SC_MOUSE_ENABLED;
716 		sc_remove_all_mouse(scp->sc);
717 		splx(s);
718 		return 0;
719 	    } else {
720 		splx(s);
721 		return EINVAL;
722 	    }
723 	    break;
724 
725 	case MOUSE_MOVEABS:
726 	    s = spltty();
727 	    scp->mouse_xpos = mouse->u.data.x;
728 	    scp->mouse_ypos = mouse->u.data.y;
729 	    set_mouse_pos(scp);
730 	    splx(s);
731 	    break;
732 
733 	case MOUSE_MOVEREL:
734 	    s = spltty();
735 	    scp->mouse_xpos += mouse->u.data.x;
736 	    scp->mouse_ypos += mouse->u.data.y;
737 	    set_mouse_pos(scp);
738 	    splx(s);
739 	    break;
740 
741 	case MOUSE_GETINFO:
742 	    mouse->u.data.x = scp->mouse_xpos;
743 	    mouse->u.data.y = scp->mouse_ypos;
744 	    mouse->u.data.z = 0;
745 	    mouse->u.data.buttons = scp->mouse_buttons;
746 	    return 0;
747 
748 	case MOUSE_ACTION:
749 	case MOUSE_MOTION_EVENT:
750 	    /* send out mouse event on /dev/sysmouse */
751 #if 0
752 	    /* this should maybe only be settable from /dev/consolectl SOS */
753 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
754 		return ENOTTY;
755 #endif
756 	    s = spltty();
757 	    if (mouse->u.data.x != 0 || mouse->u.data.y != 0) {
758 		cur_scp->mouse_xpos += mouse->u.data.x;
759 		cur_scp->mouse_ypos += mouse->u.data.y;
760 		set_mouse_pos(cur_scp);
761 	    }
762 	    f = 0;
763 	    if (mouse->operation == MOUSE_ACTION) {
764 		f = cur_scp->mouse_buttons ^ mouse->u.data.buttons;
765 		cur_scp->mouse_buttons = mouse->u.data.buttons;
766 	    }
767 	    splx(s);
768 
769 	    mouse_status.dx += mouse->u.data.x;
770 	    mouse_status.dy += mouse->u.data.y;
771 	    mouse_status.dz += mouse->u.data.z;
772 	    if (mouse->operation == MOUSE_ACTION)
773 	        mouse_status.button = mouse->u.data.buttons;
774 	    mouse_status.flags |=
775 		((mouse->u.data.x || mouse->u.data.y || mouse->u.data.z) ?
776 		    MOUSE_POSCHANGED : 0)
777 		| (mouse_status.obutton ^ mouse_status.button);
778 	    if (mouse_status.flags == 0)
779 		return 0;
780 
781 	    mtty = sc_get_mouse_tty();
782 	    if (mtty->t_state & TS_ISOPEN) {
783 		u_char buf[MOUSE_SYS_PACKETSIZE];
784 
785 		/* the first five bytes are compatible with MouseSystems' */
786 		buf[0] = MOUSE_MSC_SYNC
787 		    | butmap[mouse_status.button & MOUSE_STDBUTTONS];
788 		i = imax(imin(mouse->u.data.x, 255), -256);
789 		buf[1] = i >> 1;
790 		buf[3] = i - buf[1];
791 		i = -imax(imin(mouse->u.data.y, 255), -256);
792 		buf[2] = i >> 1;
793 		buf[4] = i - buf[2];
794 		for (i = 0; i < MOUSE_MSC_PACKETSIZE; i++)
795 	    		(*linesw[mtty->t_line].l_rint)(buf[i], mtty);
796 		if (mouse_level >= 1) { 	/* extended part */
797 		    i = imax(imin(mouse->u.data.z, 127), -128);
798 		    buf[5] = (i >> 1) & 0x7f;
799 		    buf[6] = (i - (i >> 1)) & 0x7f;
800 		    /* buttons 4-10 */
801 		    buf[7] = (~mouse_status.button >> 3) & 0x7f;
802 		    for (i = MOUSE_MSC_PACKETSIZE;
803 			 i < MOUSE_SYS_PACKETSIZE; i++)
804 	    		(*linesw[mtty->t_line].l_rint)(buf[i], mtty);
805 		}
806 	    }
807 
808 	    /*
809 	     * If any buttons are down or the mouse has moved a lot,
810 	     * stop the screen saver.
811 	     */
812 	    if (((mouse->operation == MOUSE_ACTION) && mouse->u.data.buttons)
813 		|| (mouse->u.data.x*mouse->u.data.x
814 			+ mouse->u.data.y*mouse->u.data.y
815 			>= SC_WAKEUP_DELTA*SC_WAKEUP_DELTA)) {
816 		sc_touch_scrn_saver();
817 	    }
818 
819 #ifndef SC_NO_CUTPASTE
820 	    if (!ISGRAPHSC(cur_scp) && (cur_scp->sc->flags & SC_MOUSE_ENABLED))
821 		cur_scp->status |= MOUSE_VISIBLE;
822 #endif /* SC_NO_CUTPASTE */
823 
824 	    if (cur_scp->mouse_signal) {
825     		/* has controlling process died? */
826 		if (cur_scp->mouse_proc &&
827 		    (cur_scp->mouse_proc != pfind(cur_scp->mouse_pid))){
828 		    	cur_scp->mouse_signal = 0;
829 			cur_scp->mouse_proc = NULL;
830 			cur_scp->mouse_pid = 0;
831 		} else {
832 		    psignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
833 		    break;
834 		}
835 	    }
836 
837 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
838 		break;
839 
840 #ifndef SC_NO_CUTPASTE
841 	    if ((mouse->operation == MOUSE_ACTION) && f) {
842 		/* process button presses */
843 		if (cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN)
844 		    mouse_cut_start(cur_scp);
845 		else
846 		    mouse_cut_end(cur_scp);
847 		if (cur_scp->mouse_buttons & MOUSE_BUTTON2DOWN ||
848 		    cur_scp->mouse_buttons & MOUSE_BUTTON3DOWN)
849 		    mouse_paste(cur_scp);
850 	    }
851 #endif /* SC_NO_CUTPASTE */
852 	    break;
853 
854 	case MOUSE_BUTTON_EVENT:
855 	    if ((mouse->u.event.id & MOUSE_BUTTONS) == 0)
856 		return EINVAL;
857 	    if (mouse->u.event.value < 0)
858 		return EINVAL;
859 #if 0
860 	    /* this should maybe only be settable from /dev/consolectl SOS */
861 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
862 		return ENOTTY;
863 #endif
864 	    if (mouse->u.event.value > 0) {
865 		cur_scp->mouse_buttons |= mouse->u.event.id;
866 	        mouse_status.button |= mouse->u.event.id;
867 	    } else {
868 		cur_scp->mouse_buttons &= ~mouse->u.event.id;
869 	        mouse_status.button &= ~mouse->u.event.id;
870 	    }
871 	    mouse_status.flags |= mouse_status.obutton ^ mouse_status.button;
872 	    if (mouse_status.flags == 0)
873 		return 0;
874 
875 	    mtty = sc_get_mouse_tty();
876 	    if (mtty->t_state & TS_ISOPEN) {
877 		u_char buf[MOUSE_SYS_PACKETSIZE];
878 
879 		buf[0] = MOUSE_MSC_SYNC
880 			 | butmap[mouse_status.button & MOUSE_STDBUTTONS];
881 		buf[7] = (~mouse_status.button >> 3) & 0x7f;
882 		buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
883 		for (i = 0;
884 		     i < ((mouse_level >= 1) ? MOUSE_SYS_PACKETSIZE
885 					     : MOUSE_MSC_PACKETSIZE); i++)
886 	    	    (*linesw[mtty->t_line].l_rint)(buf[i], mtty);
887 	    }
888 
889 	    /* if a button is held down, stop the screen saver */
890 	    if (mouse->u.event.value > 0)
891 		sc_touch_scrn_saver();
892 
893 #ifndef SC_NO_CUTPASTE
894 	    if (!ISGRAPHSC(cur_scp) && (cur_scp->sc->flags & SC_MOUSE_ENABLED))
895 		cur_scp->status |= MOUSE_VISIBLE;
896 #endif /* SC_NO_CUTPASTE */
897 
898 	    if (cur_scp->mouse_signal) {
899 		if (cur_scp->mouse_proc &&
900 		    (cur_scp->mouse_proc != pfind(cur_scp->mouse_pid))){
901 		    	cur_scp->mouse_signal = 0;
902 			cur_scp->mouse_proc = NULL;
903 			cur_scp->mouse_pid = 0;
904 		} else {
905 		    psignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
906 		    break;
907 		}
908 	    }
909 
910 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
911 		break;
912 
913 #ifndef SC_NO_CUTPASTE
914 	    switch (mouse->u.event.id) {
915 	    case MOUSE_BUTTON1DOWN:
916 	        switch (mouse->u.event.value % 4) {
917 		case 0:	/* up */
918 		    mouse_cut_end(cur_scp);
919 		    break;
920 		case 1: /* single click: start cut operation */
921 		    mouse_cut_start(cur_scp);
922 		    break;
923 		case 2:	/* double click: cut a word */
924 		    mouse_cut_word(cur_scp);
925 		    mouse_cut_end(cur_scp);
926 		    break;
927 		case 3:	/* triple click: cut a line */
928 		    mouse_cut_line(cur_scp);
929 		    mouse_cut_end(cur_scp);
930 		    break;
931 		}
932 		break;
933 	    case SC_MOUSE_PASTEBUTTON:
934 	        switch (mouse->u.event.value) {
935 		case 0:	/* up */
936 		    break;
937 		default:
938 		    mouse_paste(cur_scp);
939 		    break;
940 		}
941 		break;
942 	    case SC_MOUSE_EXTENDBUTTON:
943 	        switch (mouse->u.event.value) {
944 		case 0:	/* up */
945 		    if (!(cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN))
946 		        mouse_cut_end(cur_scp);
947 		    break;
948 		default:
949 		    mouse_cut_extend(cur_scp);
950 		    break;
951 		}
952 		break;
953 	    }
954 #endif /* SC_NO_CUTPASTE */
955 	    break;
956 
957 	default:
958 	    return EINVAL;
959 	}
960 
961 	return 0;
962     }
963 
964     /* MOUSE_XXX: /dev/sysmouse ioctls */
965     case MOUSE_GETHWINFO:	/* get device information */
966     {
967 	mousehw_t *hw = (mousehw_t *)data;
968 
969 	if (tp != sc_get_mouse_tty())
970 	    return ENOTTY;
971 	hw->buttons = 10;		/* XXX unknown */
972 	hw->iftype = MOUSE_IF_SYSMOUSE;
973 	hw->type = MOUSE_MOUSE;
974 	hw->model = MOUSE_MODEL_GENERIC;
975 	hw->hwid = 0;
976 	return 0;
977     }
978 
979     case MOUSE_GETMODE:		/* get protocol/mode */
980     {
981 	mousemode_t *mode = (mousemode_t *)data;
982 
983 	if (tp != sc_get_mouse_tty())
984 	    return ENOTTY;
985 	mode->level = mouse_level;
986 	switch (mode->level) {
987 	case 0:
988 	    /* at this level, sysmouse emulates MouseSystems protocol */
989 	    mode->protocol = MOUSE_PROTO_MSC;
990 	    mode->rate = -1;		/* unknown */
991 	    mode->resolution = -1;	/* unknown */
992 	    mode->accelfactor = 0;	/* disabled */
993 	    mode->packetsize = MOUSE_MSC_PACKETSIZE;
994 	    mode->syncmask[0] = MOUSE_MSC_SYNCMASK;
995 	    mode->syncmask[1] = MOUSE_MSC_SYNC;
996 	    break;
997 
998 	case 1:
999 	    /* at this level, sysmouse uses its own protocol */
1000 	    mode->protocol = MOUSE_PROTO_SYSMOUSE;
1001 	    mode->rate = -1;
1002 	    mode->resolution = -1;
1003 	    mode->accelfactor = 0;
1004 	    mode->packetsize = MOUSE_SYS_PACKETSIZE;
1005 	    mode->syncmask[0] = MOUSE_SYS_SYNCMASK;
1006 	    mode->syncmask[1] = MOUSE_SYS_SYNC;
1007 	    break;
1008 	}
1009 	return 0;
1010     }
1011 
1012     case MOUSE_SETMODE:		/* set protocol/mode */
1013     {
1014 	mousemode_t *mode = (mousemode_t *)data;
1015 
1016 	if (tp != sc_get_mouse_tty())
1017 	    return ENOTTY;
1018 	if ((mode->level < 0) || (mode->level > 1))
1019 	    return EINVAL;
1020 	sc_mouse_set_level(mode->level);
1021 	return 0;
1022     }
1023 
1024     case MOUSE_GETLEVEL:	/* get operation level */
1025 	if (tp != sc_get_mouse_tty())
1026 	    return ENOTTY;
1027 	*(int *)data = mouse_level;
1028 	return 0;
1029 
1030     case MOUSE_SETLEVEL:	/* set operation level */
1031 	if (tp != sc_get_mouse_tty())
1032 	    return ENOTTY;
1033 	if ((*(int *)data  < 0) || (*(int *)data > 1))
1034 	    return EINVAL;
1035 	sc_mouse_set_level(*(int *)data);
1036 	return 0;
1037 
1038     case MOUSE_GETSTATUS:	/* get accumulated mouse events */
1039 	if (tp != sc_get_mouse_tty())
1040 	    return ENOTTY;
1041 	s = spltty();
1042 	*(mousestatus_t *)data = mouse_status;
1043 	mouse_status.flags = 0;
1044 	mouse_status.obutton = mouse_status.button;
1045 	mouse_status.dx = 0;
1046 	mouse_status.dy = 0;
1047 	mouse_status.dz = 0;
1048 	splx(s);
1049 	return 0;
1050 
1051 #if notyet
1052     case MOUSE_GETVARS:		/* get internal mouse variables */
1053     case MOUSE_SETVARS:		/* set internal mouse variables */
1054 	if (tp != sc_get_mouse_tty())
1055 	    return ENOTTY;
1056 	return ENODEV;
1057 #endif
1058 
1059     case MOUSE_READSTATE:	/* read status from the device */
1060     case MOUSE_READDATA:	/* read data from the device */
1061 	if (tp != sc_get_mouse_tty())
1062 	    return ENOTTY;
1063 	return ENODEV;
1064     }
1065 
1066     return ENOIOCTL;
1067 }
1068 
1069 #endif /* SC_NO_SYSMOUSE */
1070 
1071 #endif /* NSC */
1072