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
40static 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
51static 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};
85static 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};
119static 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};
137static 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
158static VGLBitmap VGLMouseLargeAndMask =
159  VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
160                        LargeAndMask);
161static VGLBitmap VGLMouseLargeOrMask =
162  VGLBITMAP_INITIALIZER(MEMBUF, LARGE_MOUSE_IMG_XSIZE, LARGE_MOUSE_IMG_YSIZE,
163                        LargeOrMask);
164static VGLBitmap VGLMouseSmallAndMask =
165  VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
166                        SmallAndMask);
167static VGLBitmap VGLMouseSmallOrMask =
168  VGLBITMAP_INITIALIZER(MEMBUF, SMALL_MOUSE_IMG_XSIZE, SMALL_MOUSE_IMG_YSIZE,
169                        SmallOrMask);
170static VGLBitmap *VGLMouseAndMask, *VGLMouseOrMask;
171static int VGLMouseShown = VGL_MOUSEHIDE;
172static int VGLMouseXpos = 0;
173static int VGLMouseYpos = 0;
174static int VGLMouseButtons = 0;
175static volatile sig_atomic_t VGLMintpending;
176static 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
184int
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
211void
212VGLMouseMode(int mode)
213{
214  __VGLMouseMode(mode);
215}
216
217static void
218VGLMouseAction(int dummy)
219{
220  struct mouse_info mouseinfo;
221  int mousemode;
222
223  if (VGLMsuppressint) {
224    VGLMintpending = 1;
225    return;
226  }
227again:
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
251void
252VGLMouseSetImage(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
271void
272VGLMouseSetStdImage()
273{
274  if (VGLDisplay->VXsize > 800)
275    VGLMouseSetImage(&VGLMouseLargeAndMask, &VGLMouseLargeOrMask);
276  else
277    VGLMouseSetImage(&VGLMouseSmallAndMask, &VGLMouseSmallOrMask);
278}
279
280int
281VGLMouseInit(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
331void
332VGLMouseRestore(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
347int
348VGLMouseStatus(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
358void
359VGLMouseFreeze(void)
360{
361  INTOFF();
362}
363
364int
365VGLMouseFreezeXY(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
378int
379VGLMouseOverlap(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
398void
399VGLMouseMerge(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
417void
418VGLMouseUnFreeze()
419{
420  INTON();
421}
422