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