xref: /freebsd/lib/libvgl/mouse.c (revision 815b7436a7c6302365b6514194d27d41cb736227)
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 #include <stdio.h>
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/signal.h>
36 #include <sys/consio.h>
37 #include <sys/fbio.h>
38 #include "vgl.h"
39 
40 static void VGLMouseAction(int dummy);
41 
42 #define BORDER	0xff	/* default border -- light white in rgb 3:3:2 */
43 #define INTERIOR 0xa0	/* default interior -- red in rgb 3:3:2 */
44 #define LARGE_MOUSE_IMG_XSIZE	19
45 #define LARGE_MOUSE_IMG_YSIZE	32
46 #define SMALL_MOUSE_IMG_XSIZE	10
47 #define SMALL_MOUSE_IMG_YSIZE	16
48 #define X	0xff	/* any nonzero in And mask means part of cursor */
49 #define B	BORDER
50 #define I	INTERIOR
51 static byte LargeAndMask[] = {
52   X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
53   X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
54   X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
55   X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
56   X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,
57   X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,0,
58   X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,0,
59   X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,0,
60   X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,0,
61   X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,0,
62   X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,
63   X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,
64   X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,
65   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,
66   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,
67   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,0,
68   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,0,
69   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
70   X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
71   X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,
72   X,X,X,X,X,X,X,X,X,X,X,X,0,0,0,0,0,0,0,
73   X,X,X,X,X,X,0,X,X,X,X,X,X,0,0,0,0,0,0,
74   X,X,X,X,X,0,0,X,X,X,X,X,X,0,0,0,0,0,0,
75   X,X,X,X,0,0,0,0,X,X,X,X,X,X,0,0,0,0,0,
76   X,X,X,0,0,0,0,0,X,X,X,X,X,X,0,0,0,0,0,
77   X,X,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,0,
78   0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,0,
79   0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,
80   0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,0,
81   0,0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,
82   0,0,0,0,0,0,0,0,0,0,0,X,X,X,X,X,X,0,0,
83   0,0,0,0,0,0,0,0,0,0,0,0,X,X,X,X,0,0,0,
84 };
85 static byte LargeOrMask[] = {
86   B,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
87   B,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
88   B,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
89   B,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
90   B,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,0,
91   B,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,0,
92   B,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,0,
93   B,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,0,
94   B,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,0,
95   B,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,0,
96   B,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,
97   B,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,
98   B,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,
99   B,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,
100   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,0,
101   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,0,
102   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,0,
103   B,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,B,
104   B,I,I,I,I,I,I,I,I,I,I,B,B,B,B,B,B,B,B,
105   B,I,I,I,I,I,I,I,I,I,I,B,0,0,0,0,0,0,0,
106   B,I,I,I,I,I,B,I,I,I,I,B,0,0,0,0,0,0,0,
107   B,I,I,I,I,B,0,B,I,I,I,I,B,0,0,0,0,0,0,
108   B,I,I,I,B,0,0,B,I,I,I,I,B,0,0,0,0,0,0,
109   B,I,I,B,0,0,0,0,B,I,I,I,I,B,0,0,0,0,0,
110   B,I,B,0,0,0,0,0,B,I,I,I,I,B,0,0,0,0,0,
111   B,B,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,0,
112   0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,0,
113   0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,
114   0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,0,
115   0,0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,
116   0,0,0,0,0,0,0,0,0,0,0,B,I,I,I,I,B,0,0,
117   0,0,0,0,0,0,0,0,0,0,0,0,B,B,B,B,0,0,0,
118 };
119 static byte SmallAndMask[] = {
120   X,X,0,0,0,0,0,0,0,0,
121   X,X,X,0,0,0,0,0,0,0,
122   X,X,X,X,0,0,0,0,0,0,
123   X,X,X,X,X,0,0,0,0,0,
124   X,X,X,X,X,X,0,0,0,0,
125   X,X,X,X,X,X,X,0,0,0,
126   X,X,X,X,X,X,X,X,0,0,
127   X,X,X,X,X,X,X,X,X,0,
128   X,X,X,X,X,X,X,X,X,X,
129   X,X,X,X,X,X,X,X,X,X,
130   X,X,X,X,X,X,X,0,0,0,
131   X,X,X,0,X,X,X,X,0,0,
132   X,X,0,0,X,X,X,X,0,0,
133   0,0,0,0,0,X,X,X,X,0,
134   0,0,0,0,0,X,X,X,X,0,
135   0,0,0,0,0,0,X,X,0,0,
136 };
137 static byte SmallOrMask[] = {
138   B,B,0,0,0,0,0,0,0,0,
139   B,I,B,0,0,0,0,0,0,0,
140   B,I,I,B,0,0,0,0,0,0,
141   B,I,I,I,B,0,0,0,0,0,
142   B,I,I,I,I,B,0,0,0,0,
143   B,I,I,I,I,I,B,0,0,0,
144   B,I,I,I,I,I,I,B,0,0,
145   B,I,I,I,I,I,I,I,B,0,
146   B,I,I,I,I,I,I,I,I,B,
147   B,I,I,I,I,I,B,B,B,B,
148   B,I,I,B,I,I,B,0,0,0,
149   B,I,B,0,B,I,I,B,0,0,
150   B,B,0,0,B,I,I,B,0,0,
151   0,0,0,0,0,B,I,I,B,0,
152   0,0,0,0,0,B,I,I,B,0,
153   0,0,0,0,0,0,B,B,0,0,
154 };
155 #undef X
156 #undef B
157 #undef I
158 static VGLBitmap VGLMouseLargeAndMask =
159   VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
160                         LargeAndMask);
161 static VGLBitmap VGLMouseLargeOrMask =
162   VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
163                         LargeOrMask);
164 static VGLBitmap VGLMouseSmallAndMask =
165   VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
166                         SmallAndMask);
167 static VGLBitmap VGLMouseSmallOrMask =
168   VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
169                         SmallOrMask);
170 static VGLBitmap *VGLMouseAndMask, *VGLMouseOrMask;
171 static int VGLMouseShown = VGL_MOUSEHIDE;
172 static int VGLMouseXpos = 0;
173 static int VGLMouseYpos = 0;
174 static int VGLMouseButtons = 0;
175 static volatile sig_atomic_t VGLMintpending;
176 static volatile sig_atomic_t VGLMsuppressint;
177 
178 #define	INTOFF()	(VGLMsuppressint++)
179 #define	INTON()		do { 						\
180 				if (--VGLMsuppressint == 0 && VGLMintpending) \
181 					VGLMouseAction(0);		\
182 			} while (0)
183 
184 int
185 __VGLMouseMode(int mode)
186 {
187   int oldmode;
188 
189   INTOFF();
190   oldmode = VGLMouseShown;
191   if (mode == VGL_MOUSESHOW) {
192     if (VGLMouseShown == VGL_MOUSEHIDE) {
193       VGLMouseShown = VGL_MOUSESHOW;
194       __VGLBitmapCopy(&VGLVDisplay, VGLMouseXpos, VGLMouseYpos,
195                       VGLDisplay, VGLMouseXpos, VGLMouseYpos,
196                       VGLMouseAndMask->VXsize, -VGLMouseAndMask->VYsize);
197     }
198   }
199   else {
200     if (VGLMouseShown == VGL_MOUSESHOW) {
201       VGLMouseShown = VGL_MOUSEHIDE;
202       __VGLBitmapCopy(&VGLVDisplay, VGLMouseXpos, VGLMouseYpos,
203                       VGLDisplay, VGLMouseXpos, VGLMouseYpos,
204                       VGLMouseAndMask->VXsize, VGLMouseAndMask->VYsize);
205     }
206   }
207   INTON();
208   return oldmode;
209 }
210 
211 void
212 VGLMouseMode(int mode)
213 {
214   __VGLMouseMode(mode);
215 }
216 
217 static void
218 VGLMouseAction(int dummy)
219 {
220   struct mouse_info mouseinfo;
221   int mousemode;
222 
223   if (VGLMsuppressint) {
224     VGLMintpending = 1;
225     return;
226   }
227 again:
228   INTOFF();
229   VGLMintpending = 0;
230   mouseinfo.operation = MOUSE_GETINFO;
231   ioctl(0, CONS_MOUSECTL, &mouseinfo);
232   if (VGLMouseXpos != mouseinfo.u.data.x ||
233       VGLMouseYpos != mouseinfo.u.data.y) {
234     mousemode = __VGLMouseMode(VGL_MOUSEHIDE);
235     VGLMouseXpos = mouseinfo.u.data.x;
236     VGLMouseYpos = mouseinfo.u.data.y;
237     __VGLMouseMode(mousemode);
238   }
239   VGLMouseButtons = mouseinfo.u.data.buttons;
240 
241   /*
242    * Loop to handle any new (suppressed) signals.  This is INTON() without
243    * recursion.  !SA_RESTART prevents recursion in signal handling.  So the
244    * maximum recursion is 2 levels.
245    */
246   VGLMsuppressint = 0;
247   if (VGLMintpending)
248     goto again;
249 }
250 
251 void
252 VGLMouseSetImage(VGLBitmap *AndMask, VGLBitmap *OrMask)
253 {
254   int mousemode;
255 
256   mousemode = __VGLMouseMode(VGL_MOUSEHIDE);
257 
258   VGLMouseAndMask = AndMask;
259 
260   if (VGLMouseOrMask != NULL) {
261     free(VGLMouseOrMask->Bitmap);
262     free(VGLMouseOrMask);
263   }
264   VGLMouseOrMask = VGLBitmapCreate(MEMBUF, OrMask->VXsize, OrMask->VYsize, 0);
265   VGLBitmapAllocateBits(VGLMouseOrMask);
266   VGLBitmapCvt(OrMask, VGLMouseOrMask);
267 
268   __VGLMouseMode(mousemode);
269 }
270 
271 void
272 VGLMouseSetStdImage()
273 {
274   if (VGLDisplay->VXsize > 800)
275     VGLMouseSetImage(&VGLMouseLargeAndMask, &VGLMouseLargeOrMask);
276   else
277     VGLMouseSetImage(&VGLMouseSmallAndMask, &VGLMouseSmallOrMask);
278 }
279 
280 int
281 VGLMouseInit(int mode)
282 {
283   struct mouse_info mouseinfo;
284   VGLBitmap *ormask;
285   int border, error, i, interior;
286 
287   switch (VGLModeInfo.vi_mem_model) {
288   case V_INFO_MM_PACKED:
289   case V_INFO_MM_PLANAR:
290     border = 0x0f;
291     interior = 0x04;
292     break;
293   case V_INFO_MM_VGAX:
294     border = 0x3f;
295     interior = 0x24;
296     break;
297   default:
298     border = BORDER;
299     interior = INTERIOR;
300     break;
301   }
302   if (VGLModeInfo.vi_mode == M_BG640x480)
303     border = 0;		/* XXX (palette makes 0x04 look like 0x0f) */
304   if (getenv("VGLMOUSEBORDERCOLOR") != NULL)
305     border = strtoul(getenv("VGLMOUSEBORDERCOLOR"), NULL, 0);
306   if (getenv("VGLMOUSEINTERIORCOLOR") != NULL)
307     interior = strtoul(getenv("VGLMOUSEINTERIORCOLOR"), NULL, 0);
308   ormask = &VGLMouseLargeOrMask;
309   for (i = 0; i < ormask->VXsize * ormask->VYsize; i++)
310     ormask->Bitmap[i] = ormask->Bitmap[i] == BORDER ?  border :
311                         ormask->Bitmap[i] == INTERIOR ? interior : 0;
312   ormask = &VGLMouseSmallOrMask;
313   for (i = 0; i < ormask->VXsize * ormask->VYsize; i++)
314     ormask->Bitmap[i] = ormask->Bitmap[i] == BORDER ?  border :
315                         ormask->Bitmap[i] == INTERIOR ? interior : 0;
316   VGLMouseSetStdImage();
317   mouseinfo.operation = MOUSE_MODE;
318   mouseinfo.u.mode.signal = SIGUSR2;
319   if ((error = ioctl(0, CONS_MOUSECTL, &mouseinfo)))
320     return error;
321   signal(SIGUSR2, VGLMouseAction);
322   mouseinfo.operation = MOUSE_GETINFO;
323   ioctl(0, CONS_MOUSECTL, &mouseinfo);
324   VGLMouseXpos = mouseinfo.u.data.x;
325   VGLMouseYpos = mouseinfo.u.data.y;
326   VGLMouseButtons = mouseinfo.u.data.buttons;
327   VGLMouseMode(mode);
328   return 0;
329 }
330 
331 void
332 VGLMouseRestore(void)
333 {
334   struct mouse_info mouseinfo;
335 
336   INTOFF();
337   mouseinfo.operation = MOUSE_GETINFO;
338   if (ioctl(0, CONS_MOUSECTL, &mouseinfo) == 0) {
339     mouseinfo.operation = MOUSE_MOVEABS;
340     mouseinfo.u.data.x = VGLMouseXpos;
341     mouseinfo.u.data.y = VGLMouseYpos;
342     ioctl(0, CONS_MOUSECTL, &mouseinfo);
343   }
344   INTON();
345 }
346 
347 int
348 VGLMouseStatus(int *x, int *y, char *buttons)
349 {
350   INTOFF();
351   *x =  VGLMouseXpos;
352   *y =  VGLMouseYpos;
353   *buttons =  VGLMouseButtons;
354   INTON();
355   return VGLMouseShown;
356 }
357 
358 void
359 VGLMouseFreeze(void)
360 {
361   INTOFF();
362 }
363 
364 int
365 VGLMouseFreezeXY(int x, int y)
366 {
367   INTOFF();
368   if (VGLMouseShown != VGL_MOUSESHOW)
369     return 0;
370   if (x >= VGLMouseXpos && x < VGLMouseXpos + VGLMouseAndMask->VXsize &&
371       y >= VGLMouseYpos && y < VGLMouseYpos + VGLMouseAndMask->VYsize &&
372       VGLMouseAndMask->Bitmap[(y-VGLMouseYpos)*VGLMouseAndMask->VXsize+
373                               (x-VGLMouseXpos)])
374     return 1;
375   return 0;
376 }
377 
378 int
379 VGLMouseOverlap(int x, int y, int width, int hight)
380 {
381   int overlap;
382 
383   if (VGLMouseShown != VGL_MOUSESHOW)
384     return 0;
385   if (x > VGLMouseXpos)
386     overlap = (VGLMouseXpos + VGLMouseAndMask->VXsize) - x;
387   else
388     overlap = (x + width) - VGLMouseXpos;
389   if (overlap <= 0)
390     return 0;
391   if (y > VGLMouseYpos)
392     overlap = (VGLMouseYpos + VGLMouseAndMask->VYsize) - y;
393   else
394     overlap = (y + hight) - VGLMouseYpos;
395   return overlap > 0;
396 }
397 
398 void
399 VGLMouseMerge(int x, int y, int width, byte *line)
400 {
401   int pos, x1, xend, xstart;
402 
403   xstart = x;
404   if (xstart < VGLMouseXpos)
405     xstart = VGLMouseXpos;
406   xend = x + width;
407   if (xend > VGLMouseXpos + VGLMouseAndMask->VXsize)
408     xend = VGLMouseXpos + VGLMouseAndMask->VXsize;
409   for (x1 = xstart; x1 < xend; x1++) {
410     pos = (y - VGLMouseYpos) * VGLMouseAndMask->VXsize + x1 - VGLMouseXpos;
411     if (VGLMouseAndMask->Bitmap[pos])
412       bcopy(&VGLMouseOrMask->Bitmap[pos * VGLDisplay->PixelBytes],
413             &line[(x1 - x) * VGLDisplay->PixelBytes], VGLDisplay->PixelBytes);
414   }
415 }
416 
417 void
418 VGLMouseUnFreeze()
419 {
420   INTON();
421 }
422