VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 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 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VirtualBox X11 Additions graphics driver utility functions
4 */
5
6/*
7 * Copyright (C) 2006-2024 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 VBoxHGSMIReportCursorPosition(&pVBox->guestCtx, true, x, y, NULL, NULL);
195}
196
197static void
198vbox_hide_cursor(ScrnInfoPtr pScrn)
199{
200 VBOXPtr pVBox = pScrn->driverPrivate;
201
202 vbox_vmm_hide_cursor(pScrn, pVBox);
203}
204
205static void
206vbox_show_cursor(ScrnInfoPtr pScrn)
207{
208 VBOXPtr pVBox = pScrn->driverPrivate;
209
210 vbox_vmm_show_cursor(pScrn, pVBox);
211}
212
213static void
214vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
215{
216 VBOXPtr pVBox = pScrn->driverPrivate;
217
218 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
219}
220
221static Bool
222vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
223{
224 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
225 VBOXPtr pVBox = pScrn->driverPrivate;
226 RT_NOREF(pCurs);
227 return pVBox->fUseHardwareCursor;
228}
229
230static unsigned char
231color_to_byte(unsigned c)
232{
233 return (c >> 8) & 0xff;
234}
235
236static unsigned char *
237vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
238{
239 VBOXPtr pVBox;
240 CursorBitsPtr bitsp;
241 unsigned short w, h, x, y;
242 unsigned char *c, *p, *pm, *ps, *m;
243 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
244 CARD32 fc, bc, *cp;
245 int scrnIndex = infoPtr->pScrn->scrnIndex;
246 struct vboxCursorImage *pImage;
247
248 pVBox = infoPtr->pScrn->driverPrivate;
249 bitsp = pCurs->bits;
250 w = bitsp->width;
251 h = bitsp->height;
252
253 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
254 RETERROR(scrnIndex, NULL,
255 "Error invalid cursor dimensions %dx%d\n", w, h);
256
257 if ((bitsp->xhot > w) || (bitsp->yhot > h))
258 RETERROR(scrnIndex, NULL,
259 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
260 bitsp->xhot, bitsp->yhot, w, h);
261
262 srcPitch = PixmapBytePad (bitsp->width, 1);
263 dstPitch = (w + 7) / 8;
264 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
265 sizeRgba = w * h * 4;
266 sizeRequest = sizeMask + sizeRgba + sizeof(*pImage);
267
268 p = c = calloc (1, sizeRequest);
269 if (!c)
270 RETERROR(scrnIndex, NULL,
271 "Error failed to alloc %lu bytes for cursor\n",
272 (unsigned long) sizeRequest);
273
274 pImage = (struct vboxCursorImage *)p;
275 pImage->pPixels = m = p + sizeof(*pImage);
276 cp = (CARD32 *)(m + sizeMask);
277
278 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
279 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
280 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
281
282 fc = color_to_byte (pCurs->foreBlue)
283 | (color_to_byte (pCurs->foreGreen) << 8)
284 | (color_to_byte (pCurs->foreRed) << 16);
285
286 bc = color_to_byte (pCurs->backBlue)
287 | (color_to_byte (pCurs->backGreen) << 8)
288 | (color_to_byte (pCurs->backRed) << 16);
289
290 /*
291 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
292 * Xorg:
293 * The mask is a bitmap indicating which parts of the cursor are
294 * transparent and which parts are drawn. The source is a bitmap
295 * indicating which parts of the non-transparent portion of the
296 * the cursor should be painted in the foreground color and which
297 * should be painted in the background color. By default, set bits
298 * indicate the opaque part of the mask bitmap and clear bits
299 * indicate the transparent part.
300 * VBox:
301 * The color data is the XOR mask. The AND mask bits determine
302 * which pixels of the color data (XOR mask) will replace (overwrite)
303 * the screen pixels (AND mask bit = 0) and which ones will be XORed
304 * with existing screen pixels (AND mask bit = 1).
305 * For example when you have the AND mask all 0, then you see the
306 * correct mouse pointer image surrounded by black square.
307 */
308 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
309 y < h;
310 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
311 {
312 for (x = 0; x < w; ++x)
313 {
314 if (pm[x / 8] & (1 << (x % 8)))
315 {
316 /* opaque, leave AND mask bit at 0 */
317 if (ps[x / 8] & (1 << (x % 8)))
318 {
319 *cp++ = fc;
320 PUT_PIXEL('X');
321 }
322 else
323 {
324 *cp++ = bc;
325 PUT_PIXEL('*');
326 }
327 }
328 else
329 {
330 /* transparent, set AND mask bit */
331 m[x / 8] |= 1 << (7 - (x % 8));
332 /* don't change the screen pixel */
333 *cp++ = 0;
334 PUT_PIXEL(' ');
335 }
336 }
337 PUT_PIXEL('\n');
338 }
339
340 pImage->cWidth = w;
341 pImage->cHeight = h;
342 pImage->cHotX = bitsp->xhot;
343 pImage->cHotY = bitsp->yhot;
344 pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
345 pImage->cbLength = sizeRequest - sizeof(*pImage);
346
347#ifdef DEBUG_POINTER
348 ErrorF("shape = %p\n", p);
349 vbox_show_shape(w, h, bc, c);
350#endif
351
352 return p;
353}
354
355#ifdef ARGB_CURSOR
356static Bool
357vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
358{
359 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
360 VBOXPtr pVBox = pScrn->driverPrivate;
361
362 if (!pVBox->fUseHardwareCursor)
363 return FALSE;
364 if ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
365 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
366 || (pScrn->bitsPerPixel <= 8))
367 return FALSE;
368 return TRUE;
369}
370
371
372static void
373vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
374{
375 VBOXPtr pVBox;
376 CursorBitsPtr bitsp;
377 unsigned short w, h;
378 unsigned short cx, cy;
379 unsigned char *pm;
380 CARD32 *pc;
381 size_t sizeData, sizeMask;
382 CARD8 *p;
383 int scrnIndex;
384 uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
385 | VBOX_MOUSE_POINTER_ALPHA;
386
387 pVBox = pScrn->driverPrivate;
388 bitsp = pCurs->bits;
389 w = bitsp->width;
390 h = bitsp->height;
391 scrnIndex = pScrn->scrnIndex;
392
393 /* Mask must be generated for alpha cursors, that is required by VBox. */
394 /* note: (michael) the next struct must be 32bit aligned. */
395 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
396
397 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
398 RETERROR(scrnIndex, ,
399 "Error invalid cursor dimensions %dx%d\n", w, h);
400
401 if ((bitsp->xhot > w) || (bitsp->yhot > h))
402 RETERROR(scrnIndex, ,
403 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
404 bitsp->xhot, bitsp->yhot, w, h);
405
406 sizeData = w * h * 4 + sizeMask;
407 p = calloc(1, sizeData);
408 if (!p)
409 RETERROR(scrnIndex, ,
410 "Error failed to alloc %lu bytes for cursor\n",
411 (unsigned long)sizeData);
412
413 memcpy(p + sizeMask, bitsp->argb, w * h * 4);
414
415 /* Emulate the AND mask. */
416 pm = p;
417 pc = bitsp->argb;
418
419 /* Init AND mask to 1 */
420 memset(pm, 0xFF, sizeMask);
421
422 /*
423 * The additions driver must provide the AND mask for alpha cursors. The host frontend
424 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
425 * But if the host does not support ARGB, then it simply uses the AND mask and the color
426 * data to draw a normal color cursor.
427 */
428 for (cy = 0; cy < h; cy++)
429 {
430 unsigned char bitmask = 0x80;
431
432 for (cx = 0; cx < w; cx++, bitmask >>= 1)
433 {
434 if (bitmask == 0)
435 bitmask = 0x80;
436
437 if (pc[cx] >= 0xF0000000)
438 pm[cx / 8] &= ~bitmask;
439 }
440
441 /* Point to next source and dest scans */
442 pc += w;
443 pm += (w + 7) / 8;
444 }
445
446 VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot,
447 bitsp->yhot, w, h, p, sizeData);
448 free(p);
449}
450#endif
451
452Bool vbvxCursorInit(ScreenPtr pScreen)
453{
454 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
455 VBOXPtr pVBox = pScrn->driverPrivate;
456 xf86CursorInfoPtr pCurs = NULL;
457 Bool rc = TRUE;
458
459 TRACE_ENTRY();
460 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
461 if (!pCurs) {
462 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
463 "Failed to create X Window cursor information structures for virtual mouse.\n");
464 rc = FALSE;
465 }
466 if (rc) {
467 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
468 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
469 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
470 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
471 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST
472 | HARDWARE_CURSOR_UPDATE_UNHIDDEN;
473
474 pCurs->SetCursorColors = vbox_set_cursor_colors;
475 pCurs->SetCursorPosition = vbox_set_cursor_position;
476 pCurs->LoadCursorImage = vbox_load_cursor_image;
477 pCurs->HideCursor = vbox_hide_cursor;
478 pCurs->ShowCursor = vbox_show_cursor;
479 pCurs->UseHWCursor = vbox_use_hw_cursor;
480 pCurs->RealizeCursor = vbox_realize_cursor;
481
482#ifdef ARGB_CURSOR
483 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
484 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
485#endif
486
487 rc = xf86InitCursor(pScreen, pCurs);
488 }
489 if (!rc)
490 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
491 "Failed to enable mouse pointer integration.\n");
492 if (!rc && (pCurs != NULL))
493 xf86DestroyCursorInfoRec(pCurs);
494 return rc;
495}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette