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