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
42static 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
53static 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};
87static 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};
121static 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};
139static 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
160static VGLBitmap VGLMouseLargeAndMask =
161  VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
162                        LargeAndMask);
163static VGLBitmap VGLMouseLargeOrMask =
164  VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
165                        LargeOrMask);
166static VGLBitmap VGLMouseSmallAndMask =
167  VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
168                        SmallAndMask);
169static VGLBitmap VGLMouseSmallOrMask =
170  VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
171                        SmallOrMask);
172static VGLBitmap *VGLMouseAndMask, *VGLMouseOrMask;
173static int VGLMouseShown = VGL_MOUSEHIDE;
174static int VGLMouseXpos = 0;
175static int VGLMouseYpos = 0;
176static int VGLMouseButtons = 0;
177static volatile sig_atomic_t VGLMintpending;
178static 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
186int
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
213void
214VGLMouseMode(int mode)
215{
216  __VGLMouseMode(mode);
217}
218
219static void
220VGLMouseAction(int dummy)
221{
222  struct mouse_info mouseinfo;
223  int mousemode;
224
225  if (VGLMsuppressint) {
226    VGLMintpending = 1;
227    return;
228  }
229again:
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
253void
254VGLMouseSetImage(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
273void
274VGLMouseSetStdImage()
275{
276  if (VGLDisplay->VXsize > 800)
277    VGLMouseSetImage(&VGLMouseLargeAndMask, &VGLMouseLargeOrMask);
278  else
279    VGLMouseSetImage(&VGLMouseSmallAndMask, &VGLMouseSmallOrMask);
280}
281
282int
283VGLMouseInit(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
336void
337VGLMouseRestore(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
352int
353VGLMouseStatus(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
363void
364VGLMouseFreeze(void)
365{
366  INTOFF();
367}
368
369int
370VGLMouseFreezeXY(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
383int
384VGLMouseOverlap(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
403void
404VGLMouseMerge(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
422void
423VGLMouseUnFreeze()
424{
425  INTON();
426}
427