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