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