VirtualBox

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

Last change on this file since 53781 was 53428, checked in by vboxsync, 10 years ago

Additions/x11/vboxvideo: remove an error message for a valid condition and prevent log spam.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 17.2 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 rc = FALSE;
138 }
139 /* If we got the information from the host then make sure the host wants
140 * to draw the pointer. */
141 if (rc)
142 {
143 if ( (fFeatures & VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE)
144#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) >= 5
145 /* As of this version (server 1.6) all major Linux releases
146 * are known to handle USB tablets correctly. */
147 || (fFeatures & VMMDEV_MOUSE_HOST_HAS_ABS_DEV)
148#endif
149 )
150 /* Assume this will never be unloaded as long as the X session is
151 * running. */
152 pVBox->guestCanAbsolute = TRUE;
153 if ( (fFeatures & VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER)
154 || !pVBox->guestCanAbsolute
155 || !(fFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE)
156 )
157 rc = FALSE;
158 }
159 return rc;
160}
161
162/**************************************************************************
163* Main functions *
164**************************************************************************/
165
166void
167vbox_close(ScrnInfoPtr pScrn, VBOXPtr pVBox)
168{
169 TRACE_ENTRY();
170
171 xf86DestroyCursorInfoRec(pVBox->pCurs);
172 pVBox->pCurs = NULL;
173 TRACE_EXIT();
174}
175
176Bool
177vbox_init(int scrnIndex, VBOXPtr pVBox)
178{
179 Bool rc = TRUE;
180 int vrc;
181 uint32_t fMouseFeatures = 0;
182
183 TRACE_ENTRY();
184 vrc = VbglR3Init();
185 if (RT_FAILURE(vrc))
186 {
187 xf86DrvMsg(scrnIndex, X_ERROR,
188 "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",
189 vrc);
190 rc = FALSE;
191 }
192 pVBox->useDevice = rc;
193 return rc;
194}
195
196static void
197vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
198{
199 int rc;
200
201 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0);
202 if (RT_FAILURE(rc))
203 {
204 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not hide the virtual mouse pointer, VBox error %d.\n", rc);
205 /* Play safe, and disable the hardware cursor until the next mode
206 * switch, since obviously something happened that we didn't
207 * anticipate. */
208 pVBox->forceSWCursor = TRUE;
209 }
210}
211
212static void
213vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox)
214{
215 int rc;
216
217 if (!vbox_host_uses_hwcursor(pScrn))
218 return;
219 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE,
220 0, 0, 0, 0, NULL, 0);
221 if (RT_FAILURE(rc)) {
222 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not unhide the virtual mouse pointer.\n");
223 /* Play safe, and disable the hardware cursor until the next mode
224 * switch, since obviously something happened that we didn't
225 * anticipate. */
226 pVBox->forceSWCursor = TRUE;
227 }
228}
229
230static void
231vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox,
232 unsigned char *pvImage)
233{
234 int rc;
235 struct vboxCursorImage *pImage;
236 pImage = (struct vboxCursorImage *)pvImage;
237
238#ifdef DEBUG_POINTER
239 vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage);
240#endif
241
242 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags,
243 pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight,
244 pImage->pPixels, pImage->cbLength);
245 if (RT_FAILURE(rc)) {
246 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Unable to set the virtual mouse pointer image.\n");
247 /* Play safe, and disable the hardware cursor until the next mode
248 * switch, since obviously something happened that we didn't
249 * anticipate. */
250 pVBox->forceSWCursor = TRUE;
251 }
252}
253
254static void
255vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg)
256{
257 NOREF(pScrn);
258 NOREF(bg);
259 NOREF(fg);
260 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */
261}
262
263
264static void
265vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y)
266{
267 /* Nothing to do here, as we are telling the guest where the mouse is,
268 * not vice versa. */
269 NOREF(pScrn);
270 NOREF(x);
271 NOREF(y);
272}
273
274static void
275vbox_hide_cursor(ScrnInfoPtr pScrn)
276{
277 VBOXPtr pVBox = pScrn->driverPrivate;
278
279 vbox_vmm_hide_cursor(pScrn, pVBox);
280}
281
282static void
283vbox_show_cursor(ScrnInfoPtr pScrn)
284{
285 VBOXPtr pVBox = pScrn->driverPrivate;
286
287 vbox_vmm_show_cursor(pScrn, pVBox);
288}
289
290static void
291vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image)
292{
293 VBOXPtr pVBox = pScrn->driverPrivate;
294
295 vbox_vmm_load_cursor_image(pScrn, pVBox, image);
296}
297
298static Bool
299vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs)
300{
301 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
302 return vbox_host_uses_hwcursor(pScrn);
303}
304
305static unsigned char
306color_to_byte(unsigned c)
307{
308 return (c >> 8) & 0xff;
309}
310
311static unsigned char *
312vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs)
313{
314 VBOXPtr pVBox;
315 CursorBitsPtr bitsp;
316 unsigned short w, h, x, y;
317 unsigned char *c, *p, *pm, *ps, *m;
318 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch;
319 CARD32 fc, bc, *cp;
320 int rc, scrnIndex = infoPtr->pScrn->scrnIndex;
321 struct vboxCursorImage *pImage;
322
323 pVBox = infoPtr->pScrn->driverPrivate;
324 bitsp = pCurs->bits;
325 w = bitsp->width;
326 h = bitsp->height;
327
328 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
329 RETERROR(scrnIndex, NULL,
330 "Error invalid cursor dimensions %dx%d\n", w, h);
331
332 if ((bitsp->xhot > w) || (bitsp->yhot > h))
333 RETERROR(scrnIndex, NULL,
334 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
335 bitsp->xhot, bitsp->yhot, w, h);
336
337 srcPitch = PixmapBytePad (bitsp->width, 1);
338 dstPitch = (w + 7) / 8;
339 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3;
340 sizeRgba = w * h * 4;
341 sizeRequest = sizeMask + sizeRgba + sizeof(*pImage);
342
343 p = c = calloc (1, sizeRequest);
344 if (!c)
345 RETERROR(scrnIndex, NULL,
346 "Error failed to alloc %lu bytes for cursor\n",
347 (unsigned long) sizeRequest);
348
349 pImage = (struct vboxCursorImage *)p;
350 pImage->pPixels = m = p + sizeof(*pImage);
351 cp = (CARD32 *)(m + sizeMask);
352
353 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n",
354 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch);
355 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp);
356
357 fc = color_to_byte (pCurs->foreBlue)
358 | (color_to_byte (pCurs->foreGreen) << 8)
359 | (color_to_byte (pCurs->foreRed) << 16);
360
361 bc = color_to_byte (pCurs->backBlue)
362 | (color_to_byte (pCurs->backGreen) << 8)
363 | (color_to_byte (pCurs->backRed) << 16);
364
365 /*
366 * Convert the Xorg source/mask bits to the and/xor bits VBox needs.
367 * Xorg:
368 * The mask is a bitmap indicating which parts of the cursor are
369 * transparent and which parts are drawn. The source is a bitmap
370 * indicating which parts of the non-transparent portion of the
371 * the cursor should be painted in the foreground color and which
372 * should be painted in the background color. By default, set bits
373 * indicate the opaque part of the mask bitmap and clear bits
374 * indicate the transparent part.
375 * VBox:
376 * The color data is the XOR mask. The AND mask bits determine
377 * which pixels of the color data (XOR mask) will replace (overwrite)
378 * the screen pixels (AND mask bit = 0) and which ones will be XORed
379 * with existing screen pixels (AND mask bit = 1).
380 * For example when you have the AND mask all 0, then you see the
381 * correct mouse pointer image surrounded by black square.
382 */
383 for (pm = bitsp->mask, ps = bitsp->source, y = 0;
384 y < h;
385 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch)
386 {
387 for (x = 0; x < w; ++x)
388 {
389 if (pm[x / 8] & (1 << (x % 8)))
390 {
391 /* opaque, leave AND mask bit at 0 */
392 if (ps[x / 8] & (1 << (x % 8)))
393 {
394 *cp++ = fc;
395 PUT_PIXEL('X');
396 }
397 else
398 {
399 *cp++ = bc;
400 PUT_PIXEL('*');
401 }
402 }
403 else
404 {
405 /* transparent, set AND mask bit */
406 m[x / 8] |= 1 << (7 - (x % 8));
407 /* don't change the screen pixel */
408 *cp++ = 0;
409 PUT_PIXEL(' ');
410 }
411 }
412 PUT_PIXEL('\n');
413 }
414
415 pImage->cWidth = w;
416 pImage->cHeight = h;
417 pImage->cHotX = bitsp->xhot;
418 pImage->cHotY = bitsp->yhot;
419 pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE;
420 pImage->cbLength = sizeRequest - sizeof(*pImage);
421
422#ifdef DEBUG_POINTER
423 ErrorF("shape = %p\n", p);
424 vbox_show_shape(w, h, bc, c);
425#endif
426
427 return p;
428}
429
430#ifdef ARGB_CURSOR
431static Bool
432vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs)
433{
434 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
435 Bool rc = TRUE;
436
437 if (!vbox_host_uses_hwcursor(pScrn))
438 rc = FALSE;
439 if ( rc
440 && ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT)
441 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH)
442 || (pScrn->bitsPerPixel <= 8)
443 )
444 )
445 rc = FALSE;
446 return rc;
447}
448
449
450static void
451vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs)
452{
453 VBOXPtr pVBox;
454 VMMDevReqMousePointer *reqp;
455 CursorBitsPtr bitsp;
456 unsigned short w, h;
457 unsigned short cx, cy;
458 unsigned char *pm;
459 CARD32 *pc;
460 size_t sizeData, sizeMask;
461 CARD8 *p;
462 int scrnIndex;
463 uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE
464 | VBOX_MOUSE_POINTER_ALPHA;
465 int rc;
466
467 pVBox = pScrn->driverPrivate;
468 bitsp = pCurs->bits;
469 w = bitsp->width;
470 h = bitsp->height;
471 scrnIndex = pScrn->scrnIndex;
472
473 /* Mask must be generated for alpha cursors, that is required by VBox. */
474 /* note: (michael) the next struct must be 32bit aligned. */
475 sizeMask = ((w + 7) / 8 * h + 3) & ~3;
476
477 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT)
478 RETERROR(scrnIndex, ,
479 "Error invalid cursor dimensions %dx%d\n", w, h);
480
481 if ((bitsp->xhot > w) || (bitsp->yhot > h))
482 RETERROR(scrnIndex, ,
483 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n",
484 bitsp->xhot, bitsp->yhot, w, h);
485
486 sizeData = w * h * 4 + sizeMask;
487 p = calloc(1, sizeData);
488 if (!p)
489 RETERROR(scrnIndex, ,
490 "Error failed to alloc %lu bytes for cursor\n",
491 (unsigned long)sizeData);
492
493 memcpy(p + sizeMask, bitsp->argb, w * h * 4);
494
495 /* Emulate the AND mask. */
496 pm = p;
497 pc = bitsp->argb;
498
499 /* Init AND mask to 1 */
500 memset(pm, 0xFF, sizeMask);
501
502 /*
503 * The additions driver must provide the AND mask for alpha cursors. The host frontend
504 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor.
505 * But if the host does not support ARGB, then it simply uses the AND mask and the color
506 * data to draw a normal color cursor.
507 */
508 for (cy = 0; cy < h; cy++)
509 {
510 unsigned char bitmask = 0x80;
511
512 for (cx = 0; cx < w; cx++, bitmask >>= 1)
513 {
514 if (bitmask == 0)
515 bitmask = 0x80;
516
517 if (pc[cx] >= 0xF0000000)
518 pm[cx / 8] &= ~bitmask;
519 }
520
521 /* Point to next source and dest scans */
522 pc += w;
523 pm += (w + 7) / 8;
524 }
525
526 VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot,
527 bitsp->yhot, w, h, p, sizeData);
528 free(p);
529}
530#endif
531
532Bool
533vbox_cursor_init(ScreenPtr pScreen)
534{
535 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
536 VBOXPtr pVBox = pScrn->driverPrivate;
537 xf86CursorInfoPtr pCurs = NULL;
538 Bool rc = TRUE;
539
540 TRACE_ENTRY();
541 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec();
542 if (!pCurs) {
543 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
544 "Failed to create X Window cursor information structures for virtual mouse.\n");
545 rc = FALSE;
546 }
547 if (rc) {
548 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH;
549 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT;
550 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP
551 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1
552 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST;
553
554 pCurs->SetCursorColors = vbox_set_cursor_colors;
555 pCurs->SetCursorPosition = vbox_set_cursor_position;
556 pCurs->LoadCursorImage = vbox_load_cursor_image;
557 pCurs->HideCursor = vbox_hide_cursor;
558 pCurs->ShowCursor = vbox_show_cursor;
559 pCurs->UseHWCursor = vbox_use_hw_cursor;
560 pCurs->RealizeCursor = vbox_realize_cursor;
561
562#ifdef ARGB_CURSOR
563 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb;
564 pCurs->LoadCursorARGB = vbox_load_cursor_argb;
565#endif
566
567 /* Hide the host cursor before we initialise if we wish to use a
568 * software cursor. */
569 if (pVBox->forceSWCursor)
570 vbox_vmm_hide_cursor(pScrn, pVBox);
571 rc = xf86InitCursor(pScreen, pCurs);
572 }
573 if (!rc)
574 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
575 "Failed to enable mouse pointer integration.\n");
576 if (!rc && (pCurs != NULL))
577 xf86DestroyCursorInfoRec(pCurs);
578 return rc;
579}
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