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