VirtualBox

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

Last change on this file since 69058 was 69058, checked in by vboxsync, 7 years ago

Additions/x11/vboxvideo: update file headers before putting into X.Org.
bugref:9017: Additions/x11: put vboxvideo into upstream X.Org

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.5 KB
Line 
1/* $Id: pointer.c 69058 2017-10-12 09:48:15Z vboxsync $ */
2/** @file
3 * VirtualBox X11 Additions graphics driver utility functions
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#include <VBox/VBoxGuestLib.h>
29
30#ifndef PCIACCESS
31# include <xf86Pci.h>
32# include <Pci.h>
33#endif
34
35#include "xf86.h"
36#define NEED_XF86_TYPES
37#include <iprt/string.h>
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#endif
47
48#define VBOX_MAX_CURSOR_WIDTH 64
49#define VBOX_MAX_CURSOR_HEIGHT 64
50
51/**************************************************************************
52* Debugging functions and macros *
53**************************************************************************/
54
55/* #define DEBUG_POINTER */
56
57#ifdef DEBUG
58# define PUT_PIXEL(c) ErrorF ("%c", c)
59#else /* DEBUG_VIDEO not defined */
60# define PUT_PIXEL(c) do { } while(0)
61#endif /* DEBUG_VIDEO not defined */
62
63/** Macro to printf an error message and return from a function */
64#define RETERROR(scrnIndex, RetVal, ...) \
65 do \
66 { \
67 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
68 return RetVal; \
69 } \
70 while (0)
71
72/** Structure to pass cursor image data between realise_cursor() and
73 * load_cursor_image(). The members match the parameters to
74 * @a VBoxHGSMIUpdatePointerShape(). */
75struct vboxCursorImage
76{
77 uint32_t fFlags;
78 uint32_t cHotX;
79 uint32_t cHotY;
80 uint32_t cWidth;
81 uint32_t cHeight;
82 uint8_t *pPixels;
83 uint32_t cbLength;
84};
85
86#ifdef DEBUG_POINTER
87static void
88vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
89{
90 size_t x, y;
91 unsigned short pitch;
92 CARD32 *color;
93 unsigned char *mask;
94 size_t sizeMask;
95
96 image += sizeof(struct vboxCursorImage);
97 mask = image;
98 pitch = (w + 7) / 8;
99 sizeMask = (pitch * h + 3) & ~3;
100 color = (CARD32 *)(image + sizeMask);
101
102 TRACE_ENTRY();
103 for (y = 0; y < h; ++y, mask += pitch, color += w)
104 {
105 for (x = 0; x < w; ++x)
106 {
107 if (mask[x / 8] & (1 << (7 - (x % 8))))
108 ErrorF (" ");
109 else
110 {
111 CARD32 c = color[x];
112 if (c == bg)
113 ErrorF("Y");
114 else
115 ErrorF("X");
116 }
117 }
118 ErrorF("\n");
119 }
120}
121#endif
122
123/**************************************************************************
124* Main functions *
125**************************************************************************/
126
127void vbvxCursorTerm(VBOXPtr pVBox)
128{
129 TRACE_ENTRY();
130
131 xf86DestroyCursorInfoRec(pVBox->pCurs);
132 pVBox->pCurs = NULL;
133 TRACE_EXIT();
134}
135
136static void
137vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
138{
139 int rc;
140 RT_NOREF(pScrn);
141
142 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0);
143 VBVXASSERT(rc == VINF_SUCCESS, ("Could not hide the virtual mouse pointer, VBox error %d.\n", rc));
144}
145
146static void
147vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
148{
149 int rc;
150 RT_NOREF(pScrn);
151
152 if (!pVBox->fUseHardwareCursor)
153 return;
154 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE,
155 0, 0, 0, 0, NULL, 0);
156 VBVXASSERT(rc == VINF_SUCCESS, ("Could not unhide the virtual mouse pointer.\n"));
157}
158
159static void
160vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
161 unsigned char *pvImage)
162{
163 int rc;
164 struct vboxCursorImage *pImage;
165 pImage = (struct vboxCursorImage *)pvImage;
166 RT_NOREF(pScrn);
167
168#ifdef DEBUG_POINTER
169 vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage);
170#endif
171
172 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags,
173 pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight,
174 pImage->pPixels, pImage->cbLength);
175 VBVXASSERT(rc == VINF_SUCCESS, ("Unable to set the virtual mouse pointer image.\n"));
176}
177
178static void
179vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
180{
181 NOREF(pScrn);
182 NOREF(bg);
183 NOREF(fg);
184 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
185}
186
187
188static void
189vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
190{
191 VBOXPtr pVBox = pScrn->driverPrivate;
192
193 /* This currently does nothing. */
194 VBoxHGSMICursorPosition(&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