xref: /dragonfly/sys/dev/misc/syscons/scmouse.c (revision 6ab64ab6)
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: src/sys/dev/syscons/scmouse.c,v 1.12.2.3 2001/07/28 12:51:47 yokota Exp $
27  */
28 
29 #include "opt_syscons.h"
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/conf.h>
35 #include <sys/signalvar.h>
36 #include <sys/proc.h>
37 #include <sys/tty.h>
38 #include <sys/thread2.h>
39 #include <sys/mplock2.h>
40 
41 #include <machine/console.h>
42 #include <sys/mouse.h>
43 
44 #include "syscons.h"
45 
46 #ifdef SC_TWOBUTTON_MOUSE
47 #define SC_MOUSE_PASTEBUTTON	MOUSE_BUTTON3DOWN	/* right button */
48 #define SC_MOUSE_EXTENDBUTTON	MOUSE_BUTTON2DOWN	/* not really used */
49 #else
50 #define SC_MOUSE_PASTEBUTTON	MOUSE_BUTTON2DOWN	/* middle button */
51 #define SC_MOUSE_EXTENDBUTTON	MOUSE_BUTTON3DOWN	/* right button */
52 #endif /* SC_TWOBUTTON_MOUSE */
53 
54 #define SC_WAKEUP_DELTA		20
55 
56 #ifndef SC_NO_SYSMOUSE
57 
58 static int		cut_buffer_size;
59 static u_char		*cut_buffer;
60 
61 /* local functions */
62 static void sc_mouse_init(void *);
63 static void sc_mouse_uninit(void *);
64 
65 static void set_mouse_pos(scr_stat *scp);
66 #ifndef SC_NO_CUTPASTE
67 static int skip_spc_right(scr_stat *scp, int p);
68 static int skip_spc_left(scr_stat *scp, int p);
69 static void mouse_cut(scr_stat *scp);
70 static void mouse_cut_start(scr_stat *scp);
71 static void mouse_cut_end(scr_stat *scp);
72 static void mouse_cut_word(scr_stat *scp);
73 static void mouse_cut_line(scr_stat *scp);
74 static void mouse_cut_extend(scr_stat *scp);
75 static void mouse_paste(scr_stat *scp);
76 #endif /* SC_NO_CUTPASTE */
77 
78 #ifndef SC_NO_CUTPASTE
79 /* allocate a cut buffer */
80 void
81 sc_alloc_cut_buffer(scr_stat *scp, int wait)
82 {
83     u_char *p;
84 
85     if ((cut_buffer == NULL)
86 	|| (cut_buffer_size < scp->xsize * scp->ysize + 1)) {
87 	p = cut_buffer;
88 	cut_buffer = NULL;
89 	if (p != NULL)
90 	    kfree(p, M_SYSCONS);
91 	cut_buffer_size = scp->xsize * scp->ysize + 1;
92 	p = kmalloc(cut_buffer_size, M_SYSCONS, (wait) ? M_WAITOK : M_NOWAIT);
93 	if (p != NULL)
94 	    p[0] = '\0';
95 	cut_buffer = p;
96     }
97 }
98 #endif /* SC_NO_CUTPASTE */
99 
100 /* move mouse */
101 void
102 sc_mouse_move(scr_stat *scp, int x, int y)
103 {
104     crit_enter();
105     scp->mouse_xpos = scp->mouse_oldxpos = x;
106     scp->mouse_ypos = scp->mouse_oldypos = y;
107     if (scp->font_height <= 0)
108 	scp->mouse_pos = scp->mouse_oldpos = 0;
109     else
110 	scp->mouse_pos = scp->mouse_oldpos =
111 	    (y / scp->font_height - scp->yoff) * scp->xsize +
112 	    x / scp->font_width - scp->xoff;
113     scp->status |= MOUSE_MOVED;
114     crit_exit();
115 }
116 
117 /* adjust mouse position */
118 static void
119 set_mouse_pos(scr_stat *scp)
120 {
121     if (scp->mouse_xpos < scp->xoff * scp->font_width)
122 	scp->mouse_xpos = scp->xoff * scp->font_width;
123     if (scp->mouse_ypos < scp->yoff * scp->font_height)
124 	scp->mouse_ypos = scp->yoff * scp->font_height;
125     if (ISGRAPHSC(scp)) {
126         if (scp->mouse_xpos > scp->xpixel - 1)
127 	    scp->mouse_xpos = scp->xpixel - 1;
128         if (scp->mouse_ypos > scp->ypixel - 1)
129 	    scp->mouse_ypos = scp->ypixel - 1;
130 	return;
131     } else {
132 	if (scp->mouse_xpos > (scp->xsize + scp->xoff) * scp->font_width - 1)
133 	    scp->mouse_xpos = (scp->xsize + scp->xoff) * scp->font_width - 1;
134 	if (scp->mouse_ypos > (scp->ysize + scp->yoff) * scp->font_height - 1)
135 	    scp->mouse_ypos = (scp->ysize + scp->yoff) * scp->font_height - 1;
136     }
137 
138     if (scp->mouse_xpos != scp->mouse_oldxpos ||
139 	scp->mouse_ypos != scp->mouse_oldypos) {
140 	scp->status |= MOUSE_MOVED;
141     	scp->mouse_pos =
142 	    (scp->mouse_ypos / scp->font_height - scp->yoff) * scp->xsize +
143 	    scp->mouse_xpos / scp->font_width - scp->xoff;
144 #ifndef SC_NO_CUTPASTE
145 	if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING))
146 	    mouse_cut(scp);
147 #endif
148     }
149 }
150 
151 #ifndef SC_NO_CUTPASTE
152 
153 void
154 sc_draw_mouse_image(scr_stat *scp)
155 {
156     if (ISGRAPHSC(scp))
157 	return;
158 
159     atomic_add_int(&scp->sc->videoio_in_progress, 1);
160     (*scp->rndr->draw_mouse)(scp, scp->mouse_xpos, scp->mouse_ypos, TRUE);
161     scp->mouse_oldpos = scp->mouse_pos;
162     scp->mouse_oldxpos = scp->mouse_xpos;
163     scp->mouse_oldypos = scp->mouse_ypos;
164     scp->status |= MOUSE_VISIBLE;
165     atomic_add_int(&scp->sc->videoio_in_progress, -1);
166 }
167 
168 void
169 sc_remove_mouse_image(scr_stat *scp)
170 {
171     int size;
172     int i;
173 
174     if (ISGRAPHSC(scp))
175 	return;
176 
177     atomic_add_int(&scp->sc->videoio_in_progress, 1);
178     (*scp->rndr->draw_mouse)(scp,
179 			     (scp->mouse_oldpos%scp->xsize + scp->xoff) *
180 			      scp->font_width,
181 			     (scp->mouse_oldpos/scp->xsize + scp->yoff) *
182 			      scp->font_height,
183 			     FALSE);
184     size = scp->xsize * scp->ysize;
185     i = scp->mouse_oldpos;
186     mark_for_update(scp, i);
187     mark_for_update(scp, i);
188     if (i + scp->xsize + 1 < size) {
189 	mark_for_update(scp, i + scp->xsize + 1);
190     } else if (i + scp->xsize < size) {
191 	mark_for_update(scp, i + scp->xsize);
192     } else if (i + 1 < size) {
193 	mark_for_update(scp, i + 1);
194     }
195     scp->status &= ~MOUSE_VISIBLE;
196     atomic_add_int(&scp->sc->videoio_in_progress, -1);
197 }
198 
199 int
200 sc_inside_cutmark(scr_stat *scp, int pos)
201 {
202     int start;
203     int end;
204 
205     if (scp->mouse_cut_end < 0)
206 	return FALSE;
207     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
208 	start = scp->mouse_cut_start;
209 	end = scp->mouse_cut_end;
210     } else {
211 	start = scp->mouse_cut_end;
212 	end = scp->mouse_cut_start - 1;
213     }
214     return ((start <= pos) && (pos <= end));
215 }
216 
217 void
218 sc_remove_cutmarking(scr_stat *scp)
219 {
220     crit_enter();
221     if (scp->mouse_cut_end >= 0) {
222 	mark_for_update(scp, scp->mouse_cut_start);
223 	mark_for_update(scp, scp->mouse_cut_end);
224     }
225     scp->mouse_cut_start = scp->xsize*scp->ysize;
226     scp->mouse_cut_end = -1;
227     crit_exit();
228     scp->status &= ~MOUSE_CUTTING;
229 }
230 
231 void
232 sc_remove_all_cutmarkings(sc_softc_t *sc)
233 {
234     scr_stat *scp;
235     int i;
236 
237     /* delete cut markings in all vtys */
238     for (i = 0; i < sc->vtys; ++i) {
239 	scp = SC_STAT(sc->dev[i]);
240 	if (scp == NULL)
241 	    continue;
242 	sc_remove_cutmarking(scp);
243     }
244 }
245 
246 void
247 sc_remove_all_mouse(sc_softc_t *sc)
248 {
249     scr_stat *scp;
250     int i;
251 
252     for (i = 0; i < sc->vtys; ++i) {
253 	scp = SC_STAT(sc->dev[i]);
254 	if (scp == NULL)
255 	    continue;
256 	if (scp->status & MOUSE_VISIBLE) {
257 	    scp->status &= ~MOUSE_VISIBLE;
258 	    mark_all(scp);
259 	}
260     }
261 }
262 
263 #define IS_SPACE_CHAR(c)	(((c) & 0xff) == ' ')
264 
265 /* skip spaces to right */
266 static int
267 skip_spc_right(scr_stat *scp, int p)
268 {
269     int c;
270     int i;
271 
272     for (i = p % scp->xsize; i < scp->xsize; ++i) {
273 	c = sc_vtb_getc(&scp->vtb, p);
274 	if (!IS_SPACE_CHAR(c))
275 	    break;
276 	++p;
277     }
278     return i;
279 }
280 
281 /* skip spaces to left */
282 static int
283 skip_spc_left(scr_stat *scp, int p)
284 {
285     int c;
286     int i;
287 
288     for (i = p-- % scp->xsize - 1; i >= 0; --i) {
289 	c = sc_vtb_getc(&scp->vtb, p);
290 	if (!IS_SPACE_CHAR(c))
291 	    break;
292 	--p;
293     }
294     return i;
295 }
296 
297 /* copy marked region to the cut buffer */
298 static void
299 mouse_cut(scr_stat *scp)
300 {
301     int start;
302     int end;
303     int from;
304     int to;
305     int blank;
306     int c;
307     int p;
308     int i;
309 
310     start = scp->mouse_cut_start;
311     if (scp->mouse_pos >= start) {
312 	from = start;
313 	to = end = scp->mouse_pos;
314     } else {
315 	from = end = scp->mouse_pos;
316 	to = start - 1;
317     }
318     for (p = from, i = blank = 0; p <= to; ++p) {
319 	cut_buffer[i] = sc_vtb_getc(&scp->vtb, p);
320 	/* remember the position of the last non-space char */
321 	if (!IS_SPACE_CHAR(cut_buffer[i++]))
322 	    blank = i;		/* the first space after the last non-space */
323 	/* trim trailing blank when crossing lines */
324 	if ((p % scp->xsize) == (scp->xsize - 1)) {
325 	    cut_buffer[blank] = '\r';
326 	    i = blank + 1;
327 	}
328     }
329     cut_buffer[i] = '\0';
330 
331     /* scan towards the end of the last line */
332     --p;
333     for (i = p % scp->xsize; i < scp->xsize; ++i) {
334 	c = sc_vtb_getc(&scp->vtb, p);
335 	if (!IS_SPACE_CHAR(c))
336 	    break;
337 	++p;
338     }
339     /* if there is nothing but blank chars, trim them, but mark towards eol */
340     if (i >= scp->xsize) {
341 	if (end >= start)
342 	    to = end = p - 1;
343 	else
344 	    to = start = p;
345 	cut_buffer[blank++] = '\r';
346 	cut_buffer[blank] = '\0';
347     }
348 
349     /* remove the current marking */
350     crit_enter();
351     if (scp->mouse_cut_start <= scp->mouse_cut_end) {
352 	mark_for_update(scp, scp->mouse_cut_start);
353 	mark_for_update(scp, scp->mouse_cut_end);
354     } else if (scp->mouse_cut_end >= 0) {
355 	mark_for_update(scp, scp->mouse_cut_end);
356 	mark_for_update(scp, scp->mouse_cut_start);
357     }
358 
359     /* mark the new region */
360     scp->mouse_cut_start = start;
361     scp->mouse_cut_end = end;
362     mark_for_update(scp, from);
363     mark_for_update(scp, to);
364     crit_exit();
365 }
366 
367 /* a mouse button is pressed, start cut operation */
368 static void
369 mouse_cut_start(scr_stat *scp)
370 {
371     int i;
372     int j;
373 
374     if (scp->status & MOUSE_VISIBLE) {
375 	i = scp->mouse_cut_start;
376 	j = scp->mouse_cut_end;
377 	sc_remove_all_cutmarkings(scp->sc);
378 	if (scp->mouse_pos == i && i == j) {
379 	    cut_buffer[0] = '\0';
380 	} else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) {
381 	    /* if the pointer is on trailing blank chars, mark towards eol */
382 	    i = skip_spc_left(scp, scp->mouse_pos) + 1;
383 	    crit_enter();
384 	    scp->mouse_cut_start =
385 	        (scp->mouse_pos / scp->xsize) * scp->xsize + i;
386 	    scp->mouse_cut_end =
387 	        (scp->mouse_pos / scp->xsize + 1) * scp->xsize - 1;
388 	    crit_exit();
389 	    cut_buffer[0] = '\r';
390 	    cut_buffer[1] = '\0';
391 	    scp->status |= MOUSE_CUTTING;
392 	} else {
393 	    crit_enter();
394 	    scp->mouse_cut_start = scp->mouse_pos;
395 	    scp->mouse_cut_end = scp->mouse_cut_start;
396 	    crit_exit();
397 	    cut_buffer[0] = sc_vtb_getc(&scp->vtb, scp->mouse_cut_start);
398 	    cut_buffer[1] = '\0';
399 	    scp->status |= MOUSE_CUTTING;
400 	}
401     	mark_all(scp);	/* this is probably overkill XXX */
402     }
403 }
404 
405 /* end of cut operation */
406 static void
407 mouse_cut_end(scr_stat *scp)
408 {
409     if (scp->status & MOUSE_VISIBLE)
410 	scp->status &= ~MOUSE_CUTTING;
411 }
412 
413 /* copy a word under the mouse pointer */
414 static void
415 mouse_cut_word(scr_stat *scp)
416 {
417     int start;
418     int end;
419     int sol;
420     int eol;
421     int c;
422     int i;
423     int j;
424 
425     /*
426      * Because we don't have locale information in the kernel,
427      * we only distinguish space char and non-space chars.  Punctuation
428      * chars, symbols and other regular chars are all treated alike.
429      */
430     if (scp->status & MOUSE_VISIBLE) {
431 	/* remove the current cut mark */
432 	crit_enter();
433 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
434 	    mark_for_update(scp, scp->mouse_cut_start);
435 	    mark_for_update(scp, scp->mouse_cut_end);
436 	} else if (scp->mouse_cut_end >= 0) {
437 	    mark_for_update(scp, scp->mouse_cut_end);
438 	    mark_for_update(scp, scp->mouse_cut_start);
439 	}
440 	scp->mouse_cut_start = scp->xsize*scp->ysize;
441 	scp->mouse_cut_end = -1;
442 	crit_exit();
443 
444 	sol = (scp->mouse_pos / scp->xsize) * scp->xsize;
445 	eol = sol + scp->xsize;
446 	c = sc_vtb_getc(&scp->vtb, scp->mouse_pos);
447 	if (IS_SPACE_CHAR(c)) {
448 	    /* blank space */
449 	    for (j = scp->mouse_pos; j >= sol; --j) {
450 		c = sc_vtb_getc(&scp->vtb, j);
451 	        if (!IS_SPACE_CHAR(c))
452 		    break;
453 	    }
454 	    start = ++j;
455 	    for (j = scp->mouse_pos; j < eol; ++j) {
456 		c = sc_vtb_getc(&scp->vtb, j);
457 	        if (!IS_SPACE_CHAR(c))
458 		    break;
459 	    }
460 	    end = j - 1;
461 	} else {
462 	    /* non-space word */
463 	    for (j = scp->mouse_pos; j >= sol; --j) {
464 		c = sc_vtb_getc(&scp->vtb, j);
465 	        if (IS_SPACE_CHAR(c))
466 		    break;
467 	    }
468 	    start = ++j;
469 	    for (j = scp->mouse_pos; j < eol; ++j) {
470 		c = sc_vtb_getc(&scp->vtb, j);
471 	        if (IS_SPACE_CHAR(c))
472 		    break;
473 	    }
474 	    end = j - 1;
475 	}
476 
477 	/* copy the found word */
478 	for (i = 0, j = start; j <= end; ++j)
479 	    cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j);
480 	cut_buffer[i] = '\0';
481 	scp->status |= MOUSE_CUTTING;
482 
483 	/* mark the region */
484 	crit_enter();
485 	scp->mouse_cut_start = start;
486 	scp->mouse_cut_end = end;
487 	mark_for_update(scp, start);
488 	mark_for_update(scp, end);
489 	crit_exit();
490     }
491 }
492 
493 /* copy a line under the mouse pointer */
494 static void
495 mouse_cut_line(scr_stat *scp)
496 {
497     int i;
498     int j;
499 
500     if (scp->status & MOUSE_VISIBLE) {
501 	/* remove the current cut mark */
502 	crit_enter();
503 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
504 	    mark_for_update(scp, scp->mouse_cut_start);
505 	    mark_for_update(scp, scp->mouse_cut_end);
506 	} else if (scp->mouse_cut_end >= 0) {
507 	    mark_for_update(scp, scp->mouse_cut_end);
508 	    mark_for_update(scp, scp->mouse_cut_start);
509 	}
510 
511 	/* mark the entire line */
512 	scp->mouse_cut_start =
513 	    (scp->mouse_pos / scp->xsize) * scp->xsize;
514 	scp->mouse_cut_end = scp->mouse_cut_start + scp->xsize - 1;
515 	mark_for_update(scp, scp->mouse_cut_start);
516 	mark_for_update(scp, scp->mouse_cut_end);
517 	crit_exit();
518 
519 	/* copy the line into the cut buffer */
520 	for (i = 0, j = scp->mouse_cut_start; j <= scp->mouse_cut_end; ++j)
521 	    cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j);
522 	cut_buffer[i++] = '\r';
523 	cut_buffer[i] = '\0';
524 	scp->status |= MOUSE_CUTTING;
525     }
526 }
527 
528 /* extend the marked region to the mouse pointer position */
529 static void
530 mouse_cut_extend(scr_stat *scp)
531 {
532     int start;
533     int end;
534 
535     if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING)
536 	&& (scp->mouse_cut_end >= 0)) {
537 	if (scp->mouse_cut_start <= scp->mouse_cut_end) {
538 	    start = scp->mouse_cut_start;
539 	    end = scp->mouse_cut_end;
540 	} else {
541 	    start = scp->mouse_cut_end;
542 	    end = scp->mouse_cut_start - 1;
543 	}
544 	crit_enter();
545 	if (scp->mouse_pos > end) {
546 	    scp->mouse_cut_start = start;
547 	    scp->mouse_cut_end = end;
548 	} else if (scp->mouse_pos < start) {
549 	    scp->mouse_cut_start = end + 1;
550 	    scp->mouse_cut_end = start;
551 	} else {
552 	    if (scp->mouse_pos - start > end + 1 - scp->mouse_pos) {
553 		scp->mouse_cut_start = start;
554 		scp->mouse_cut_end = end;
555 	    } else {
556 		scp->mouse_cut_start = end + 1;
557 		scp->mouse_cut_end = start;
558 	    }
559 	}
560 	crit_exit();
561 	mouse_cut(scp);
562 	scp->status |= MOUSE_CUTTING;
563     }
564 }
565 
566 /* paste cut buffer contents into the current vty */
567 static void
568 mouse_paste(scr_stat *scp)
569 {
570     if (scp->status & MOUSE_VISIBLE)
571 	sc_paste(scp, cut_buffer, strlen(cut_buffer));
572 }
573 
574 #endif /* SC_NO_CUTPASTE */
575 
576 static void
577 sc_mouse_exit1_proc(struct proc *p)
578 {
579     scr_stat *scp;
580 
581     scp = p->p_drv_priv;
582     KKASSERT(scp != NULL);
583 
584     get_mplock();
585     KKASSERT(scp->mouse_proc == p);
586     KKASSERT(scp->mouse_pid == p->p_pid);
587 
588     scp->mouse_signal = 0;
589     scp->mouse_proc = NULL;
590     scp->mouse_pid = 0;
591     rel_mplock();
592 
593     PRELE(p);
594     p->p_flags &= ~P_SCMOUSE;
595     p->p_drv_priv = NULL;
596 }
597 
598 /*
599  * sc_mouse_exit1:
600  *
601  *	Handle exit1 for processes registered as MOUSE_MODE handlers.
602  *	We must remove a process hold, established when MOUSE_MODE
603  *	was enabled.
604  */
605 static void
606 sc_mouse_exit1(struct thread *td)
607 {
608     struct proc *p;
609 
610     p = td->td_proc;
611     KKASSERT(p != NULL);
612 
613     if ((p->p_flags & P_SCMOUSE) == 0)
614 	return;
615 
616 
617     sc_mouse_exit1_proc(p);
618 }
619 
620 int
621 sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
622 {
623     mouse_info_t *mouse;
624     scr_stat *cur_scp;
625     scr_stat *scp;
626     int f;
627 
628     scp = SC_STAT(tp->t_dev);
629 
630     switch (cmd) {
631 
632     case CONS_MOUSECTL:		/* control mouse arrow */
633 	mouse = (mouse_info_t*) data;
634 	cur_scp = scp->sc->cur_scp;
635 
636 	switch (mouse->operation) {
637 	/*
638 	 * Setup a process to receive signals on mouse events.
639 	 */
640 	case MOUSE_MODE:
641 	    get_mplock();
642 
643 	    if (!ISSIGVALID(mouse->u.mode.signal)) {
644 		/* Setting MOUSE_MODE w/ an invalid signal is used to disarm */
645 		if (scp->mouse_proc == curproc) {
646 		    sc_mouse_exit1_proc(curproc);
647 		    rel_mplock();
648 		    return 0;
649 		} else {
650 		    rel_mplock();
651 		    return EINVAL;
652 		}
653 	    } else {
654 		/* Only one mouse process per syscons */
655 		if (scp->mouse_proc) {
656 		    rel_mplock();
657 		    return EINVAL;
658 		}
659 
660 		/* Only one syscons signal source per process */
661 		if (curproc->p_flags & P_SCMOUSE) {
662 		    rel_mplock();
663 		    return EINVAL;
664 		}
665 
666 	        /*
667 	         * Process is stabilized by a hold, which is removed from
668 	         * sc_mouse_exit1. scp's mouse_{signal,proc,pid} fields
669 	         * are synchronized by the MP Lock.
670 	         */
671 	        scp->mouse_signal = mouse->u.mode.signal;
672 	        scp->mouse_proc = curproc;
673 	        scp->mouse_pid = curproc->p_pid;
674 	        curproc->p_flags |= P_SCMOUSE;
675 		KKASSERT(curproc->p_drv_priv == NULL);
676 	        curproc->p_drv_priv = scp;
677 	        PHOLD(curproc);
678 
679 	        rel_mplock();
680 	        return 0;
681             }
682 	    /*NOTREACHED*/
683 	    break;
684 
685 	case MOUSE_SHOW:
686 	    crit_enter();
687 	    if (!(scp->sc->flags & SC_MOUSE_ENABLED)) {
688 		scp->sc->flags |= SC_MOUSE_ENABLED;
689 		cur_scp->status &= ~MOUSE_HIDDEN;
690 		if (!ISGRAPHSC(cur_scp))
691 		    mark_all(cur_scp);
692 		crit_exit();
693 		return 0;
694 	    } else {
695 		crit_exit();
696 		return EINVAL;
697 	    }
698 	    break;
699 
700 	case MOUSE_HIDE:
701 	    crit_enter();
702 	    if (scp->sc->flags & SC_MOUSE_ENABLED) {
703 		scp->sc->flags &= ~SC_MOUSE_ENABLED;
704 		sc_remove_all_mouse(scp->sc);
705 		crit_exit();
706 		return 0;
707 	    } else {
708 		crit_exit();
709 		return EINVAL;
710 	    }
711 	    break;
712 
713 	case MOUSE_MOVEABS:
714 	    crit_enter();
715 	    scp->mouse_xpos = mouse->u.data.x;
716 	    scp->mouse_ypos = mouse->u.data.y;
717 	    set_mouse_pos(scp);
718 	    crit_exit();
719 	    break;
720 
721 	case MOUSE_MOVEREL:
722 	    crit_enter();
723 	    scp->mouse_xpos += mouse->u.data.x;
724 	    scp->mouse_ypos += mouse->u.data.y;
725 	    set_mouse_pos(scp);
726 	    crit_exit();
727 	    break;
728 
729 	case MOUSE_GETINFO:
730 	    mouse->u.data.x = scp->mouse_xpos;
731 	    mouse->u.data.y = scp->mouse_ypos;
732 	    mouse->u.data.z = 0;
733 	    mouse->u.data.buttons = scp->mouse_buttons;
734 	    return 0;
735 
736 	case MOUSE_ACTION:
737 	case MOUSE_MOTION_EVENT:
738 	    /* send out mouse event on /dev/sysmouse */
739 #if 0
740 	    /* this should maybe only be settable from /dev/consolectl SOS */
741 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
742 		return ENOTTY;
743 #endif
744 	    crit_enter();
745 	    if (mouse->u.data.x != 0 || mouse->u.data.y != 0) {
746 		cur_scp->mouse_xpos += mouse->u.data.x;
747 		cur_scp->mouse_ypos += mouse->u.data.y;
748 		set_mouse_pos(cur_scp);
749 	    }
750 	    f = 0;
751 	    if (mouse->operation == MOUSE_ACTION) {
752 		f = cur_scp->mouse_buttons ^ mouse->u.data.buttons;
753 		cur_scp->mouse_buttons = mouse->u.data.buttons;
754 	    }
755 	    crit_exit();
756 
757 	    if (sysmouse_event(mouse) == 0)
758 		return 0;
759 
760 	    /*
761 	     * If any buttons are down or the mouse has moved a lot,
762 	     * stop the screen saver.
763 	     */
764 	    if (((mouse->operation == MOUSE_ACTION) && mouse->u.data.buttons)
765 		|| (mouse->u.data.x*mouse->u.data.x
766 			+ mouse->u.data.y*mouse->u.data.y
767 			>= SC_WAKEUP_DELTA*SC_WAKEUP_DELTA)) {
768 		sc_touch_scrn_saver();
769 	    }
770 
771 	    cur_scp->status &= ~MOUSE_HIDDEN;
772 
773 	    get_mplock();
774 	    if (cur_scp->mouse_signal) {
775 		KKASSERT(cur_scp->mouse_proc != NULL);
776 		ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
777 		rel_mplock();
778 	        break;
779 	    }
780 	    rel_mplock();
781 
782 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
783 		break;
784 
785 #ifndef SC_NO_CUTPASTE
786 	    if ((mouse->operation == MOUSE_ACTION) && f) {
787 		/* process button presses */
788 		if (cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN)
789 		    mouse_cut_start(cur_scp);
790 		else
791 		    mouse_cut_end(cur_scp);
792 		if (cur_scp->mouse_buttons & MOUSE_BUTTON2DOWN ||
793 		    cur_scp->mouse_buttons & MOUSE_BUTTON3DOWN)
794 		    mouse_paste(cur_scp);
795 	    }
796 #endif /* SC_NO_CUTPASTE */
797 	    break;
798 
799 	case MOUSE_BUTTON_EVENT:
800 	    if ((mouse->u.event.id & MOUSE_BUTTONS) == 0)
801 		return EINVAL;
802 	    if (mouse->u.event.value < 0)
803 		return EINVAL;
804 #if 0
805 	    /* this should maybe only be settable from /dev/consolectl SOS */
806 	    if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
807 		return ENOTTY;
808 #endif
809 	    if (mouse->u.event.value > 0)
810 		cur_scp->mouse_buttons |= mouse->u.event.id;
811 	    else
812 		cur_scp->mouse_buttons &= ~mouse->u.event.id;
813 
814 	    if (sysmouse_event(mouse) == 0)
815 		return 0;
816 
817 	    /* if a button is held down, stop the screen saver */
818 	    if (mouse->u.event.value > 0)
819 		sc_touch_scrn_saver();
820 
821 	    cur_scp->status &= ~MOUSE_HIDDEN;
822 
823 	    get_mplock();
824 	    if (cur_scp->mouse_signal) {
825 		KKASSERT(cur_scp->mouse_proc != NULL);
826 		ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
827 		rel_mplock();
828 	        break;
829 	    }
830 	    rel_mplock();
831 
832 	    if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
833 		break;
834 
835 #ifndef SC_NO_CUTPASTE
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 		    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 >= (unsigned char)-1 - 4)
884 		    return EINVAL;
885 		crit_enter();
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_height,
890 				 cur_scp->font,
891 				 cur_scp->sc->mouse_char, 4);
892 #endif
893 		scp->sc->mouse_char = mouse->u.mouse_char;
894 		crit_exit();
895 	    }
896 	    break;
897 
898 	default:
899 	    return EINVAL;
900 	}
901 
902 	return 0;
903     }
904 
905     return ENOIOCTL;
906 }
907 
908 void
909 sc_mouse_init(void *unused)
910 {
911     at_exit(sc_mouse_exit1);
912 }
913 
914 void
915 sc_mouse_uninit(void *unused)
916 {
917 }
918 
919 SYSINIT(sc_mouse_init, SI_SUB_DRIVERS, SI_ORDER_ANY, sc_mouse_init, NULL);
920 SYSUNINIT(sc_mouse_uninit, SI_SUB_DRIVERS, SI_ORDER_ANY, sc_mouse_uninit, NULL);
921 
922 #endif /* SC_NO_SYSMOUSE */
923