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