VirtualBox

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

Last change on this file since 50900 was 50900, checked in by vboxsync, 11 years ago

Additions/common/VBoxVideo: fix a return value.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.7 KB
Line 
1/** @file
2 * VirtualBox X11 Additions graphics driver utility functions
3 */
4
5/*
6 * Copyright (C) 2006-2012 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#include <VBox/VMMDev.h>
18#include <VBox/VBoxGuestLib.h>
19
20#ifndef PCIACCESS
21# include <xf86Pci.h>
22# include <Pci.h>
23#endif
24
25#include "xf86.h"
26#define NEED_XF86_TYPES
27#include <iprt/string.h>
28#include "compiler.h"
29#include "cursorstr.h"
30#include "servermd.h"
31
32#include "vboxvideo.h"
33
34#ifdef XORG_7X
35# include <stdlib.h>
36#endif
37
38#define VBOX_MAX_CURSOR_WIDTH 64
39#define VBOX_MAX_CURSOR_HEIGHT 64
40
41/**************************************************************************
42* Debugging functions and macros *
43**************************************************************************/
44
45/* #define DEBUG_POINTER */
46
47#ifdef DEBUG
48# define PUT_PIXEL(c) ErrorF ("%c", c)
49#else /* DEBUG_VIDEO not defined */
50# define PUT_PIXEL(c) do { } while(0)
51#endif /* DEBUG_VIDEO not defined */
52
53/** Macro to printf an error message and return from a function */
54#define RETERROR(scrnIndex, RetVal, ...) \
55 do \
56 { \
57 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \
58 return RetVal; \
59 } \
60 while (0)
61
62/** Structure to pass cursor image data between realise_cursor() and
63 * load_cursor_image(). The members match the parameters to
64 * @a VBoxHGSMIUpdatePointerShape(). */
65struct vboxCursorImage
66{
67 uint32_t fFlags;
68 uint32_t cHotX;
69 uint32_t cHotY;
70 uint32_t cWidth;
71 uint32_t cHeight;
72 uint8_t *pPixels;
73 uint32_t cbLength;
74};
75
76#ifdef DEBUG_POINTER
77static void
78vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image)
79{
80 size_t x, y;
81 unsigned short pitch;
82 CARD32 *color;
83 unsigned char *mask;
84 size_t sizeMask;
85
86 image += sizeof(struct vboxCursorImage);
87 mask = image;
88 pitch = (w + 7) / 8;
89 sizeMask = (pitch * h + 3) & ~3;
90 color = (CARD32 *)(image + sizeMask);
91
92 TRACE_ENTRY();
93 for (y = 0; y < h; ++y, mask += pitch, color += w)
94 {
95 for (x = 0; x < w; ++x)
96 {
97 if (mask[x / 8] & (1 << (7 - (x % 8))))
98 ErrorF (" ");
99 else
100 {
101 CARD32 c = color[x];
102 if (c == bg)
103 ErrorF("Y");
104 else
105 ErrorF("X");
106 }
107 }
108 ErrorF("\n");
109 }
110}
111#endif
112
113/**************************************************************************
114* Helper functions and macros *
115**************************************************************************/
116
117/* This is called by the X server every time it loads a new cursor to see
118 * whether our "cursor hardware" can handle the cursor. This provides us with
119 * a mechanism (the only one!) to switch back from a software to a hardware
120 * cursor. */
121static Bool
122vbox_host_uses_hwcursor(ScrnInfoPtr pScrn)
123{
124 Bool rc = TRUE;
125 uint32_t fFeatures = 0;
126 VBOXPtr pVBox = pScrn->driverPrivate;
127
128 /* We may want to force the use of a software cursor. Currently this is
129 * needed if the guest uses a large virtual resolution, as in this case
130 * the host and guest tend to disagree about the pointer location. */
131 if (pVBox->forceSWCursor)
132 rc = FALSE;
133 /* Query information about mouse integration from the host. */
134 if (rc) {
135 int vrc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
136 if (RT_FAILURE(vrc)) {
137 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
138 "Unable to determine whether the virtual machine supports mouse pointer integration - request initialization failed with return code %d\n", vrc);
139 rc = FALSE;
140 }
141 }
142 /* If we got the information from the host then make sure the host wants
143 * to draw the pointer. */
144 if (rc)
145 {
146 if ( (fFeatures & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
147#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 5
148 /* As of this version (server 1.6) all major Linux releases
149 * are known to handle USB tablets correctly. */
150 || (fFeatures & VMMDEV_MOUSE_HOST_HAS_ABS_DEV)
151#endif
152 )
153 /* Assume this will never be unloaded as long as the X session is
154 * running. */
155 pVBox->guestCanAbsolute = TRUE;
156 if ( (fFeatures & VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER)
157 || !pVBox->guestCanAbsolute
158 || !(fFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE)
159 )
160 rc = FALSE;
161 }
162 return rc;
163}
164
165/**************************************************************************
166* Main functions *
167**************************************************************************/
168
169void
170vbox_close(ScrnInfoPtr pScrn, VBOXPtr pVBox)
171{
172 TRACE_ENTRY();
173
174 xf86DestroyCursorInfoRec(pVBox->pCurs);
175 pVBox->pCurs = NULL;
176 TRACE_EXIT();
177}
178
179Bool
180vbox_init(int scrnIndex, VBOXPtr pVBox)
181{
182 Bool rc = TRUE;
183 int vrc;
184 uint32_t fMouseFeatures = 0;
185
186 TRACE_ENTRY();
187 vrc = VbglR3Init();
188 if (RT_FAILURE(vrc))
189 {
190 xf86DrvMsg(scrnIndex, X_ERROR,
191 "Failed to initialize the VirtualBox device (rc=%d) - make sure that the VirtualBox guest additions are properly installed. If you are not sure, try reinstalling them. The X Window graphics drivers will run in compatibility mode.\n",
192 vrc);
193 rc = FALSE;
194 }
195 pVBox->useDevice = rc;
196 return rc;
197}
198
199static void
200vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
201{
202 int rc;
203
204 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0);
205 if (RT_FAILURE(rc))
206 {
207 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not hide the virtual mouse pointer, VBox error %d.\n", rc);
208 /* Play safe, and disable the hardware cursor until the next mode
209 * switch, since obviously something happened that we didn't
210 * anticipate. */
211 pVBox->forceSWCursor = TRUE;
212 }
213}
214
215static void
216vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
217{
218 int rc;
219
220 if (!vbox_host_uses_hwcursor(pScrn))
221 return;
222 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE,
223 0, 0, 0, 0, NULL, 0);
224 if (RT_FAILURE(rc)) {
225 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not unhide the virtual mouse pointer.\n");
226 /* Play safe, and disable the hardware cursor until the next mode
227 * switch, since obviously something happened that we didn't
228 * anticipate. */
229 pVBox->forceSWCursor = TRUE;
230 }
231}
232
233static void
234vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
235 unsigned char *pvImage)
236{
237 int rc;
238 struct vboxCursorImage *pImage;
239 pImage = (struct vboxCursorImage *)pvImage;
240
241#ifdef DEBUG_POINTER
242 vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage);
243#endif
244
245 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags,
246 pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight,
247 pImage->pPixels, pImage->cbLength);
248 if (RT_FAILURE(rc)) {
249 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to set the virtual mouse pointer image.\n");
250 /* Play safe, and disable the hardware cursor until the next mode
251 * switch, since obviously something happened that we didn't
252 * anticipate. */
253 pVBox->forceSWCursor = TRUE;
254 }
255}
256
257static void
258vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
259{
260 NOREF(pScrn);
261 NOREF(bg);
262 NOREF(fg);
263 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
264}
265
266
267static void
268vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
269{
270 /* Nothing to do here, as we are telling the guest where the mouse is,
271 * not vice versa. */
272 NOREF(pScrn);
273 NOREF(x);
274 NOREF(y);
275}
276
277static void
278vbox_hide_cursor(ScrnInfoPtr pScrn)
279{
280 VBOXPtr pVBox = pScrn->driverPrivate;
281
282 vbox_vmm_hide_cursor(pScrn, pVBox);
283}
284
285static void
286vbox_show_cursor(ScrnInfoPtr pScrn)
287{
288 VBOXPtr pVBox = pScrn->driverPrivate;
289
290 vbox_vmm_show_cursor(pScrn, pVBox);
291}
292
293static void
294vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
295{
296 VBOXPtr pVBox = pScrn->driverPrivate;
297
298 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
299}
300
301static Bool
302vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
303{
304 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
305 return vbox_host_uses_hwcursor(pScrn);
306}
307
308static unsigned char
309color_to_byte(unsigned c)
310{
311 return (c >> 8) & 0xff;
312}
313
314static unsigned char *
315vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
316{
317 VBOXPtr pVBox;
318 CursorBitsPtr bitsp;
319 unsigned short w, h, x, y;
320 unsigned char *c, *p, *pm, *ps, *m;
321 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
322 CARD32 fc, bc, *cp;
323 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
324 struct vboxCursorImage *pImage;
325
326 pVBox = infoPtr->pScrn->driverPrivate;
327 bitsp = pCurs->bits;
328 w = bitsp->width;
329 h = bitsp->height;
330
331 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
332 RETERROR(scrnIndex, NULL,
333 "Error invalid cursor dimensions %dx%d\n", w, h);
334
335 if ((bitsp->xhot > w) || (bitsp->yhot > h))
336 RETERROR(scrnIndex, NULL,
337 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
338 bitsp->xhot, bitsp->yhot, w, h);
339
340 srcPitch = PixmapBytePad (bitsp->width, 1);
341 dstPitch = (w + 7) / 8;
342 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
343 sizeRgba = w * h * 4;
344 sizeRequest = sizeMask + sizeRgba + sizeof(*pImage);
345
346 p = c = calloc (1, sizeRequest);
347 if (!c)
348 RETERROR(scrnIndex, NULL,
349 "Error failed to alloc %lu bytes for cursor\n",
350 (unsigned long) sizeRequest);
351
352 pImage = (struct vboxCursorImage *)p;
353 pImage->pPixels = m = p + sizeof(*pImage);
354 cp = (CARD32 *)(m + sizeMask);
355
356 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
357 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
358 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
359
360 fc = color_to_byte (pCurs->foreBlue)
361 | (color_to_byte (pCurs->foreGreen) << 8)
362 | (color_to_byte (pCurs->foreRed) << 16);
363
364 bc = color_to_byte (pCurs->backBlue)
365 | (color_to_byte (pCurs->backGreen) << 8)
366 | (color_to_byte (pCurs->backRed) << 16);
367
368 /*
369 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
370 * Xorg:
371 * The mask is a bitmap indicating which parts of the cursor are
372 * transparent and which parts are drawn. The source is a bitmap
373 * indicating which parts of the non-transparent portion of the
374 * the cursor should be painted in the foreground color and which
375 * should be painted in the background color. By default, set bits
376 * indicate the opaque part of the mask bitmap and clear bits
377 * indicate the transparent part.
378 * VBox:
379 * The color data is the XOR mask. The AND mask bits determine
380 * which pixels of the color data (XOR mask) will replace (overwrite)
381 * the screen pixels (AND mask bit = 0) and which ones will be XORed
382 * with existing screen pixels (AND mask bit = 1).
383 * For example when you have the AND mask all 0, then you see the
384 * correct mouse pointer image surrounded by black square.
385 */
386 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
387 y < h;
388 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
389 {
390 for (x = 0; x < w; ++x)
391 {
392 if (pm[x / 8] & (1 << (x % 8)))
393 {
394 /* opaque, leave AND mask bit at 0 */
395 if (ps[x / 8] & (1 << (x % 8)))
396 {
397 *cp++ = fc;
398 PUT_PIXEL('X');
399 }
400 else
401 {
402 *cp++ = bc;
403 PUT_PIXEL('*');
404 }
405 }
406 else
407 {
408 /* transparent, set AND mask bit */
409 m[x / 8] |= 1 << (7 - (x % 8));
410 /* don't change the screen pixel */
411 *cp++ = 0;
412 PUT_PIXEL(' ');
413 }
414 }
415 PUT_PIXEL('\n');
416 }
417
418 pImage->cWidth = w;
419 pImage->cHeight = h;
420 pImage->cHotX = bitsp->xhot;
421 pImage->cHotY = bitsp->yhot;
422 pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
423 pImage->cbLength = sizeRequest - sizeof(*pImage);
424
425#ifdef DEBUG_POINTER
426 ErrorF("shape = %p\n", p);
427 vbox_show_shape(w, h, bc, c);
428#endif
429
430 return p;
431}
432
433#ifdef ARGB_CURSOR
434static Bool
435vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
436{
437 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
438 Bool rc = TRUE;
439
440 if (!vbox_host_uses_hwcursor(pScrn))
441 rc = FALSE;
442 if ( rc
443 && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
444 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
445 || (pScrn->bitsPerPixel <= 8)
446 )
447 )
448 rc = FALSE;
449#ifndef VBOXVIDEO_13
450 /* Evil hack - we use this as another way of poking the driver to update
451 * our list of video modes. */
452 vboxWriteHostModes(pScrn, pScrn->currentMode);
453#endif
454 return rc;
455}
456
457
458static void
459vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
460{
461 VBOXPtr pVBox;
462 VMMDevReqMousePointer *reqp;
463 CursorBitsPtr bitsp;
464 unsigned short w, h;
465 unsigned short cx, cy;
466 unsigned char *pm;
467 CARD32 *pc;
468 size_t sizeData, sizeMask;
469 CARD8 *p;
470 int scrnIndex;
471 uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
472 | VBOX_MOUSE_POINTER_ALPHA;
473 int rc;
474
475 pVBox = pScrn->driverPrivate;
476 bitsp = pCurs->bits;
477 w = bitsp->width;
478 h = bitsp->height;
479 scrnIndex = pScrn->scrnIndex;
480
481 /* Mask must be generated for alpha cursors, that is required by VBox. */
482 /* note: (michael) the next struct must be 32bit aligned. */
483 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
484
485 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
486 RETERROR(scrnIndex, ,
487 "Error invalid cursor dimensions %dx%d\n", w, h);
488
489 if ((bitsp->xhot > w) || (bitsp->yhot > h))
490 RETERROR(scrnIndex, ,
491 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
492 bitsp->xhot, bitsp->yhot, w, h);
493
494 sizeData = w * h * 4 + sizeMask;
495 p = calloc(1, sizeData);
496 if (!p)
497 RETERROR(scrnIndex, ,
498 "Error failed to alloc %lu bytes for cursor\n",
499 (unsigned long)sizeData);
500
501 memcpy(p + sizeMask, bitsp->argb, w * h * 4);
502
503 /* Emulate the AND mask. */
504 pm = p;
505 pc = bitsp->argb;
506
507 /* Init AND mask to 1 */
508 memset(pm, 0xFF, sizeMask);
509
510 /*
511 * The additions driver must provide the AND mask for alpha cursors. The host frontend
512 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
513 * But if the host does not support ARGB, then it simply uses the AND mask and the color
514 * data to draw a normal color cursor.
515 */
516 for (cy = 0; cy < h; cy++)
517 {
518 unsigned char bitmask = 0x80;
519
520 for (cx = 0; cx < w; cx++, bitmask >>= 1)
521 {
522 if (bitmask == 0)
523 bitmask = 0x80;
524
525 if (pc[cx] >= 0xF0000000)
526 pm[cx / 8] &= ~bitmask;
527 }
528
529 /* Point to next source and dest scans */
530 pc += w;
531 pm += (w + 7) / 8;
532 }
533
534 VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot,
535 bitsp->yhot, w, h, p, sizeData);
536 free(p);
537}
538#endif
539
540Bool
541vbox_cursor_init(ScreenPtr pScreen)
542{
543 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
544 VBOXPtr pVBox = pScrn->driverPrivate;
545 xf86CursorInfoPtr pCurs = NULL;
546 Bool rc = TRUE;
547
548 TRACE_ENTRY();
549 if (!pVBox->fHaveHGSMI)
550 return FALSE;
551 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
552 if (!pCurs) {
553 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
554 "Failed to create X Window cursor information structures for virtual mouse.\n");
555 rc = FALSE;
556 }
557 if (rc) {
558 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
559 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
560 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
561 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
562 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
563
564 pCurs->SetCursorColors = vbox_set_cursor_colors;
565 pCurs->SetCursorPosition = vbox_set_cursor_position;
566 pCurs->LoadCursorImage = vbox_load_cursor_image;
567 pCurs->HideCursor = vbox_hide_cursor;
568 pCurs->ShowCursor = vbox_show_cursor;
569 pCurs->UseHWCursor = vbox_use_hw_cursor;
570 pCurs->RealizeCursor = vbox_realize_cursor;
571
572#ifdef ARGB_CURSOR
573 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
574 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
575#endif
576
577 /* Hide the host cursor before we initialise if we wish to use a
578 * software cursor. */
579 if (pVBox->forceSWCursor)
580 vbox_vmm_hide_cursor(pScrn, pVBox);
581 rc = xf86InitCursor(pScreen, pCurs);
582 }
583 if (!rc)
584 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
585 "Failed to enable mouse pointer integration.\n");
586 if (!rc && (pCurs != NULL))
587 xf86DestroyCursorInfoRec(pCurs);
588 return rc;
589}
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