2 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 * $FreeBSD: src/sys/dev/syscons/scmouse.c,v 1.12.2.3 2001/07/28 12:51:47 yokota Exp $
27 * $DragonFly: src/sys/dev/misc/syscons/scmouse.c,v 1.13 2006/09/05 00:55:38 dillon Exp $
30 #include "opt_syscons.h"
32 #include <sys/param.h>
33 #include <sys/systm.h>
35 #include <sys/signalvar.h>
38 #include <sys/thread2.h>
40 #include <machine/console.h>
41 #include <machine/mouse.h>
45 #ifdef SC_TWOBUTTON_MOUSE
46 #define SC_MOUSE_PASTEBUTTON MOUSE_BUTTON3DOWN /* right button */
47 #define SC_MOUSE_EXTENDBUTTON MOUSE_BUTTON2DOWN /* not really used */
49 #define SC_MOUSE_PASTEBUTTON MOUSE_BUTTON2DOWN /* middle button */
50 #define SC_MOUSE_EXTENDBUTTON MOUSE_BUTTON3DOWN /* right button */
51 #endif /* SC_TWOBUTTON_MOUSE */
53 #define SC_WAKEUP_DELTA 20
55 #ifndef SC_NO_SYSMOUSE
58 static int cut_buffer_size;
59 static u_char *cut_buffer;
62 static void set_mouse_pos(scr_stat *scp);
63 #ifndef SC_NO_CUTPASTE
64 static int skip_spc_right(scr_stat *scp, int p);
65 static int skip_spc_left(scr_stat *scp, int p);
66 static void mouse_cut(scr_stat *scp);
67 static void mouse_cut_start(scr_stat *scp);
68 static void mouse_cut_end(scr_stat *scp);
69 static void mouse_cut_word(scr_stat *scp);
70 static void mouse_cut_line(scr_stat *scp);
71 static void mouse_cut_extend(scr_stat *scp);
72 static void mouse_paste(scr_stat *scp);
73 #endif /* SC_NO_CUTPASTE */
75 #ifndef SC_NO_CUTPASTE
76 /* allocate a cut buffer */
78 sc_alloc_cut_buffer(scr_stat *scp, int wait)
82 if ((cut_buffer == NULL)
83 || (cut_buffer_size < scp->xsize * scp->ysize + 1)) {
88 cut_buffer_size = scp->xsize * scp->ysize + 1;
89 p = kmalloc(cut_buffer_size, M_SYSCONS, (wait) ? M_WAITOK : M_NOWAIT);
95 #endif /* SC_NO_CUTPASTE */
99 sc_mouse_move(scr_stat *scp, int x, int y)
102 scp->mouse_xpos = scp->mouse_oldxpos = x;
103 scp->mouse_ypos = scp->mouse_oldypos = y;
104 if (scp->font_size <= 0)
105 scp->mouse_pos = scp->mouse_oldpos = 0;
107 scp->mouse_pos = scp->mouse_oldpos =
108 (y/scp->font_size - scp->yoff)*scp->xsize + x/8 - scp->xoff;
109 scp->status |= MOUSE_MOVED;
113 /* adjust mouse position */
115 set_mouse_pos(scr_stat *scp)
117 if (scp->mouse_xpos < scp->xoff*8)
118 scp->mouse_xpos = scp->xoff*8;
119 if (scp->mouse_ypos < scp->yoff*scp->font_size)
120 scp->mouse_ypos = scp->yoff*scp->font_size;
121 if (ISGRAPHSC(scp)) {
122 if (scp->mouse_xpos > scp->xpixel-1)
123 scp->mouse_xpos = scp->xpixel-1;
124 if (scp->mouse_ypos > scp->ypixel-1)
125 scp->mouse_ypos = scp->ypixel-1;
128 if (scp->mouse_xpos > (scp->xsize + scp->xoff)*8 - 1)
129 scp->mouse_xpos = (scp->xsize + scp->xoff)*8 - 1;
130 if (scp->mouse_ypos > (scp->ysize + scp->yoff)*scp->font_size - 1)
131 scp->mouse_ypos = (scp->ysize + scp->yoff)*scp->font_size - 1;
134 if (scp->mouse_xpos != scp->mouse_oldxpos || scp->mouse_ypos != scp->mouse_oldypos) {
135 scp->status |= MOUSE_MOVED;
137 (scp->mouse_ypos/scp->font_size - scp->yoff)*scp->xsize
138 + scp->mouse_xpos/8 - scp->xoff;
139 #ifndef SC_NO_CUTPASTE
140 if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING))
146 #ifndef SC_NO_CUTPASTE
149 sc_draw_mouse_image(scr_stat *scp)
154 ++scp->sc->videoio_in_progress;
155 (*scp->rndr->draw_mouse)(scp, scp->mouse_xpos, scp->mouse_ypos, TRUE);
156 scp->mouse_oldpos = scp->mouse_pos;
157 scp->mouse_oldxpos = scp->mouse_xpos;
158 scp->mouse_oldypos = scp->mouse_ypos;
159 scp->status |= MOUSE_VISIBLE;
160 --scp->sc->videoio_in_progress;
164 sc_remove_mouse_image(scr_stat *scp)
172 ++scp->sc->videoio_in_progress;
173 (*scp->rndr->draw_mouse)(scp,
174 (scp->mouse_oldpos%scp->xsize + scp->xoff)*8,
175 (scp->mouse_oldpos/scp->xsize + scp->yoff)
178 size = scp->xsize*scp->ysize;
179 i = scp->mouse_oldpos;
180 mark_for_update(scp, i);
181 mark_for_update(scp, i);
182 if (i + scp->xsize + 1 < size) {
183 mark_for_update(scp, i + scp->xsize + 1);
184 } else if (i + scp->xsize < size) {
185 mark_for_update(scp, i + scp->xsize);
186 } else if (i + 1 < size) {
187 mark_for_update(scp, i + 1);
189 scp->status &= ~MOUSE_VISIBLE;
190 --scp->sc->videoio_in_progress;
194 sc_inside_cutmark(scr_stat *scp, int pos)
199 if (scp->mouse_cut_end < 0)
201 if (scp->mouse_cut_start <= scp->mouse_cut_end) {
202 start = scp->mouse_cut_start;
203 end = scp->mouse_cut_end;
205 start = scp->mouse_cut_end;
206 end = scp->mouse_cut_start - 1;
208 return ((start <= pos) && (pos <= end));
212 sc_remove_cutmarking(scr_stat *scp)
215 if (scp->mouse_cut_end >= 0) {
216 mark_for_update(scp, scp->mouse_cut_start);
217 mark_for_update(scp, scp->mouse_cut_end);
219 scp->mouse_cut_start = scp->xsize*scp->ysize;
220 scp->mouse_cut_end = -1;
222 scp->status &= ~MOUSE_CUTTING;
226 sc_remove_all_cutmarkings(sc_softc_t *sc)
231 /* delete cut markings in all vtys */
232 for (i = 0; i < sc->vtys; ++i) {
233 scp = SC_STAT(sc->dev[i]);
236 sc_remove_cutmarking(scp);
241 sc_remove_all_mouse(sc_softc_t *sc)
246 for (i = 0; i < sc->vtys; ++i) {
247 scp = SC_STAT(sc->dev[i]);
250 if (scp->status & MOUSE_VISIBLE) {
251 scp->status &= ~MOUSE_VISIBLE;
257 #define IS_SPACE_CHAR(c) (((c) & 0xff) == ' ')
259 /* skip spaces to right */
261 skip_spc_right(scr_stat *scp, int p)
266 for (i = p % scp->xsize; i < scp->xsize; ++i) {
267 c = sc_vtb_getc(&scp->vtb, p);
268 if (!IS_SPACE_CHAR(c))
275 /* skip spaces to left */
277 skip_spc_left(scr_stat *scp, int p)
282 for (i = p-- % scp->xsize - 1; i >= 0; --i) {
283 c = sc_vtb_getc(&scp->vtb, p);
284 if (!IS_SPACE_CHAR(c))
291 /* copy marked region to the cut buffer */
293 mouse_cut(scr_stat *scp)
304 start = scp->mouse_cut_start;
305 end = scp->mouse_cut_end;
306 if (scp->mouse_pos >= start) {
308 to = end = scp->mouse_pos;
310 from = end = scp->mouse_pos;
313 for (p = from, i = blank = 0; p <= to; ++p) {
314 cut_buffer[i] = sc_vtb_getc(&scp->vtb, p);
315 /* remember the position of the last non-space char */
316 if (!IS_SPACE_CHAR(cut_buffer[i++]))
317 blank = i; /* the first space after the last non-space */
318 /* trim trailing blank when crossing lines */
319 if ((p % scp->xsize) == (scp->xsize - 1)) {
320 cut_buffer[blank] = '\r';
324 cut_buffer[i] = '\0';
326 /* scan towards the end of the last line */
328 for (i = p % scp->xsize; i < scp->xsize; ++i) {
329 c = sc_vtb_getc(&scp->vtb, p);
330 if (!IS_SPACE_CHAR(c))
334 /* if there is nothing but blank chars, trim them, but mark towards eol */
335 if (i >= scp->xsize) {
340 cut_buffer[blank++] = '\r';
341 cut_buffer[blank] = '\0';
344 /* remove the current marking */
346 if (scp->mouse_cut_start <= scp->mouse_cut_end) {
347 mark_for_update(scp, scp->mouse_cut_start);
348 mark_for_update(scp, scp->mouse_cut_end);
349 } else if (scp->mouse_cut_end >= 0) {
350 mark_for_update(scp, scp->mouse_cut_end);
351 mark_for_update(scp, scp->mouse_cut_start);
354 /* mark the new region */
355 scp->mouse_cut_start = start;
356 scp->mouse_cut_end = end;
357 mark_for_update(scp, from);
358 mark_for_update(scp, to);
362 /* a mouse button is pressed, start cut operation */
364 mouse_cut_start(scr_stat *scp)
369 if (scp->status & MOUSE_VISIBLE) {
370 i = scp->mouse_cut_start;
371 j = scp->mouse_cut_end;
372 sc_remove_all_cutmarkings(scp->sc);
373 if (scp->mouse_pos == i && i == j) {
374 cut_buffer[0] = '\0';
375 } else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) {
376 /* if the pointer is on trailing blank chars, mark towards eol */
377 i = skip_spc_left(scp, scp->mouse_pos) + 1;
379 scp->mouse_cut_start =
380 (scp->mouse_pos / scp->xsize) * scp->xsize + i;
382 (scp->mouse_pos / scp->xsize + 1) * scp->xsize - 1;
384 cut_buffer[0] = '\r';
385 cut_buffer[1] = '\0';
386 scp->status |= MOUSE_CUTTING;
389 scp->mouse_cut_start = scp->mouse_pos;
390 scp->mouse_cut_end = scp->mouse_cut_start;
392 cut_buffer[0] = sc_vtb_getc(&scp->vtb, scp->mouse_cut_start);
393 cut_buffer[1] = '\0';
394 scp->status |= MOUSE_CUTTING;
396 mark_all(scp); /* this is probably overkill XXX */
400 /* end of cut operation */
402 mouse_cut_end(scr_stat *scp)
404 if (scp->status & MOUSE_VISIBLE)
405 scp->status &= ~MOUSE_CUTTING;
408 /* copy a word under the mouse pointer */
410 mouse_cut_word(scr_stat *scp)
421 * Because we don't have locale information in the kernel,
422 * we only distinguish space char and non-space chars. Punctuation
423 * chars, symbols and other regular chars are all treated alike.
425 if (scp->status & MOUSE_VISIBLE) {
426 /* remove the current cut mark */
428 if (scp->mouse_cut_start <= scp->mouse_cut_end) {
429 mark_for_update(scp, scp->mouse_cut_start);
430 mark_for_update(scp, scp->mouse_cut_end);
431 } else if (scp->mouse_cut_end >= 0) {
432 mark_for_update(scp, scp->mouse_cut_end);
433 mark_for_update(scp, scp->mouse_cut_start);
435 scp->mouse_cut_start = scp->xsize*scp->ysize;
436 scp->mouse_cut_end = -1;
439 sol = (scp->mouse_pos / scp->xsize) * scp->xsize;
440 eol = sol + scp->xsize;
441 c = sc_vtb_getc(&scp->vtb, scp->mouse_pos);
442 if (IS_SPACE_CHAR(c)) {
444 for (j = scp->mouse_pos; j >= sol; --j) {
445 c = sc_vtb_getc(&scp->vtb, j);
446 if (!IS_SPACE_CHAR(c))
450 for (j = scp->mouse_pos; j < eol; ++j) {
451 c = sc_vtb_getc(&scp->vtb, j);
452 if (!IS_SPACE_CHAR(c))
458 for (j = scp->mouse_pos; j >= sol; --j) {
459 c = sc_vtb_getc(&scp->vtb, j);
460 if (IS_SPACE_CHAR(c))
464 for (j = scp->mouse_pos; j < eol; ++j) {
465 c = sc_vtb_getc(&scp->vtb, j);
466 if (IS_SPACE_CHAR(c))
472 /* copy the found word */
473 for (i = 0, j = start; j <= end; ++j)
474 cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j);
475 cut_buffer[i] = '\0';
476 scp->status |= MOUSE_CUTTING;
478 /* mark the region */
480 scp->mouse_cut_start = start;
481 scp->mouse_cut_end = end;
482 mark_for_update(scp, start);
483 mark_for_update(scp, end);
488 /* copy a line under the mouse pointer */
490 mouse_cut_line(scr_stat *scp)
495 if (scp->status & MOUSE_VISIBLE) {
496 /* remove the current cut mark */
498 if (scp->mouse_cut_start <= scp->mouse_cut_end) {
499 mark_for_update(scp, scp->mouse_cut_start);
500 mark_for_update(scp, scp->mouse_cut_end);
501 } else if (scp->mouse_cut_end >= 0) {
502 mark_for_update(scp, scp->mouse_cut_end);
503 mark_for_update(scp, scp->mouse_cut_start);
506 /* mark the entire line */
507 scp->mouse_cut_start =
508 (scp->mouse_pos / scp->xsize) * scp->xsize;
509 scp->mouse_cut_end = scp->mouse_cut_start + scp->xsize - 1;
510 mark_for_update(scp, scp->mouse_cut_start);
511 mark_for_update(scp, scp->mouse_cut_end);
514 /* copy the line into the cut buffer */
515 for (i = 0, j = scp->mouse_cut_start; j <= scp->mouse_cut_end; ++j)
516 cut_buffer[i++] = sc_vtb_getc(&scp->vtb, j);
517 cut_buffer[i++] = '\r';
518 cut_buffer[i] = '\0';
519 scp->status |= MOUSE_CUTTING;
523 /* extend the marked region to the mouse pointer position */
525 mouse_cut_extend(scr_stat *scp)
530 if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING)
531 && (scp->mouse_cut_end >= 0)) {
532 if (scp->mouse_cut_start <= scp->mouse_cut_end) {
533 start = scp->mouse_cut_start;
534 end = scp->mouse_cut_end;
536 start = scp->mouse_cut_end;
537 end = scp->mouse_cut_start - 1;
540 if (scp->mouse_pos > end) {
541 scp->mouse_cut_start = start;
542 scp->mouse_cut_end = end;
543 } else if (scp->mouse_pos < start) {
544 scp->mouse_cut_start = end + 1;
545 scp->mouse_cut_end = start;
547 if (scp->mouse_pos - start > end + 1 - scp->mouse_pos) {
548 scp->mouse_cut_start = start;
549 scp->mouse_cut_end = end;
551 scp->mouse_cut_start = end + 1;
552 scp->mouse_cut_end = start;
557 scp->status |= MOUSE_CUTTING;
561 /* paste cut buffer contents into the current vty */
563 mouse_paste(scr_stat *scp)
565 if (scp->status & MOUSE_VISIBLE)
566 sc_paste(scp, cut_buffer, strlen(cut_buffer));
569 #endif /* SC_NO_CUTPASTE */
572 sc_mouse_ioctl(struct tty *tp, u_long cmd, caddr_t data, int flag)
579 scp = SC_STAT(tp->t_dev);
583 case CONS_MOUSECTL: /* control mouse arrow */
584 mouse = (mouse_info_t*)data;
585 cur_scp = scp->sc->cur_scp;
587 switch (mouse->operation) {
589 if (ISSIGVALID(mouse->u.mode.signal)) {
590 scp->mouse_signal = mouse->u.mode.signal;
591 scp->mouse_proc = curproc;
592 scp->mouse_pid = curproc->p_pid;
595 scp->mouse_signal = 0;
596 scp->mouse_proc = NULL;
603 if (!(scp->sc->flags & SC_MOUSE_ENABLED)) {
604 scp->sc->flags |= SC_MOUSE_ENABLED;
605 cur_scp->status &= ~MOUSE_HIDDEN;
606 if (!ISGRAPHSC(cur_scp))
618 if (scp->sc->flags & SC_MOUSE_ENABLED) {
619 scp->sc->flags &= ~SC_MOUSE_ENABLED;
620 sc_remove_all_mouse(scp->sc);
631 scp->mouse_xpos = mouse->u.data.x;
632 scp->mouse_ypos = mouse->u.data.y;
639 scp->mouse_xpos += mouse->u.data.x;
640 scp->mouse_ypos += mouse->u.data.y;
646 mouse->u.data.x = scp->mouse_xpos;
647 mouse->u.data.y = scp->mouse_ypos;
649 mouse->u.data.buttons = scp->mouse_buttons;
653 case MOUSE_MOTION_EVENT:
654 /* send out mouse event on /dev/sysmouse */
656 /* this should maybe only be settable from /dev/consolectl SOS */
657 if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
661 if (mouse->u.data.x != 0 || mouse->u.data.y != 0) {
662 cur_scp->mouse_xpos += mouse->u.data.x;
663 cur_scp->mouse_ypos += mouse->u.data.y;
664 set_mouse_pos(cur_scp);
667 if (mouse->operation == MOUSE_ACTION) {
668 f = cur_scp->mouse_buttons ^ mouse->u.data.buttons;
669 cur_scp->mouse_buttons = mouse->u.data.buttons;
673 if (sysmouse_event(mouse) == 0)
677 * If any buttons are down or the mouse has moved a lot,
678 * stop the screen saver.
680 if (((mouse->operation == MOUSE_ACTION) && mouse->u.data.buttons)
681 || (mouse->u.data.x*mouse->u.data.x
682 + mouse->u.data.y*mouse->u.data.y
683 >= SC_WAKEUP_DELTA*SC_WAKEUP_DELTA)) {
684 sc_touch_scrn_saver();
687 cur_scp->status &= ~MOUSE_HIDDEN;
689 if (cur_scp->mouse_signal) {
690 /* has controlling process died? */
691 if (cur_scp->mouse_proc &&
692 (cur_scp->mouse_proc != pfind(cur_scp->mouse_pid))){
693 cur_scp->mouse_signal = 0;
694 cur_scp->mouse_proc = NULL;
695 cur_scp->mouse_pid = 0;
697 ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
702 if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
705 #ifndef SC_NO_CUTPASTE
706 if ((mouse->operation == MOUSE_ACTION) && f) {
707 /* process button presses */
708 if (cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN)
709 mouse_cut_start(cur_scp);
711 mouse_cut_end(cur_scp);
712 if (cur_scp->mouse_buttons & MOUSE_BUTTON2DOWN ||
713 cur_scp->mouse_buttons & MOUSE_BUTTON3DOWN)
714 mouse_paste(cur_scp);
716 #endif /* SC_NO_CUTPASTE */
719 case MOUSE_BUTTON_EVENT:
720 if ((mouse->u.event.id & MOUSE_BUTTONS) == 0)
722 if (mouse->u.event.value < 0)
725 /* this should maybe only be settable from /dev/consolectl SOS */
726 if (SC_VTY(tp->t_dev) != SC_CONSOLECTL)
729 if (mouse->u.event.value > 0)
730 cur_scp->mouse_buttons |= mouse->u.event.id;
732 cur_scp->mouse_buttons &= ~mouse->u.event.id;
734 if (sysmouse_event(mouse) == 0)
737 /* if a button is held down, stop the screen saver */
738 if (mouse->u.event.value > 0)
739 sc_touch_scrn_saver();
741 cur_scp->status &= ~MOUSE_HIDDEN;
743 if (cur_scp->mouse_signal) {
744 if (cur_scp->mouse_proc &&
745 (cur_scp->mouse_proc != pfind(cur_scp->mouse_pid))){
746 cur_scp->mouse_signal = 0;
747 cur_scp->mouse_proc = NULL;
748 cur_scp->mouse_pid = 0;
750 ksignal(cur_scp->mouse_proc, cur_scp->mouse_signal);
755 if (ISGRAPHSC(cur_scp) || (cut_buffer == NULL))
758 #ifndef SC_NO_CUTPASTE
759 switch (mouse->u.event.id) {
760 case MOUSE_BUTTON1DOWN:
761 switch (mouse->u.event.value % 4) {
763 mouse_cut_end(cur_scp);
765 case 1: /* single click: start cut operation */
766 mouse_cut_start(cur_scp);
768 case 2: /* double click: cut a word */
769 mouse_cut_word(cur_scp);
770 mouse_cut_end(cur_scp);
772 case 3: /* triple click: cut a line */
773 mouse_cut_line(cur_scp);
774 mouse_cut_end(cur_scp);
778 case SC_MOUSE_PASTEBUTTON:
779 switch (mouse->u.event.value) {
783 mouse_paste(cur_scp);
787 case SC_MOUSE_EXTENDBUTTON:
788 switch (mouse->u.event.value) {
790 if (!(cur_scp->mouse_buttons & MOUSE_BUTTON1DOWN))
791 mouse_cut_end(cur_scp);
794 mouse_cut_extend(cur_scp);
799 #endif /* SC_NO_CUTPASTE */
802 case MOUSE_MOUSECHAR:
803 if (mouse->u.mouse_char < 0) {
804 mouse->u.mouse_char = scp->sc->mouse_char;
806 if (mouse->u.mouse_char >= (unsigned char)-1 - 4)
809 sc_remove_all_mouse(scp->sc);
810 #ifndef SC_NO_FONT_LOADING
811 if (ISTEXTSC(cur_scp) && (cur_scp->font != NULL))
812 sc_load_font(cur_scp, 0, cur_scp->font_size, cur_scp->font,
813 cur_scp->sc->mouse_char, 4);
815 scp->sc->mouse_char = mouse->u.mouse_char;
830 #endif /* SC_NO_SYSMOUSE */