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