pointer.c revision e07dc26b
1/* $Id: pointer.c,v 1.1.1.1 2019/01/09 23:50:31 mrg Exp $ */ 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#ifndef PCIACCESS 29# include "xf86Pci.h" 30# include <Pci.h> 31#endif 32 33#include "xf86.h" 34#define NEED_XF86_TYPES 35#include "compiler.h" 36#include "cursorstr.h" 37#include "servermd.h" 38 39#include "vboxvideo.h" 40 41#ifdef XORG_7X 42# include <stdlib.h> 43# include <string.h> 44#endif 45 46#define VBOX_MAX_CURSOR_WIDTH 64 47#define VBOX_MAX_CURSOR_HEIGHT 64 48 49/************************************************************************** 50* Debugging functions and macros * 51**************************************************************************/ 52 53/* #define DEBUG_POINTER */ 54 55#ifdef DEBUG 56# define PUT_PIXEL(c) ErrorF ("%c", c) 57#else /* DEBUG_VIDEO not defined */ 58# define PUT_PIXEL(c) do { } while(0) 59#endif /* DEBUG_VIDEO not defined */ 60 61/** Macro to printf an error message and return from a function */ 62#define RETERROR(scrnIndex, RetVal, ...) \ 63 do \ 64 { \ 65 xf86DrvMsg(scrnIndex, X_ERROR, __VA_ARGS__); \ 66 return RetVal; \ 67 } \ 68 while (0) 69 70/** Structure to pass cursor image data between realise_cursor() and 71 * load_cursor_image(). The members match the parameters to 72 * @a VBoxHGSMIUpdatePointerShape(). */ 73struct vboxCursorImage 74{ 75 uint32_t fFlags; 76 uint32_t cHotX; 77 uint32_t cHotY; 78 uint32_t cWidth; 79 uint32_t cHeight; 80 uint8_t *pPixels; 81 uint32_t cbLength; 82}; 83 84#ifdef DEBUG_POINTER 85static void 86vbox_show_shape(unsigned short w, unsigned short h, CARD32 bg, unsigned char *image) 87{ 88 size_t x, y; 89 unsigned short pitch; 90 CARD32 *color; 91 unsigned char *mask; 92 size_t sizeMask; 93 94 image += sizeof(struct vboxCursorImage); 95 mask = image; 96 pitch = (w + 7) / 8; 97 sizeMask = (pitch * h + 3) & ~3; 98 color = (CARD32 *)(image + sizeMask); 99 100 TRACE_ENTRY(); 101 for (y = 0; y < h; ++y, mask += pitch, color += w) 102 { 103 for (x = 0; x < w; ++x) 104 { 105 if (mask[x / 8] & (1 << (7 - (x % 8)))) 106 ErrorF (" "); 107 else 108 { 109 CARD32 c = color[x]; 110 if (c == bg) 111 ErrorF("Y"); 112 else 113 ErrorF("X"); 114 } 115 } 116 ErrorF("\n"); 117 } 118} 119#endif 120 121/************************************************************************** 122* Main functions * 123**************************************************************************/ 124 125void vbvxCursorTerm(VBOXPtr pVBox) 126{ 127 TRACE_ENTRY(); 128 129 xf86DestroyCursorInfoRec(pVBox->pCurs); 130 pVBox->pCurs = NULL; 131 TRACE_EXIT(); 132} 133 134static void 135vbox_vmm_hide_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox) 136{ 137 int rc; 138 RT_NOREF(pScrn); 139 140 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, 0, 0, 0, 0, 0, NULL, 0); 141 AssertMsg(rc == VINF_SUCCESS, ("Could not hide the virtual mouse pointer, VBox error %d.\n", rc)); 142} 143 144static void 145vbox_vmm_show_cursor(ScrnInfoPtr pScrn, VBOXPtr pVBox) 146{ 147 int rc; 148 RT_NOREF(pScrn); 149 150 if (!pVBox->fUseHardwareCursor) 151 return; 152 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, VBOX_MOUSE_POINTER_VISIBLE, 153 0, 0, 0, 0, NULL, 0); 154 AssertMsg(rc == VINF_SUCCESS, ("Could not unhide the virtual mouse pointer.\n")); 155} 156 157static void 158vbox_vmm_load_cursor_image(ScrnInfoPtr pScrn, VBOXPtr pVBox, 159 unsigned char *pvImage) 160{ 161 int rc; 162 struct vboxCursorImage *pImage; 163 pImage = (struct vboxCursorImage *)pvImage; 164 RT_NOREF(pScrn); 165 166#ifdef DEBUG_POINTER 167 vbox_show_shape(pImage->cWidth, pImage->cHeight, 0, pvImage); 168#endif 169 170 rc = VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, pImage->fFlags, 171 pImage->cHotX, pImage->cHotY, pImage->cWidth, pImage->cHeight, 172 pImage->pPixels, pImage->cbLength); 173 AssertMsg(rc == VINF_SUCCESS, ("Unable to set the virtual mouse pointer image.\n")); 174} 175 176static void 177vbox_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg) 178{ 179 RT_NOREF(pScrn); 180 RT_NOREF(bg); 181 RT_NOREF(fg); 182 /* ErrorF("vbox_set_cursor_colors NOT IMPLEMENTED\n"); */ 183} 184 185 186static void 187vbox_set_cursor_position(ScrnInfoPtr pScrn, int x, int y) 188{ 189 VBOXPtr pVBox = pScrn->driverPrivate; 190 191 /* This currently does nothing. */ 192 VBoxHGSMICursorPosition(&pVBox->guestCtx, true, x, y, NULL, NULL); 193} 194 195static void 196vbox_hide_cursor(ScrnInfoPtr pScrn) 197{ 198 VBOXPtr pVBox = pScrn->driverPrivate; 199 200 vbox_vmm_hide_cursor(pScrn, pVBox); 201} 202 203static void 204vbox_show_cursor(ScrnInfoPtr pScrn) 205{ 206 VBOXPtr pVBox = pScrn->driverPrivate; 207 208 vbox_vmm_show_cursor(pScrn, pVBox); 209} 210 211static void 212vbox_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *image) 213{ 214 VBOXPtr pVBox = pScrn->driverPrivate; 215 216 vbox_vmm_load_cursor_image(pScrn, pVBox, image); 217} 218 219static Bool 220vbox_use_hw_cursor(ScreenPtr pScreen, CursorPtr pCurs) 221{ 222 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; 223 VBOXPtr pVBox = pScrn->driverPrivate; 224 RT_NOREF(pCurs); 225 return pVBox->fUseHardwareCursor; 226} 227 228static unsigned char 229color_to_byte(unsigned c) 230{ 231 return (c >> 8) & 0xff; 232} 233 234static unsigned char * 235vbox_realize_cursor(xf86CursorInfoPtr infoPtr, CursorPtr pCurs) 236{ 237 VBOXPtr pVBox; 238 CursorBitsPtr bitsp; 239 unsigned short w, h, x, y; 240 unsigned char *c, *p, *pm, *ps, *m; 241 size_t sizeRequest, sizeRgba, sizeMask, srcPitch, dstPitch; 242 CARD32 fc, bc, *cp; 243 int scrnIndex = infoPtr->pScrn->scrnIndex; 244 struct vboxCursorImage *pImage; 245 246 pVBox = infoPtr->pScrn->driverPrivate; 247 bitsp = pCurs->bits; 248 w = bitsp->width; 249 h = bitsp->height; 250 251 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT) 252 RETERROR(scrnIndex, NULL, 253 "Error invalid cursor dimensions %dx%d\n", w, h); 254 255 if ((bitsp->xhot > w) || (bitsp->yhot > h)) 256 RETERROR(scrnIndex, NULL, 257 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n", 258 bitsp->xhot, bitsp->yhot, w, h); 259 260 srcPitch = PixmapBytePad (bitsp->width, 1); 261 dstPitch = (w + 7) / 8; 262 sizeMask = ((dstPitch * h) + 3) & (size_t) ~3; 263 sizeRgba = w * h * 4; 264 sizeRequest = sizeMask + sizeRgba + sizeof(*pImage); 265 266 p = c = calloc (1, sizeRequest); 267 if (!c) 268 RETERROR(scrnIndex, NULL, 269 "Error failed to alloc %lu bytes for cursor\n", 270 (unsigned long) sizeRequest); 271 272 pImage = (struct vboxCursorImage *)p; 273 pImage->pPixels = m = p + sizeof(*pImage); 274 cp = (CARD32 *)(m + sizeMask); 275 276 TRACE_LOG ("w=%d h=%d sm=%d sr=%d p=%d\n", 277 w, h, (int) sizeMask, (int) sizeRgba, (int) dstPitch); 278 TRACE_LOG ("m=%p c=%p cp=%p\n", m, c, (void *)cp); 279 280 fc = color_to_byte (pCurs->foreBlue) 281 | (color_to_byte (pCurs->foreGreen) << 8) 282 | (color_to_byte (pCurs->foreRed) << 16); 283 284 bc = color_to_byte (pCurs->backBlue) 285 | (color_to_byte (pCurs->backGreen) << 8) 286 | (color_to_byte (pCurs->backRed) << 16); 287 288 /* 289 * Convert the Xorg source/mask bits to the and/xor bits VBox needs. 290 * Xorg: 291 * The mask is a bitmap indicating which parts of the cursor are 292 * transparent and which parts are drawn. The source is a bitmap 293 * indicating which parts of the non-transparent portion of the 294 * the cursor should be painted in the foreground color and which 295 * should be painted in the background color. By default, set bits 296 * indicate the opaque part of the mask bitmap and clear bits 297 * indicate the transparent part. 298 * VBox: 299 * The color data is the XOR mask. The AND mask bits determine 300 * which pixels of the color data (XOR mask) will replace (overwrite) 301 * the screen pixels (AND mask bit = 0) and which ones will be XORed 302 * with existing screen pixels (AND mask bit = 1). 303 * For example when you have the AND mask all 0, then you see the 304 * correct mouse pointer image surrounded by black square. 305 */ 306 for (pm = bitsp->mask, ps = bitsp->source, y = 0; 307 y < h; 308 ++y, pm += srcPitch, ps += srcPitch, m += dstPitch) 309 { 310 for (x = 0; x < w; ++x) 311 { 312 if (pm[x / 8] & (1 << (x % 8))) 313 { 314 /* opaque, leave AND mask bit at 0 */ 315 if (ps[x / 8] & (1 << (x % 8))) 316 { 317 *cp++ = fc; 318 PUT_PIXEL('X'); 319 } 320 else 321 { 322 *cp++ = bc; 323 PUT_PIXEL('*'); 324 } 325 } 326 else 327 { 328 /* transparent, set AND mask bit */ 329 m[x / 8] |= 1 << (7 - (x % 8)); 330 /* don't change the screen pixel */ 331 *cp++ = 0; 332 PUT_PIXEL(' '); 333 } 334 } 335 PUT_PIXEL('\n'); 336 } 337 338 pImage->cWidth = w; 339 pImage->cHeight = h; 340 pImage->cHotX = bitsp->xhot; 341 pImage->cHotY = bitsp->yhot; 342 pImage->fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE; 343 pImage->cbLength = sizeRequest - sizeof(*pImage); 344 345#ifdef DEBUG_POINTER 346 ErrorF("shape = %p\n", p); 347 vbox_show_shape(w, h, bc, c); 348#endif 349 350 return p; 351} 352 353#ifdef ARGB_CURSOR 354static Bool 355vbox_use_hw_cursor_argb(ScreenPtr pScreen, CursorPtr pCurs) 356{ 357 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; 358 VBOXPtr pVBox = pScrn->driverPrivate; 359 360 if (!pVBox->fUseHardwareCursor) 361 return FALSE; 362 if ( (pCurs->bits->height > VBOX_MAX_CURSOR_HEIGHT) 363 || (pCurs->bits->width > VBOX_MAX_CURSOR_WIDTH) 364 || (pScrn->bitsPerPixel <= 8)) 365 return FALSE; 366 return TRUE; 367} 368 369 370static void 371vbox_load_cursor_argb(ScrnInfoPtr pScrn, CursorPtr pCurs) 372{ 373 VBOXPtr pVBox; 374 CursorBitsPtr bitsp; 375 unsigned short w, h; 376 unsigned short cx, cy; 377 unsigned char *pm; 378 CARD32 *pc; 379 size_t sizeData, sizeMask; 380 CARD8 *p; 381 int scrnIndex; 382 uint32_t fFlags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE 383 | VBOX_MOUSE_POINTER_ALPHA; 384 385 pVBox = pScrn->driverPrivate; 386 bitsp = pCurs->bits; 387 w = bitsp->width; 388 h = bitsp->height; 389 scrnIndex = pScrn->scrnIndex; 390 391 /* Mask must be generated for alpha cursors, that is required by VBox. */ 392 /* note: (michael) the next struct must be 32bit aligned. */ 393 sizeMask = ((w + 7) / 8 * h + 3) & ~3; 394 395 if (!w || !h || w > VBOX_MAX_CURSOR_WIDTH || h > VBOX_MAX_CURSOR_HEIGHT) 396 RETERROR(scrnIndex, , 397 "Error invalid cursor dimensions %dx%d\n", w, h); 398 399 if ((bitsp->xhot > w) || (bitsp->yhot > h)) 400 RETERROR(scrnIndex, , 401 "Error invalid cursor hotspot location %dx%d (max %dx%d)\n", 402 bitsp->xhot, bitsp->yhot, w, h); 403 404 sizeData = w * h * 4 + sizeMask; 405 p = calloc(1, sizeData); 406 if (!p) 407 RETERROR(scrnIndex, , 408 "Error failed to alloc %lu bytes for cursor\n", 409 (unsigned long)sizeData); 410 411 memcpy(p + sizeMask, bitsp->argb, w * h * 4); 412 413 /* Emulate the AND mask. */ 414 pm = p; 415 pc = bitsp->argb; 416 417 /* Init AND mask to 1 */ 418 memset(pm, 0xFF, sizeMask); 419 420 /* 421 * The additions driver must provide the AND mask for alpha cursors. The host frontend 422 * which can handle alpha channel, will ignore the AND mask and draw an alpha cursor. 423 * But if the host does not support ARGB, then it simply uses the AND mask and the color 424 * data to draw a normal color cursor. 425 */ 426 for (cy = 0; cy < h; cy++) 427 { 428 unsigned char bitmask = 0x80; 429 430 for (cx = 0; cx < w; cx++, bitmask >>= 1) 431 { 432 if (bitmask == 0) 433 bitmask = 0x80; 434 435 if (pc[cx] >= 0xF0000000) 436 pm[cx / 8] &= ~bitmask; 437 } 438 439 /* Point to next source and dest scans */ 440 pc += w; 441 pm += (w + 7) / 8; 442 } 443 444 VBoxHGSMIUpdatePointerShape(&pVBox->guestCtx, fFlags, bitsp->xhot, 445 bitsp->yhot, w, h, p, sizeData); 446 free(p); 447} 448#endif 449 450Bool vbvxCursorInit(ScreenPtr pScreen) 451{ 452 ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; 453 VBOXPtr pVBox = pScrn->driverPrivate; 454 xf86CursorInfoPtr pCurs = NULL; 455 Bool rc = TRUE; 456 457 TRACE_ENTRY(); 458 pVBox->pCurs = pCurs = xf86CreateCursorInfoRec(); 459 if (!pCurs) { 460 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 461 "Failed to create X Window cursor information structures for virtual mouse.\n"); 462 rc = FALSE; 463 } 464 if (rc) { 465 pCurs->MaxWidth = VBOX_MAX_CURSOR_WIDTH; 466 pCurs->MaxHeight = VBOX_MAX_CURSOR_HEIGHT; 467 pCurs->Flags = HARDWARE_CURSOR_TRUECOLOR_AT_8BPP 468 | HARDWARE_CURSOR_SOURCE_MASK_INTERLEAVE_1 469 | HARDWARE_CURSOR_BIT_ORDER_MSBFIRST 470 | HARDWARE_CURSOR_UPDATE_UNHIDDEN; 471 472 pCurs->SetCursorColors = vbox_set_cursor_colors; 473 pCurs->SetCursorPosition = vbox_set_cursor_position; 474 pCurs->LoadCursorImage = vbox_load_cursor_image; 475 pCurs->HideCursor = vbox_hide_cursor; 476 pCurs->ShowCursor = vbox_show_cursor; 477 pCurs->UseHWCursor = vbox_use_hw_cursor; 478 pCurs->RealizeCursor = vbox_realize_cursor; 479 480#ifdef ARGB_CURSOR 481 pCurs->UseHWCursorARGB = vbox_use_hw_cursor_argb; 482 pCurs->LoadCursorARGB = vbox_load_cursor_argb; 483#endif 484 485 rc = xf86InitCursor(pScreen, pCurs); 486 } 487 if (!rc) 488 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 489 "Failed to enable mouse pointer integration.\n"); 490 if (!rc && (pCurs != NULL)) 491 xf86DestroyCursorInfoRec(pCurs); 492 return rc; 493} 494