xref: /freebsd/lib/libvgl/mouse.c (revision f6a3b357e9be4c6423c85eff9a847163a0d307c8)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1991-1997 Søren Schmidt
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
37 #include <sys/signal.h>
38 #include <sys/consio.h>
39 #include <sys/fbio.h>
40 #include "vgl.h"
41 
42 static void VGLMouseAction(int dummy);
43 
44 #define BORDER	0xff	/* default border -- light white in rgb 3:3:2 */
45 #define INTERIOR 0xa0	/* default interior -- red in rgb 3:3:2 */
46 #define LARGE_MOUSE_IMG_XSIZE	19
47 #define LARGE_MOUSE_IMG_YSIZE	32
48 #define SMALL_MOUSE_IMG_XSIZE	10
49 #define SMALL_MOUSE_IMG_YSIZE	16
50 #define X	0xff	/* any nonzero in And mask means part of cursor */
51 #define B	BORDER
52 #define I	INTERIOR
53 static byte LargeAndMask[] = {
54   X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
55   X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
56   X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
57   X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
58   X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,
59   X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,
60   X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,
61   X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,
62   X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,
63   X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,
64   X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,
65   X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,
66   X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,
67   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,
68   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,
69   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,
70   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,
71   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
72   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
73   X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,
74   X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,
75   X,X,X,X,X,X,0,X,X,X,X,X,X,0,0,0,0,0,0,
76   X,X,X,X,X,0,0,X,X,X,X,X,X,0,0,0,0,0,0,
77   X,X,X,X,0,0,0,0,X,X,X,X,X,X,0,0,0,0,0,
78   X,X,X,0,0,0,0,0,X,X,X,X,X,X,0,0,0,0,0,
79   X,X,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,0,
80   0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,0,
81   0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,
82   0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,
83   0,0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,
84   0,0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,
85   0,0,0,0,0,0,0,0,0,0,0,0,X,X,X,X,0,0,0,
86 };
87 static byte LargeOrMask[] = {
88   B,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
89   B,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
90   B,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
91   B,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
92   B,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,
93   B,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,
94   B,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,
95   B,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,
96   B,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,
97   B,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,
98   B,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,
99   B,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,
100   B,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,
101   B,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,
102   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,
103   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,
104   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,
105   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,
106   B,I,I,I,I,I,I,I,I,I,I,B,B,B,B,B,B,B,B,
107   B,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,
108   B,I,I,I,I,I,B,I,I,I,I,B,0,0,0,0,0,0,0,
109   B,I,I,I,I,B,0,B,I,I,I,I,B,0,0,0,0,0,0,
110   B,I,I,I,B,0,0,B,I,I,I,I,B,0,0,0,0,0,0,
111   B,I,I,B,0,0,0,0,B,I,I,I,I,B,0,0,0,0,0,
112   B,I,B,0,0,0,0,0,B,I,I,I,I,B,0,0,0,0,0,
113   B,B,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,0,
114   0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,0,
115   0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,
116   0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,
117   0,0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,
118   0,0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,
119   0,0,0,0,0,0,0,0,0,0,0,0,B,B,B,B,0,0,0,
120 };
121 static byte SmallAndMask[] = {
122   X,X,0,0,0,0,0,0,0,0,
123   X,X,X,0,0,0,0,0,0,0,
124   X,X,X,X,0,0,0,0,0,0,
125   X,X,X,X,X,0,0,0,0,0,
126   X,X,X,X,X,X,0,0,0,0,
127   X,X,X,X,X,X,X,0,0,0,
128   X,X,X,X,X,X,X,X,0,0,
129   X,X,X,X,X,X,X,X,X,0,
130   X,X,X,X,X,X,X,X,X,X,
131   X,X,X,X,X,X,X,X,X,X,
132   X,X,X,X,X,X,X,0,0,0,
133   X,X,X,0,X,X,X,X,0,0,
134   X,X,0,0,X,X,X,X,0,0,
135   0,0,0,0,0,X,X,X,X,0,
136   0,0,0,0,0,X,X,X,X,0,
137   0,0,0,0,0,0,X,X,0,0,
138 };
139 static byte SmallOrMask[] = {
140   B,B,0,0,0,0,0,0,0,0,
141   B,I,B,0,0,0,0,0,0,0,
142   B,I,I,B,0,0,0,0,0,0,
143   B,I,I,I,B,0,0,0,0,0,
144   B,I,I,I,I,B,0,0,0,0,
145   B,I,I,I,I,I,B,0,0,0,
146   B,I,I,I,I,I,I,B,0,0,
147   B,I,I,I,I,I,I,I,B,0,
148   B,I,I,I,I,I,I,I,I,B,
149   B,I,I,I,I,I,B,B,B,B,
150   B,I,I,B,I,I,B,0,0,0,
151   B,I,B,0,B,I,I,B,0,0,
152   B,B,0,0,B,I,I,B,0,0,
153   0,0,0,0,0,B,I,I,B,0,
154   0,0,0,0,0,B,I,I,B,0,
155   0,0,0,0,0,0,B,B,0,0,
156 };
157 #undef X
158 #undef B
159 #undef I
160 static VGLBitmap VGLMouseLargeAndMask =
161   VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
162                         LargeAndMask);
163 static VGLBitmap VGLMouseLargeOrMask =
164   VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
165                         LargeOrMask);
166 static VGLBitmap VGLMouseSmallAndMask =
167   VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
168                         SmallAndMask);
169 static VGLBitmap VGLMouseSmallOrMask =
170   VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
171                         SmallOrMask);
172 static VGLBitmap *VGLMouseAndMask, *VGLMouseOrMask;
173 static int VGLMouseShown = VGL_MOUSEHIDE;
174 static int VGLMouseXpos = 0;
175 static int VGLMouseYpos = 0;
176 static int VGLMouseButtons = 0;
177 static volatile sig_atomic_t VGLMintpending;
178 static volatile sig_atomic_t VGLMsuppressint;
179 
180 #define	INTOFF()	(VGLMsuppressint++)
181 #define	INTON()		do { 						\
182 				if (--VGLMsuppressint == 0 && VGLMintpending) \
183 					VGLMouseAction(0);		\
184 			} while (0)
185 
186 int
187 __VGLMouseMode(int mode)
188 {
189   int oldmode;
190 
191   INTOFF();
192   oldmode = VGLMouseShown;
193   if (mode == VGL_MOUSESHOW) {
194     if (VGLMouseShown == VGL_MOUSEHIDE) {
195       VGLMouseShown = VGL_MOUSESHOW;
196       __VGLBitmapCopy(&VGLVDisplay, VGLMouseXpos, VGLMouseYpos,
197                       VGLDisplay, VGLMouseXpos, VGLMouseYpos,
198                       VGLMouseAndMask->VXsize, -VGLMouseAndMask->VYsize);
199     }
200   }
201   else {
202     if (VGLMouseShown == VGL_MOUSESHOW) {
203       VGLMouseShown = VGL_MOUSEHIDE;
204       __VGLBitmapCopy(&VGLVDisplay, VGLMouseXpos, VGLMouseYpos,
205                       VGLDisplay, VGLMouseXpos, VGLMouseYpos,
206                       VGLMouseAndMask->VXsize, VGLMouseAndMask->VYsize);
207     }
208   }
209   INTON();
210   return oldmode;
211 }
212 
213 void
214 VGLMouseMode(int mode)
215 {
216   __VGLMouseMode(mode);
217 }
218 
219 static void
220 VGLMouseAction(int dummy)
221 {
222   struct mouse_info mouseinfo;
223   int mousemode;
224 
225   if (VGLMsuppressint) {
226     VGLMintpending = 1;
227     return;
228   }
229 again:
230   INTOFF();
231   VGLMintpending = 0;
232   mouseinfo.operation = MOUSE_GETINFO;
233   ioctl(0, CONS_MOUSECTL, &mouseinfo);
234   if (VGLMouseXpos != mouseinfo.u.data.x ||
235       VGLMouseYpos != mouseinfo.u.data.y) {
236     mousemode = __VGLMouseMode(VGL_MOUSEHIDE);
237     VGLMouseXpos = mouseinfo.u.data.x;
238     VGLMouseYpos = mouseinfo.u.data.y;
239     __VGLMouseMode(mousemode);
240   }
241   VGLMouseButtons = mouseinfo.u.data.buttons;
242 
243   /*
244    * Loop to handle any new (suppressed) signals.  This is INTON() without
245    * recursion.  !SA_RESTART prevents recursion in signal handling.  So the
246    * maximum recursion is 2 levels.
247    */
248   VGLMsuppressint = 0;
249   if (VGLMintpending)
250     goto again;
251 }
252 
253 void
254 VGLMouseSetImage(VGLBitmap *AndMask, VGLBitmap *OrMask)
255 {
256   int mousemode;
257 
258   mousemode = __VGLMouseMode(VGL_MOUSEHIDE);
259 
260   VGLMouseAndMask = AndMask;
261 
262   if (VGLMouseOrMask != NULL) {
263     free(VGLMouseOrMask->Bitmap);
264     free(VGLMouseOrMask);
265   }
266   VGLMouseOrMask = VGLBitmapCreate(MEMBUF, OrMask->VXsize, OrMask->VYsize, 0);
267   VGLBitmapAllocateBits(VGLMouseOrMask);
268   VGLBitmapCvt(OrMask, VGLMouseOrMask);
269 
270   __VGLMouseMode(mousemode);
271 }
272 
273 void
274 VGLMouseSetStdImage()
275 {
276   if (VGLDisplay->VXsize > 800)
277     VGLMouseSetImage(&VGLMouseLargeAndMask, &VGLMouseLargeOrMask);
278   else
279     VGLMouseSetImage(&VGLMouseSmallAndMask, &VGLMouseSmallOrMask);
280 }
281 
282 int
283 VGLMouseInit(int mode)
284 {
285   struct mouse_info mouseinfo;
286   VGLBitmap *ormask;
287   int andmask, border, error, i, interior;
288 
289   switch (VGLModeInfo.vi_mem_model) {
290   case V_INFO_MM_PACKED:
291   case V_INFO_MM_PLANAR:
292     andmask = 0x0f;
293     border = 0x0f;
294     interior = 0x04;
295     break;
296   case V_INFO_MM_VGAX:
297     andmask = 0x3f;
298     border = 0x3f;
299     interior = 0x24;
300     break;
301   default:
302     andmask = 0xff;
303     border = BORDER;
304     interior = INTERIOR;
305     break;
306   }
307   if (VGLModeInfo.vi_mode == M_BG640x480)
308     border = 0;		/* XXX (palette makes 0x04 look like 0x0f) */
309   if (getenv("VGLMOUSEBORDERCOLOR") != NULL)
310     border = strtoul(getenv("VGLMOUSEBORDERCOLOR"), NULL, 0);
311   if (getenv("VGLMOUSEINTERIORCOLOR") != NULL)
312     interior = strtoul(getenv("VGLMOUSEINTERIORCOLOR"), NULL, 0);
313   ormask = &VGLMouseLargeOrMask;
314   for (i = 0; i < ormask->VXsize * ormask->VYsize; i++)
315     ormask->Bitmap[i] = ormask->Bitmap[i] == BORDER ?  border :
316                         ormask->Bitmap[i] == INTERIOR ? interior : 0;
317   ormask = &VGLMouseSmallOrMask;
318   for (i = 0; i < ormask->VXsize * ormask->VYsize; i++)
319     ormask->Bitmap[i] = ormask->Bitmap[i] == BORDER ?  border :
320                         ormask->Bitmap[i] == INTERIOR ? interior : 0;
321   VGLMouseSetStdImage();
322   mouseinfo.operation = MOUSE_MODE;
323   mouseinfo.u.mode.signal = SIGUSR2;
324   if ((error = ioctl(0, CONS_MOUSECTL, &mouseinfo)))
325     return error;
326   signal(SIGUSR2, VGLMouseAction);
327   mouseinfo.operation = MOUSE_GETINFO;
328   ioctl(0, CONS_MOUSECTL, &mouseinfo);
329   VGLMouseXpos = mouseinfo.u.data.x;
330   VGLMouseYpos = mouseinfo.u.data.y;
331   VGLMouseButtons = mouseinfo.u.data.buttons;
332   VGLMouseMode(mode);
333   return 0;
334 }
335 
336 void
337 VGLMouseRestore(void)
338 {
339   struct mouse_info mouseinfo;
340 
341   INTOFF();
342   mouseinfo.operation = MOUSE_GETINFO;
343   if (ioctl(0, CONS_MOUSECTL, &mouseinfo) == 0) {
344     mouseinfo.operation = MOUSE_MOVEABS;
345     mouseinfo.u.data.x = VGLMouseXpos;
346     mouseinfo.u.data.y = VGLMouseYpos;
347     ioctl(0, CONS_MOUSECTL, &mouseinfo);
348   }
349   INTON();
350 }
351 
352 int
353 VGLMouseStatus(int *x, int *y, char *buttons)
354 {
355   INTOFF();
356   *x =  VGLMouseXpos;
357   *y =  VGLMouseYpos;
358   *buttons =  VGLMouseButtons;
359   INTON();
360   return VGLMouseShown;
361 }
362 
363 void
364 VGLMouseFreeze(void)
365 {
366   INTOFF();
367 }
368 
369 int
370 VGLMouseFreezeXY(int x, int y)
371 {
372   INTOFF();
373   if (VGLMouseShown != VGL_MOUSESHOW)
374     return 0;
375   if (x >= VGLMouseXpos && x < VGLMouseXpos + VGLMouseAndMask->VXsize &&
376       y >= VGLMouseYpos && y < VGLMouseYpos + VGLMouseAndMask->VYsize &&
377       VGLMouseAndMask->Bitmap[(y-VGLMouseYpos)*VGLMouseAndMask->VXsize+
378                               (x-VGLMouseXpos)])
379     return 1;
380   return 0;
381 }
382 
383 int
384 VGLMouseOverlap(int x, int y, int width, int hight)
385 {
386   int overlap;
387 
388   if (VGLMouseShown != VGL_MOUSESHOW)
389     return 0;
390   if (x > VGLMouseXpos)
391     overlap = (VGLMouseXpos + VGLMouseAndMask->VXsize) - x;
392   else
393     overlap = (x + width) - VGLMouseXpos;
394   if (overlap <= 0)
395     return 0;
396   if (y > VGLMouseYpos)
397     overlap = (VGLMouseYpos + VGLMouseAndMask->VYsize) - y;
398   else
399     overlap = (y + hight) - VGLMouseYpos;
400   return overlap > 0;
401 }
402 
403 void
404 VGLMouseMerge(int x, int y, int width, byte *line)
405 {
406   int pos, x1, xend, xstart;
407 
408   xstart = x;
409   if (xstart < VGLMouseXpos)
410     xstart = VGLMouseXpos;
411   xend = x + width;
412   if (xend > VGLMouseXpos + VGLMouseAndMask->VXsize)
413     xend = VGLMouseXpos + VGLMouseAndMask->VXsize;
414   for (x1 = xstart; x1 < xend; x1++) {
415     pos = (y - VGLMouseYpos) * VGLMouseAndMask->VXsize + x1 - VGLMouseXpos;
416     if (VGLMouseAndMask->Bitmap[pos])
417       bcopy(&VGLMouseOrMask->Bitmap[pos * VGLDisplay->PixelBytes],
418             &line[(x1 - x) * VGLDisplay->PixelBytes], VGLDisplay->PixelBytes);
419   }
420 }
421 
422 void
423 VGLMouseUnFreeze()
424 {
425   INTON();
426 }
427