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