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