VirtualBox

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

Last change on this file since 36308 was 35843, checked in by vboxsync, 14 years ago

Additions/x11/vboxvideo: some splitting up of the code

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