VirtualBox

source: vbox/trunk/src/VBox/Additions/x11/vboxvideo/pointer.c

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.5 KB
Line 
1/* $Id: pointer.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox X11 Additions graphics driver utility functions
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31#ifndef PCIACCESS
32# include "xf86Pci.h"
33# include <Pci.h>
34#endif
35
36#include "xf86.h"
37#define NEED_XF86_TYPES
38#include "compiler.h"
39#include "cursorstr.h"
40#include "servermd.h"
41
42#include "vboxvideo.h"
43
44#ifdef XORG_7X
45# include <stdlib.h>
46# include <string.h>
47#endif
48
49#define VBOX_MAX_CURSOR_WIDTH 64
50#define VBOX_MAX_CURSOR_HEIGHT 64
51
52/**************************************************************************
53* Debugging functions and macros *
54**************************************************************************/
55
56/* #define DEBUG_POINTER */
57
58#ifdef DEBUG
59# define PUT_PIXEL(c) ErrorF ("%c", c)
60#else /* DEBUG_VIDEO not defined */
61# define PUT_PIXEL(c) do { } while(0)
62#endif /* DEBUG_VIDEO not defined */
63
64/** Macro to printf an error message and return from a function */
65#define RETERROR(scrnIndex, RetVal, ...) \
66 do \
67 { \
68 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
69 return RetVal; \
70 } \
71 while (0)
72
73/** Structure to pass cursor image data between realise_cursor() and
74 * load_cursor_image(). The members match the parameters to
75 * @a VBoxHGSMIUpdatePointerShape(). */
76struct vboxCursorImage
77{
78 uint32_t fFlags;
79 uint32_t cHotX;
80 uint32_t cHotY;
81 uint32_t cWidth;
82 uint32_t cHeight;
83 uint8_t *pPixels;
84 uint32_t cbLength;
85};
86
87#ifdef DEBUG_POINTER
88static void
89vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
90{
91 size_t x, y;
92 unsigned short pitch;
93 CARD32 *color;
94 unsigned char *mask;
95 size_t sizeMask;
96
97 image += sizeof(struct vboxCursorImage);
98 mask = image;
99 pitch = (w + 7) / 8;
100 sizeMask = (pitch * h + 3) & ~3;
101 color = (CARD32 *)(image + sizeMask);
102
103 TRACE_ENTRY();
104 for (y = 0; y < h; ++y, mask += pitch, color += w)
105 {
106 for (x = 0; x < w; ++x)
107 {
108 if (mask[x / 8] & (1 << (7 - (x % 8))))
109 ErrorF (" ");
110 else
111 {
112 CARD32 c = color[x];
113 if (c == bg)
114 ErrorF("Y");
115 else
116 ErrorF("X");
117 }
118 }
119 ErrorF("\n");
120 }
121}
122#endif
123
124/**************************************************************************
125* Main functions *
126**************************************************************************/
127
128void vbvxCursorTerm(VBOXPtr pVBox)
129{
130 TRACE_ENTRY();
131
132 xf86DestroyCursorInfoRec(pVBox->pCurs);
133 pVBox->pCurs = NULL;
134 TRACE_EXIT();
135}
136
137static void
138vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
139{
140 int rc;
141 RT_NOREF(pScrn);
142
143 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0);
144 AssertMsg(rc == VINF_SUCCESS, ("Could not hide the virtual mouse pointer, VBox error %d.\n", rc));
145}
146
147static void
148vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
149{
150 int rc;
151 RT_NOREF(pScrn);
152
153 if (!pVBox->fUseHardwareCursor)
154 return;
155 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE,
156 0, 0, 0, 0, NULL, 0);
157 AssertMsg(rc == VINF_SUCCESS, ("Could not unhide the virtual mouse pointer.\n"));
158}
159
160static void
161vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
162 unsigned char *pvImage)
163{
164 int rc;
165 struct vboxCursorImage *pImage;
166 pImage = (struct vboxCursorImage *)pvImage;
167 RT_NOREF(pScrn);
168
169#ifdef DEBUG_POINTER
170 vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage);
171#endif
172
173 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags,
174 pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight,
175 pImage->pPixels, pImage->cbLength);
176 AssertMsg(rc == VINF_SUCCESS, ("Unable to set the virtual mouse pointer image.\n"));
177}
178
179static void
180vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
181{
182 RT_NOREF(pScrn);
183 RT_NOREF(bg);
184 RT_NOREF(fg);
185 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
186}
187
188
189static void
190vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
191{
192 VBOXPtr pVBox = pScrn->driverPrivate;
193
194 /* This currently does nothing. */
195 VBoxHGSMICursorPosition(&pVBox->guestCtx, true, x, y, NULL, NULL);
196}
197
198static void
199vbox_hide_cursor(ScrnInfoPtr pScrn)
200{
201 VBOXPtr pVBox = pScrn->driverPrivate;
202
203 vbox_vmm_hide_cursor(pScrn, pVBox);
204}
205
206static void
207vbox_show_cursor(ScrnInfoPtr pScrn)
208{
209 VBOXPtr pVBox = pScrn->driverPrivate;
210
211 vbox_vmm_show_cursor(pScrn, pVBox);
212}
213
214static void
215vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
216{
217 VBOXPtr pVBox = pScrn->driverPrivate;
218
219 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
220}
221
222static Bool
223vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
224{
225 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
226 VBOXPtr pVBox = pScrn->driverPrivate;
227 RT_NOREF(pCurs);
228 return pVBox->fUseHardwareCursor;
229}
230
231static unsigned char
232color_to_byte(unsigned c)
233{
234 return (c >> 8) & 0xff;
235}
236
237static unsigned char *
238vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
239{
240 VBOXPtr pVBox;
241 CursorBitsPtr bitsp;
242 unsigned short w, h, x, y;
243 unsigned char *c, *p, *pm, *ps, *m;
244 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
245 CARD32 fc, bc, *cp;
246 int scrnIndex = infoPtr->pScrn->scrnIndex;
247 struct vboxCursorImage *pImage;
248
249 pVBox = infoPtr->pScrn->driverPrivate;
250 bitsp = pCurs->bits;
251 w = bitsp->width;
252 h = bitsp->height;
253
254 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
255 RETERROR(scrnIndex, NULL,
256 "Error invalid cursor dimensions %dx%d\n", w, h);
257
258 if ((bitsp->xhot > w) || (bitsp->yhot > h))
259 RETERROR(scrnIndex, NULL,
260 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
261 bitsp->xhot, bitsp->yhot, w, h);
262
263 srcPitch = PixmapBytePad (bitsp->width, 1);
264 dstPitch = (w + 7) / 8;
265 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
266 sizeRgba = w * h * 4;
267 sizeRequest = sizeMask + sizeRgba + sizeof(*pImage);
268
269 p = c = calloc (1, sizeRequest);
270 if (!c)
271 RETERROR(scrnIndex, NULL,
272 "Error failed to alloc %lu bytes for cursor\n",
273 (unsigned long) sizeRequest);
274
275 pImage = (struct vboxCursorImage *)p;
276 pImage->pPixels = m = p + sizeof(*pImage);
277 cp = (CARD32 *)(m + sizeMask);
278
279 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
280 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
281 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
282
283 fc = color_to_byte (pCurs->foreBlue)
284 | (color_to_byte (pCurs->foreGreen) << 8)
285 | (color_to_byte (pCurs->foreRed) << 16);
286
287 bc = color_to_byte (pCurs->backBlue)
288 | (color_to_byte (pCurs->backGreen) << 8)
289 | (color_to_byte (pCurs->backRed) << 16);
290
291 /*
292 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
293 * Xorg:
294 * The mask is a bitmap indicating which parts of the cursor are
295 * transparent and which parts are drawn. The source is a bitmap
296 * indicating which parts of the non-transparent portion of the
297 * the cursor should be painted in the foreground color and which
298 * should be painted in the background color. By default, set bits
299 * indicate the opaque part of the mask bitmap and clear bits
300 * indicate the transparent part.
301 * VBox:
302 * The color data is the XOR mask. The AND mask bits determine
303 * which pixels of the color data (XOR mask) will replace (overwrite)
304 * the screen pixels (AND mask bit = 0) and which ones will be XORed
305 * with existing screen pixels (AND mask bit = 1).
306 * For example when you have the AND mask all 0, then you see the
307 * correct mouse pointer image surrounded by black square.
308 */
309 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
310 y < h;
311 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
312 {
313 for (x = 0; x < w; ++x)
314 {
315 if (pm[x / 8] & (1 << (x % 8)))
316 {
317 /* opaque, leave AND mask bit at 0 */
318 if (ps[x / 8] & (1 << (x % 8)))
319 {
320 *cp++ = fc;
321 PUT_PIXEL('X');
322 }
323 else
324 {
325 *cp++ = bc;
326 PUT_PIXEL('*');
327 }
328 }
329 else
330 {
331 /* transparent, set AND mask bit */
332 m[x / 8] |= 1 << (7 - (x % 8));
333 /* don't change the screen pixel */
334 *cp++ = 0;
335 PUT_PIXEL(' ');
336 }
337 }
338 PUT_PIXEL('\n');
339 }
340
341 pImage->cWidth = w;
342 pImage->cHeight = h;
343 pImage->cHotX = bitsp->xhot;
344 pImage->cHotY = bitsp->yhot;
345 pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
346 pImage->cbLength = sizeRequest - sizeof(*pImage);
347
348#ifdef DEBUG_POINTER
349 ErrorF("shape = %p\n", p);
350 vbox_show_shape(w, h, bc, c);
351#endif
352
353 return p;
354}
355
356#ifdef ARGB_CURSOR
357static Bool
358vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
359{
360 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
361 VBOXPtr pVBox = pScrn->driverPrivate;
362
363 if (!pVBox->fUseHardwareCursor)
364 return FALSE;
365 if ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
366 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
367 || (pScrn->bitsPerPixel <= 8))
368 return FALSE;
369 return TRUE;
370}
371
372
373static void
374vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
375{
376 VBOXPtr pVBox;
377 CursorBitsPtr bitsp;
378 unsigned short w, h;
379 unsigned short cx, cy;
380 unsigned char *pm;
381 CARD32 *pc;
382 size_t sizeData, sizeMask;
383 CARD8 *p;
384 int scrnIndex;
385 uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
386 | VBOX_MOUSE_POINTER_ALPHA;
387
388 pVBox = pScrn->driverPrivate;
389 bitsp = pCurs->bits;
390 w = bitsp->width;
391 h = bitsp->height;
392 scrnIndex = pScrn->scrnIndex;
393
394 /* Mask must be generated for alpha cursors, that is required by VBox. */
395 /* note: (michael) the next struct must be 32bit aligned. */
396 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
397
398 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
399 RETERROR(scrnIndex, ,
400 "Error invalid cursor dimensions %dx%d\n", w, h);
401
402 if ((bitsp->xhot > w) || (bitsp->yhot > h))
403 RETERROR(scrnIndex, ,
404 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
405 bitsp->xhot, bitsp->yhot, w, h);
406
407 sizeData = w * h * 4 + sizeMask;
408 p = calloc(1, sizeData);
409 if (!p)
410 RETERROR(scrnIndex, ,
411 "Error failed to alloc %lu bytes for cursor\n",
412 (unsigned long)sizeData);
413
414 memcpy(p + sizeMask, bitsp->argb, w * h * 4);
415
416 /* Emulate the AND mask. */
417 pm = p;
418 pc = bitsp->argb;
419
420 /* Init AND mask to 1 */
421 memset(pm, 0xFF, sizeMask);
422
423 /*
424 * The additions driver must provide the AND mask for alpha cursors. The host frontend
425 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
426 * But if the host does not support ARGB, then it simply uses the AND mask and the color
427 * data to draw a normal color cursor.
428 */
429 for (cy = 0; cy < h; cy++)
430 {
431 unsigned char bitmask = 0x80;
432
433 for (cx = 0; cx < w; cx++, bitmask >>= 1)
434 {
435 if (bitmask == 0)
436 bitmask = 0x80;
437
438 if (pc[cx] >= 0xF0000000)
439 pm[cx / 8] &= ~bitmask;
440 }
441
442 /* Point to next source and dest scans */
443 pc += w;
444 pm += (w + 7) / 8;
445 }
446
447 VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot,
448 bitsp->yhot, w, h, p, sizeData);
449 free(p);
450}
451#endif
452
453Bool vbvxCursorInit(ScreenPtr pScreen)
454{
455 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
456 VBOXPtr pVBox = pScrn->driverPrivate;
457 xf86CursorInfoPtr pCurs = NULL;
458 Bool rc = TRUE;
459
460 TRACE_ENTRY();
461 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
462 if (!pCurs) {
463 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
464 "Failed to create X Window cursor information structures for virtual mouse.\n");
465 rc = FALSE;
466 }
467 if (rc) {
468 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
469 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
470 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
471 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
472 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST
473 | HARDWARE_CURSOR_UPDATE_UNHIDDEN;
474
475 pCurs->SetCursorColors = vbox_set_cursor_colors;
476 pCurs->SetCursorPosition = vbox_set_cursor_position;
477 pCurs->LoadCursorImage = vbox_load_cursor_image;
478 pCurs->HideCursor = vbox_hide_cursor;
479 pCurs->ShowCursor = vbox_show_cursor;
480 pCurs->UseHWCursor = vbox_use_hw_cursor;
481 pCurs->RealizeCursor = vbox_realize_cursor;
482
483#ifdef ARGB_CURSOR
484 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
485 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
486#endif
487
488 rc = xf86InitCursor(pScreen, pCurs);
489 }
490 if (!rc)
491 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
492 "Failed to enable mouse pointer integration.\n");
493 if (!rc && (pCurs != NULL))
494 xf86DestroyCursorInfoRec(pCurs);
495 return rc;
496}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use