vmwarectrl.c revision 2e8abef9
1/* 2 * Copyright 2006 by VMware, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Except as contained in this notice, the name of the copyright holder(s) 23 * and author(s) shall not be used in advertising or otherwise to promote 24 * the sale, use or other dealings in this Software without prior written 25 * authorization from the copyright holder(s) and author(s). 26 */ 27 28/* 29 * vmwarectrl.c -- 30 * 31 * The implementation of the VMWARE_CTRL protocol extension that 32 * allows X clients to communicate with the driver. 33 */ 34 35 36#ifdef HAVE_CONFIG_H 37#include "config.h" 38#endif 39 40#define NEED_REPLIES 41#define NEED_EVENTS 42#include "dixstruct.h" 43#include "extnsionst.h" 44#include <X11/X.h> 45#include <X11/extensions/panoramiXproto.h> 46 47#include "vmware.h" 48#include "vmwarectrlproto.h" 49 50 51/* 52 *---------------------------------------------------------------------------- 53 * 54 * VMwareCtrlQueryVersion -- 55 * 56 * Implementation of QueryVersion command handler. Initialises and 57 * sends a reply. 58 * 59 * Results: 60 * Standard response codes. 61 * 62 * Side effects: 63 * Writes reply to client 64 * 65 *---------------------------------------------------------------------------- 66 */ 67 68static int 69VMwareCtrlQueryVersion(ClientPtr client) 70{ 71 xVMwareCtrlQueryVersionReply rep = { 0, }; 72 register int n; 73 74 REQUEST_SIZE_MATCH(xVMwareCtrlQueryVersionReq); 75 76 rep.type = X_Reply; 77 rep.length = 0; 78 rep.sequenceNumber = client->sequence; 79 rep.majorVersion = VMWARE_CTRL_MAJOR_VERSION; 80 rep.minorVersion = VMWARE_CTRL_MINOR_VERSION; 81 if (client->swapped) { 82 swaps(&rep.sequenceNumber, n); 83 swapl(&rep.length, n); 84 swapl(&rep.majorVersion, n); 85 swapl(&rep.minorVersion, n); 86 } 87 WriteToClient(client, sizeof(xVMwareCtrlQueryVersionReply), (char *)&rep); 88 89 return client->noClientException; 90} 91 92 93/* 94 *---------------------------------------------------------------------------- 95 * 96 * VMwareCtrlDoSetRes -- 97 * 98 * Set the custom resolution into the mode list. 99 * 100 * This is done by alternately updating one of two dynamic modes. It is 101 * done this way because the server gets upset if you try to switch 102 * to a new resolution that has the same index as the current one. 103 * 104 * Results: 105 * TRUE on success, FALSE otherwise. 106 * 107 * Side effects: 108 * One dynamic mode will be updated if successful. 109 * 110 *---------------------------------------------------------------------------- 111 */ 112 113static Bool 114VMwareCtrlDoSetRes(ScrnInfoPtr pScrn, 115 CARD32 x, 116 CARD32 y, 117 Bool resetXinerama) 118{ 119 int modeIndex; 120 DisplayModePtr mode; 121 VMWAREPtr pVMWARE = VMWAREPTR(pScrn); 122 123 if (pScrn && pScrn->modes) { 124 VmwareLog(("DoSetRes: %d %d\n", x, y)); 125 126 if (resetXinerama) { 127 xfree(pVMWARE->xineramaNextState); 128 pVMWARE->xineramaNextState = NULL; 129 pVMWARE->xineramaNextNumOutputs = 0; 130 } 131 132 /* 133 * Don't resize larger than possible but don't 134 * return an X Error either. 135 */ 136 if (x > pVMWARE->maxWidth || 137 y > pVMWARE->maxHeight) { 138 return TRUE; 139 } 140 141 /* 142 * Find an dynamic mode which isn't current, and replace it with 143 * the requested mode. Normally this will cause us to alternate 144 * between two dynamic mode slots, but there are some important 145 * corner cases to consider. For example, adding the same mode 146 * multiple times, adding a mode that we never switch to, or 147 * adding a mode which is a duplicate of a built-in mode. The 148 * best way to handle all of these cases is to directly test the 149 * dynamic mode against the current mode pointer for this 150 * screen. 151 */ 152 153 for (modeIndex = 0; modeIndex < NUM_DYN_MODES; modeIndex++) { 154 /* 155 * Initialise the dynamic mode if it hasn't been used before. 156 */ 157 if (!pVMWARE->dynModes[modeIndex]) { 158 pVMWARE->dynModes[modeIndex] = VMWAREAddDisplayMode(pScrn, "DynMode", 1, 1); 159 } 160 161 mode = pVMWARE->dynModes[modeIndex]; 162 if (mode != pScrn->currentMode) { 163 break; 164 } 165 } 166 167 mode->HDisplay = x; 168 mode->VDisplay = y; 169 170 return TRUE; 171 } else { 172 return FALSE; 173 } 174} 175 176 177/* 178 *---------------------------------------------------------------------------- 179 * 180 * VMwareCtrlSetRes -- 181 * 182 * Implementation of SetRes command handler. Initialises and sends a 183 * reply. 184 * 185 * Results: 186 * Standard response codes. 187 * 188 * Side effects: 189 * Writes reply to client 190 * 191 *---------------------------------------------------------------------------- 192 */ 193 194static int 195VMwareCtrlSetRes(ClientPtr client) 196{ 197 REQUEST(xVMwareCtrlSetResReq); 198 xVMwareCtrlSetResReply rep = { 0, }; 199 ScrnInfoPtr pScrn; 200 ExtensionEntry *ext; 201 register int n; 202 203 REQUEST_SIZE_MATCH(xVMwareCtrlSetResReq); 204 205 if (!(ext = CheckExtension(VMWARE_CTRL_PROTOCOL_NAME))) { 206 return BadMatch; 207 } 208 209 pScrn = ext->extPrivate; 210 if (pScrn->scrnIndex != stuff->screen) { 211 return BadMatch; 212 } 213 214 if (!VMwareCtrlDoSetRes(pScrn, stuff->x, stuff->y, TRUE)) { 215 return BadValue; 216 } 217 218 rep.type = X_Reply; 219 rep.length = (sizeof(xVMwareCtrlSetResReply) - sizeof(xGenericReply)) >> 2; 220 rep.sequenceNumber = client->sequence; 221 rep.screen = stuff->screen; 222 rep.x = stuff->x; 223 rep.y = stuff->y; 224 if (client->swapped) { 225 swaps(&rep.sequenceNumber, n); 226 swapl(&rep.length, n); 227 swapl(&rep.screen, n); 228 swapl(&rep.x, n); 229 swapl(&rep.y, n); 230 } 231 WriteToClient(client, sizeof(xVMwareCtrlSetResReply), (char *)&rep); 232 233 return client->noClientException; 234} 235 236 237/* 238 *---------------------------------------------------------------------------- 239 * 240 * VMwareCtrlDoSetTopology -- 241 * 242 * Set the custom topology and set a dynamic mode to the bounding box 243 * of the passed topology. If a topology is already pending, then do 244 * nothing but do not return failure. 245 * 246 * Results: 247 * TRUE on success, FALSE otherwise. 248 * 249 * Side effects: 250 * One dynamic mode and the pending xinerama state will be updated if 251 * successful. 252 * 253 *---------------------------------------------------------------------------- 254 */ 255 256static Bool 257VMwareCtrlDoSetTopology(ScrnInfoPtr pScrn, 258 xXineramaScreenInfo *extents, 259 unsigned long number) 260{ 261 VMWAREPtr pVMWARE = VMWAREPTR(pScrn); 262 263 if (pVMWARE && pVMWARE->xinerama) { 264 VMWAREXineramaPtr xineramaState; 265 short maxX = 0; 266 short maxY = 0; 267 size_t i; 268 269 if (pVMWARE->xineramaNextState) { 270 VmwareLog(("DoSetTopology: Aborting due to existing pending state\n")); 271 return TRUE; 272 } 273 274 for (i = 0; i < number; i++) { 275 maxX = MAX(maxX, extents[i].x_org + extents[i].width); 276 maxY = MAX(maxY, extents[i].y_org + extents[i].height); 277 } 278 279 VmwareLog(("DoSetTopology: %d %d\n", maxX, maxY)); 280 281 xineramaState = (VMWAREXineramaPtr)xcalloc(number, sizeof(VMWAREXineramaRec)); 282 if (xineramaState) { 283 memcpy(xineramaState, extents, number * sizeof (VMWAREXineramaRec)); 284 285 /* 286 * Make this the new pending Xinerama state. Normally we'll 287 * wait until the next mode switch in order to synchronously 288 * push this state out to X clients and the virtual hardware. 289 * 290 * However, if we're already in the right video mode, there 291 * will be no mode change. In this case, push it out 292 * immediately. 293 */ 294 xfree(pVMWARE->xineramaNextState); 295 pVMWARE->xineramaNextState = xineramaState; 296 pVMWARE->xineramaNextNumOutputs = number; 297 298 if (maxX == pVMWARE->ModeReg.svga_reg_width && 299 maxY == pVMWARE->ModeReg.svga_reg_height) { 300 301 /* 302 * XXX: 303 * 304 * There are problems with trying to set a Xinerama state 305 * without a mode switch. The biggest one is that 306 * applications typically won't notice a topology change 307 * that occurs without a mode switch. If you run "xdpyinfo 308 * -ext XINERAMA" after one such topology change, it will 309 * report the new data, but apps (like the GNOME Panel) 310 * will not notice until the next mode change. 311 * 312 * I don't think there's any good solution to this... as 313 * far as I know, even on a non-virtualized machine 314 * there's no way for an app to find out if the Xinerama 315 * opology changes without a resolution change also 316 * occurring. There might be some cheats we can take, like 317 * swithcing to a new mode with the same resolution and a 318 * different (fake) refresh rate, or temporarily switching 319 * to an intermediate mode. Ick. 320 * 321 * The other annoyance here is that when we reprogram the 322 * SVGA device's monitor topology registers, it may 323 * rearrange those monitors on the host's screen, but they 324 * will still have the old contents. This might be 325 * correct, but it isn't guaranteed to match what's on X's 326 * framebuffer at the moment. So we'll send a 327 * full-framebuffer update rect afterwards. 328 */ 329 330 vmwareNextXineramaState(pVMWARE); 331 vmwareSendSVGACmdUpdateFullScreen(pVMWARE); 332 333 return TRUE; 334 } else { 335 return VMwareCtrlDoSetRes(pScrn, maxX, maxY, FALSE); 336 } 337 338 } else { 339 return FALSE; 340 } 341 } else { 342 return FALSE; 343 } 344} 345 346 347/* 348 *---------------------------------------------------------------------------- 349 * 350 * VMwareCtrlSetTopology -- 351 * 352 * Implementation of SetTopology command handler. Initialises and sends a 353 * reply. 354 * 355 * Results: 356 * Standard response codes. 357 * 358 * Side effects: 359 * Writes reply to client 360 * 361 *---------------------------------------------------------------------------- 362 */ 363 364static int 365VMwareCtrlSetTopology(ClientPtr client) 366{ 367 REQUEST(xVMwareCtrlSetTopologyReq); 368 xVMwareCtrlSetTopologyReply rep = { 0, }; 369 ScrnInfoPtr pScrn; 370 ExtensionEntry *ext; 371 register int n; 372 xXineramaScreenInfo *extents; 373 374 REQUEST_AT_LEAST_SIZE(xVMwareCtrlSetTopologyReq); 375 376 if (!(ext = CheckExtension(VMWARE_CTRL_PROTOCOL_NAME))) { 377 return BadMatch; 378 } 379 380 pScrn = ext->extPrivate; 381 if (pScrn->scrnIndex != stuff->screen) { 382 return BadMatch; 383 } 384 385 extents = (xXineramaScreenInfo *)(stuff + 1); 386 if (!VMwareCtrlDoSetTopology(pScrn, extents, stuff->number)) { 387 return BadValue; 388 } 389 390 rep.type = X_Reply; 391 rep.length = (sizeof(xVMwareCtrlSetTopologyReply) - sizeof(xGenericReply)) >> 2; 392 rep.sequenceNumber = client->sequence; 393 rep.screen = stuff->screen; 394 if (client->swapped) { 395 swaps(&rep.sequenceNumber, n); 396 swapl(&rep.length, n); 397 swapl(&rep.screen, n); 398 } 399 WriteToClient(client, sizeof(xVMwareCtrlSetTopologyReply), (char *)&rep); 400 401 return client->noClientException; 402} 403 404 405/* 406 *---------------------------------------------------------------------------- 407 * 408 * VMwareCtrlDispatch -- 409 * 410 * Dispatcher for VMWARE_CTRL commands. Calls the correct handler for 411 * each command type. 412 * 413 * Results: 414 * Standard response codes. 415 * 416 * Side effects: 417 * Side effects of individual command handlers. 418 * 419 *---------------------------------------------------------------------------- 420 */ 421 422static int 423VMwareCtrlDispatch(ClientPtr client) 424{ 425 REQUEST(xReq); 426 427 switch(stuff->data) { 428 case X_VMwareCtrlQueryVersion: 429 return VMwareCtrlQueryVersion(client); 430 case X_VMwareCtrlSetRes: 431 return VMwareCtrlSetRes(client); 432 case X_VMwareCtrlSetTopology: 433 return VMwareCtrlSetTopology(client); 434 } 435 return BadRequest; 436} 437 438 439/* 440 *---------------------------------------------------------------------------- 441 * 442 * SVMwareCtrlQueryVersion -- 443 * 444 * Wrapper for QueryVersion handler that handles input from other-endian 445 * clients. 446 * 447 * Results: 448 * Standard response codes. 449 * 450 * Side effects: 451 * Side effects of unswapped implementation. 452 * 453 *---------------------------------------------------------------------------- 454 */ 455 456static int 457SVMwareCtrlQueryVersion(ClientPtr client) 458{ 459 register int n; 460 461 REQUEST(xVMwareCtrlQueryVersionReq); 462 REQUEST_SIZE_MATCH(xVMwareCtrlQueryVersionReq); 463 464 swaps(&stuff->length, n); 465 466 return VMwareCtrlQueryVersion(client); 467} 468 469 470/* 471 *---------------------------------------------------------------------------- 472 * 473 * SVMwareCtrlSetRes -- 474 * 475 * Wrapper for SetRes handler that handles input from other-endian 476 * clients. 477 * 478 * Results: 479 * Standard response codes. 480 * 481 * Side effects: 482 * Side effects of unswapped implementation. 483 * 484 *---------------------------------------------------------------------------- 485 */ 486 487static int 488SVMwareCtrlSetRes(ClientPtr client) 489{ 490 register int n; 491 492 REQUEST(xVMwareCtrlSetResReq); 493 REQUEST_SIZE_MATCH(xVMwareCtrlSetResReq); 494 495 swaps(&stuff->length, n); 496 swapl(&stuff->screen, n); 497 swapl(&stuff->x, n); 498 swapl(&stuff->y, n); 499 500 return VMwareCtrlSetRes(client); 501} 502 503 504/* 505 *---------------------------------------------------------------------------- 506 * 507 * SVMwareCtrlSetTopology -- 508 * 509 * Wrapper for SetTopology handler that handles input from other-endian 510 * clients. 511 * 512 * Results: 513 * Standard response codes. 514 * 515 * Side effects: 516 * Side effects of unswapped implementation. 517 * 518 *---------------------------------------------------------------------------- 519 */ 520 521static int 522SVMwareCtrlSetTopology(ClientPtr client) 523{ 524 register int n; 525 526 REQUEST(xVMwareCtrlSetTopologyReq); 527 REQUEST_SIZE_MATCH(xVMwareCtrlSetTopologyReq); 528 529 swaps(&stuff->length, n); 530 swapl(&stuff->screen, n); 531 swapl(&stuff->number, n); 532 /* Each extent is a struct of shorts. */ 533 SwapRestS(stuff); 534 535 return VMwareCtrlSetTopology(client); 536} 537 538 539/* 540 *---------------------------------------------------------------------------- 541 * 542 * SVMwareCtrlDispatch -- 543 * 544 * Wrapper for dispatcher that handles input from other-endian clients. 545 * 546 * Results: 547 * Standard response codes. 548 * 549 * Side effects: 550 * Side effects of individual command handlers. 551 * 552 *---------------------------------------------------------------------------- 553 */ 554 555static int 556SVMwareCtrlDispatch(ClientPtr client) 557{ 558 REQUEST(xReq); 559 560 switch(stuff->data) { 561 case X_VMwareCtrlQueryVersion: 562 return SVMwareCtrlQueryVersion(client); 563 case X_VMwareCtrlSetRes: 564 return SVMwareCtrlSetRes(client); 565 case X_VMwareCtrlSetTopology: 566 return SVMwareCtrlSetTopology(client); 567 } 568 return BadRequest; 569} 570 571 572/* 573 *---------------------------------------------------------------------------- 574 * 575 * VMwareCtrlResetProc -- 576 * 577 * Cleanup handler called when the extension is removed. 578 * 579 * Results: 580 * None 581 * 582 * Side effects: 583 * None 584 * 585 *---------------------------------------------------------------------------- 586 */ 587 588static void 589VMwareCtrlResetProc(ExtensionEntry* extEntry) 590{ 591 /* Currently, no cleanup is necessary. */ 592} 593 594 595/* 596 *---------------------------------------------------------------------------- 597 * 598 * VMwareCtrl_ExitInit -- 599 * 600 * Initialiser for the VMWARE_CTRL protocol extension. 601 * 602 * Results: 603 * None. 604 * 605 * Side effects: 606 * Protocol extension will be registered if successful. 607 * 608 *---------------------------------------------------------------------------- 609 */ 610 611void 612VMwareCtrl_ExtInit(ScrnInfoPtr pScrn) 613{ 614 ExtensionEntry *myext; 615 616 if (!(myext = CheckExtension(VMWARE_CTRL_PROTOCOL_NAME))) { 617 if (!(myext = AddExtension(VMWARE_CTRL_PROTOCOL_NAME, 0, 0, 618 VMwareCtrlDispatch, 619 SVMwareCtrlDispatch, 620 VMwareCtrlResetProc, 621 StandardMinorOpcode))) { 622 xf86DrvMsg(pScrn->scrnIndex, X_ERROR, 623 "Failed to add VMWARE_CTRL extension\n"); 624 return; 625 } 626 627 /* 628 * For now, only support one screen as that's all the virtual 629 * hardware supports. 630 */ 631 myext->extPrivate = pScrn; 632 633 xf86DrvMsg(pScrn->scrnIndex, X_INFO, 634 "Initialized VMWARE_CTRL extension version %d.%d\n", 635 VMWARE_CTRL_MAJOR_VERSION, VMWARE_CTRL_MINOR_VERSION); 636 } 637} 638