summaryrefslogtreecommitdiff
path: root/src/Modesetting.c
blob: db1773c55dbfd78b811eb9f26472b376bf8a67e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/*
 * Copyright (C) 2006-2017 Oracle Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <VBoxVideoGuest.h>
#include <VBoxVideoVBE.h>
#include <HGSMIChannels.h>

#ifndef VBOX_GUESTR3XF86MOD
# include <VBoxVideoIPRT.h>
#endif

/**
 * Gets the count of virtual monitors attached to the guest via an HGSMI
 * command
 *
 * @returns the right count on success or 1 on failure.
 * @param  pCtx  the context containing the heap to use
 */
DECLHIDDEN(uint32_t) VBoxHGSMIGetMonitorCount(PHGSMIGUESTCOMMANDCONTEXT pCtx)
{
    /* Query the configured number of displays. */
    uint32_t cDisplays = 0;
    VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays);
    // LogFunc(("cDisplays = %d\n", cDisplays));
    if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS)
        /* Host reported some bad value. Continue in the 1 screen mode. */
        cDisplays = 1;
    return cDisplays;
}


/**
 * Returns the size of the video RAM in bytes.
 *
 * @returns the size
 */
DECLHIDDEN(uint32_t) VBoxVideoGetVRAMSize(void)
{
    /** @note A 32bit read on this port returns the VRAM size. */
    return VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA);
}


/**
 * Check whether this hardware allows the display width to have non-multiple-
 * of-eight values.
 *
 * @returns true if any width is allowed, false otherwise.
 */
DECLHIDDEN(bool) VBoxVideoAnyWidthAllowed(void)
{
    unsigned DispiId;
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_ANYX);
    DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
    return (DispiId == VBE_DISPI_ID_ANYX);
}


/**
 * Tell the host about how VRAM is divided up between each screen via an HGSMI
 * command.  It is acceptable to specify identical data for each screen if
 * they share a single framebuffer.
 *
 * @returns iprt status code, either VERR_NO_MEMORY or the status returned by
 *          @a pfnFill
 * @todo  What was I thinking of with that callback function?  It
 *        would be much simpler to just pass in a structure in normal
 *        memory and copy it.
 * @param  pCtx      the context containing the heap to use
 * @param  u32Count  the number of screens we are activating
 * @param  pfnFill   a callback which initialises the VBVAINFOVIEW structures
 *                   for all screens
 * @param  pvData    context data for @a pfnFill
 */
DECLHIDDEN(int) VBoxHGSMISendViewInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
                                      uint32_t u32Count,
                                      PFNHGSMIFILLVIEWINFO pfnFill,
                                      void *pvData)
{
    int rc;
    /* Issue the screen info command. */
    void *p = VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOVIEW) * u32Count,
                                   HGSMI_CH_VBVA, VBVA_INFO_VIEW);
    if (p)
    {
        VBVAINFOVIEW *pInfo = (VBVAINFOVIEW *)p;
        rc = pfnFill(pvData, pInfo, u32Count);
        if (RT_SUCCESS(rc))
            VBoxHGSMIBufferSubmit (pCtx, p);
        VBoxHGSMIBufferFree(pCtx, p);
    }
    else
        rc = VERR_NO_MEMORY;
    return rc;
}


/**
 * Set a video mode using port registers.  This must be done for the first
 * screen before every HGSMI modeset and also works when HGSM is not enabled.
 * @param  cWidth      the mode width
 * @param  cHeight     the mode height
 * @param  cVirtWidth  the mode pitch
 * @param  cBPP        the colour depth of the mode
 * @param  fFlags      flags for the mode.  These will be or-ed with the
 *                     default _ENABLED flag, so unless you are restoring
 *                     a saved mode or have special requirements you can pass
 *                     zero here.
 * @param  cx          the horizontal panning offset
 * @param  cy          the vertical panning offset
 */
DECLHIDDEN(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight,
                                           uint16_t cVirtWidth, uint16_t cBPP,
                                           uint16_t fFlags, uint16_t cx,
                                           uint16_t cy)
{
    /* set the mode characteristics */
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cWidth);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cHeight);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                VBE_DISPI_INDEX_VIRT_WIDTH);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cVirtWidth);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cBPP);
    /* enable the mode */
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                VBE_DISPI_INDEX_ENABLE);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA,
                                fFlags | VBE_DISPI_ENABLED);
    /* Panning registers */
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                VBE_DISPI_INDEX_X_OFFSET);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cx);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                VBE_DISPI_INDEX_Y_OFFSET);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cy);
    /** @todo read from the port to see if the mode switch was successful */
}


/**
 * Get the video mode for the first screen using the port registers.  All
 * parameters are optional
 * @returns  true if the VBE mode returned is active, false if we are in VGA
 *           mode
 * @note  If anyone else needs additional register values just extend the
 *        function with additional parameters and fix any existing callers.
 * @param  pcWidth      where to store the mode width
 * @param  pcHeight     where to store the mode height
 * @param  pcVirtWidth  where to store the mode pitch
 * @param  pcBPP        where to store the colour depth of the mode
 * @param  pfFlags      where to store the flags for the mode
 */
DECLHIDDEN(bool) VBoxVideoGetModeRegisters(uint16_t *pcWidth, uint16_t *pcHeight,
                                           uint16_t *pcVirtWidth, uint16_t *pcBPP,
                                           uint16_t *pfFlags)
{
    uint16_t fFlags;

    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                VBE_DISPI_INDEX_ENABLE);
    fFlags = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
    if (pcWidth)
    {
        VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                    VBE_DISPI_INDEX_XRES);
        *pcWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
    }
    if (pcHeight)
    {
        VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                    VBE_DISPI_INDEX_YRES);
        *pcHeight = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
    }
    if (pcVirtWidth)
    {
        VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                    VBE_DISPI_INDEX_VIRT_WIDTH);
        *pcVirtWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
    }
    if (pcBPP)
    {
        VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                    VBE_DISPI_INDEX_BPP);
        *pcBPP = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
    }
    if (pfFlags)
        *pfFlags = fFlags;
    return !!(fFlags & VBE_DISPI_ENABLED);
}


/**
 * Disable our extended graphics mode and go back to VGA mode.
 */
DECLHIDDEN(void) VBoxVideoDisableVBE(void)
{
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX,
                                VBE_DISPI_INDEX_ENABLE);
    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, 0);
}


/**
 * Set a video mode via an HGSMI request.  The views must have been
 * initialised first using @a VBoxHGSMISendViewInfo and if the mode is being
 * set on the first display then it must be set first using registers.
 * @param  pCtx      The context containing the heap to use.
 * @param  cDisplay  the screen number
 * @param  cOriginX  the horizontal displacement relative to the first screen
 * @param  cOriginY  the vertical displacement relative to the first screen
 * @param  offStart  the offset of the visible area of the framebuffer
 *                   relative to the framebuffer start
 * @param  cbPitch   the offset in bytes between the starts of two adjecent
 *                   scan lines in video RAM
 * @param  cWidth    the mode width
 * @param  cHeight   the mode height
 * @param  cBPP      the colour depth of the mode
 * @param  fFlags    flags
 */
DECLHIDDEN(void) VBoxHGSMIProcessDisplayInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
                                             uint32_t cDisplay,
                                             int32_t  cOriginX,
                                             int32_t  cOriginY,
                                             uint32_t offStart,
                                             uint32_t cbPitch,
                                             uint32_t cWidth,
                                             uint32_t cHeight,
                                             uint16_t cBPP,
                                             uint16_t fFlags)
{
    /* Issue the screen info command. */
    void *p = VBoxHGSMIBufferAlloc(pCtx,
                                   sizeof (VBVAINFOSCREEN),
                                   HGSMI_CH_VBVA,
                                   VBVA_INFO_SCREEN);
    if (!p)
    {
        // LogFunc(("HGSMIHeapAlloc failed\n"));
    }
    else
    {
        VBVAINFOSCREEN *pScreen = (VBVAINFOSCREEN *)p;

        pScreen->u32ViewIndex    = cDisplay;
        pScreen->i32OriginX      = cOriginX;
        pScreen->i32OriginY      = cOriginY;
        pScreen->u32StartOffset  = offStart;
        pScreen->u32LineSize     = cbPitch;
        pScreen->u32Width        = cWidth;
        pScreen->u32Height       = cHeight;
        pScreen->u16BitsPerPixel = cBPP;
        pScreen->u16Flags        = fFlags;

        VBoxHGSMIBufferSubmit(pCtx, p);

        VBoxHGSMIBufferFree(pCtx, p);
    }
}


/** Report the rectangle relative to which absolute pointer events should be
 *  expressed.  This information remains valid until the next VBVA resize event
 *  for any screen, at which time it is reset to the bounding rectangle of all
 *  virtual screens.
 * @param  pCtx      The context containing the heap to use.
 * @param  cOriginX  Upper left X coordinate relative to the first screen.
 * @param  cOriginY  Upper left Y coordinate relative to the first screen.
 * @param  cWidth    Rectangle width.
 * @param  cHeight   Rectangle height.
 * @returns  iprt status code.
 * @returns  VERR_NO_MEMORY      HGSMI heap allocation failed.
 */
DECLHIDDEN(int)      VBoxHGSMIUpdateInputMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, int32_t  cOriginX, int32_t  cOriginY,
                                                 uint32_t cWidth, uint32_t cHeight)
{
    int rc = VINF_SUCCESS;
    VBVAREPORTINPUTMAPPING *p;
    // Log(("%s: cOriginX=%d, cOriginY=%d, cWidth=%u, cHeight=%u\n", __PRETTY_FUNCTION__, (int)cOriginX, (int)cOriginX,
    //      (unsigned)cWidth, (unsigned)cHeight));

    /* Allocate the IO buffer. */
    p = (VBVAREPORTINPUTMAPPING *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAREPORTINPUTMAPPING), HGSMI_CH_VBVA,
                                                       VBVA_REPORT_INPUT_MAPPING);
    if (p)
    {
        /* Prepare data to be sent to the host. */
        p->x  = cOriginX;
        p->y  = cOriginY;
        p->cx = cWidth;
        p->cy = cHeight;
        rc = VBoxHGSMIBufferSubmit(pCtx, p);
        /* Free the IO buffer. */
        VBoxHGSMIBufferFree(pCtx, p);
    }
    else
        rc = VERR_NO_MEMORY;
    // LogFunc(("rc = %d\n", rc));
    return rc;
}


/**
 * Get most recent video mode hints.
 * @param  pCtx      the context containing the heap to use
 * @param  cScreens  the number of screens to query hints for, starting at 0.
 * @param  paHints   array of VBVAMODEHINT structures for receiving the hints.
 * @returns  iprt status code
 * @returns  VERR_NO_MEMORY      HGSMI heap allocation failed.
 * @returns  VERR_NOT_SUPPORTED  Host does not support this command.
 */
DECLHIDDEN(int) VBoxHGSMIGetModeHints(PHGSMIGUESTCOMMANDCONTEXT pCtx,
                                      unsigned cScreens, VBVAMODEHINT *paHints)
{
    int rc;
    void *p;

    AssertPtr(paHints);
    if (!paHints)
        return VERR_INVALID_POINTER;

    p = VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAQUERYMODEHINTS)
                                       + cScreens * sizeof(VBVAMODEHINT),
                             HGSMI_CH_VBVA, VBVA_QUERY_MODE_HINTS);
    if (!p)
    {
        // LogFunc(("HGSMIHeapAlloc failed\n"));
        return VERR_NO_MEMORY;
    }
    else
    {
        VBVAQUERYMODEHINTS *pQuery   = (VBVAQUERYMODEHINTS *)p;

        pQuery->cHintsQueried        = cScreens;
        pQuery->cbHintStructureGuest = sizeof(VBVAMODEHINT);
        pQuery->rc                   = VERR_NOT_SUPPORTED;

        VBoxHGSMIBufferSubmit(pCtx, p);
        rc = pQuery->rc;
        if (RT_SUCCESS(rc))
            memcpy(paHints, ((uint8_t *)p) + sizeof(VBVAQUERYMODEHINTS),
                   cScreens * sizeof(VBVAMODEHINT));

        VBoxHGSMIBufferFree(pCtx, p);
    }
    return rc;
}


/**
 * Query the supported flags in VBVAINFOSCREEN::u16Flags.
 *
 * @returns The mask of VBVA_SCREEN_F_* flags or 0 if host does not support the request.
 * @param  pCtx  the context containing the heap to use
 */
DECLHIDDEN(uint16_t) VBoxHGSMIGetScreenFlags(PHGSMIGUESTCOMMANDCONTEXT pCtx)
{
    uint32_t u32Flags = 0;
    int rc = VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &u32Flags);
    // LogFunc(("u32Flags = 0x%x rc %Rrc\n", u32Flags, rc));
    if (RT_FAILURE(rc) || u32Flags > UINT16_MAX)
        u32Flags = 0;
    return (uint16_t)u32Flags;
}