VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxSDL/Framebuffer.cpp@ 99090

Last change on this file since 99090 was 99090, checked in by vboxsync, 15 months ago

FE/SDL: Framebuffer rendering fixes for Windows hosts. bugref:9449

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.7 KB
Line 
1/* $Id: Framebuffer.cpp 99090 2023-03-21 15:24:35Z vboxsync $ */
2/** @file
3 * VBoxSDL - Implementation of VBoxSDLFB (SDL framebuffer) class
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28#include <VBox/com/com.h>
29#include <VBox/com/array.h>
30#include <VBox/com/string.h>
31#include <VBox/com/Guid.h>
32#include <VBox/com/ErrorInfo.h>
33#include <VBox/com/VirtualBox.h>
34
35#include <iprt/stream.h>
36#include <iprt/env.h>
37
38#ifdef RT_OS_OS2
39# undef RT_MAX
40// from <iprt/cdefs.h>
41# define RT_MAX(Value1, Value2) ((Value1) >= (Value2) ? (Value1) : (Value2))
42#endif
43
44using namespace com;
45
46#define LOG_GROUP LOG_GROUP_GUI
47#include <iprt/errcore.h>
48#include <VBox/log.h>
49
50#include "VBoxSDL.h"
51#include "Framebuffer.h"
52#include "Ico64x01.h"
53
54#if defined(RT_OS_WINDOWS) || defined(RT_OS_LINUX)
55# ifdef _MSC_VER
56# pragma warning(push)
57# pragma warning(disable: 4121) /* warning C4121: 'SDL_SysWMmsg' : alignment of a member was sensitive to packing*/
58# endif
59# include <SDL_syswm.h> /* for SDL_GetWMInfo() */
60# ifdef _MSC_VER
61# pragma warning(pop)
62# endif
63#endif
64
65#if defined(VBOX_WITH_XPCOM)
66NS_IMPL_THREADSAFE_ISUPPORTS1_CI(VBoxSDLFB, IFramebuffer)
67NS_DECL_CLASSINFO(VBoxSDLFB)
68NS_IMPL_THREADSAFE_ISUPPORTS2_CI(VBoxSDLFBOverlay, IFramebufferOverlay, IFramebuffer)
69NS_DECL_CLASSINFO(VBoxSDLFBOverlay)
70#endif
71
72static bool gfSdlInitialized = false; /**< if SDL was initialized */
73static SDL_Surface *gWMIcon = NULL; /**< the application icon */
74static RTNATIVETHREAD gSdlNativeThread = NIL_RTNATIVETHREAD; /**< the SDL thread */
75
76//
77// Constructor / destructor
78//
79
80VBoxSDLFB::VBoxSDLFB()
81{
82}
83
84HRESULT VBoxSDLFB::FinalConstruct()
85{
86 return 0;
87}
88
89void VBoxSDLFB::FinalRelease()
90{
91 return;
92}
93
94/**
95 * SDL framebuffer constructor. It is called from the main
96 * (i.e. SDL) thread. Therefore it is safe to use SDL calls
97 * here.
98 * @param fFullscreen flag whether we start in fullscreen mode
99 * @param fResizable flag whether the SDL window should be resizable
100 * @param fShowSDLConfig flag whether we print out SDL settings
101 * @param fKeepHostRes flag whether we switch the host screen resolution
102 * when switching to fullscreen or not
103 * @param iFixedWidth fixed SDL width (-1 means not set)
104 * @param iFixedHeight fixed SDL height (-1 means not set)
105 */
106HRESULT VBoxSDLFB::init(uint32_t uScreenId,
107 bool fFullscreen, bool fResizable, bool fShowSDLConfig,
108 bool fKeepHostRes, uint32_t u32FixedWidth,
109 uint32_t u32FixedHeight, uint32_t u32FixedBPP,
110 bool fUpdateImage)
111{
112 LogFlow(("VBoxSDLFB::VBoxSDLFB\n"));
113
114 mScreenId = uScreenId;
115 mfUpdateImage = fUpdateImage;
116 mpWindow = NULL;
117 mpTexture = NULL;
118 mpRenderer = NULL;
119 mSurfVRAM = NULL;
120 mfInitialized = false;
121 mfFullscreen = fFullscreen;
122 mfKeepHostRes = fKeepHostRes;
123 mTopOffset = 0;
124 mfResizable = fResizable;
125 mfShowSDLConfig = fShowSDLConfig;
126 mFixedSDLWidth = u32FixedWidth;
127 mFixedSDLHeight = u32FixedHeight;
128 mFixedSDLBPP = u32FixedBPP;
129 mCenterXOffset = 0;
130 mCenterYOffset = 0;
131 /* Start with standard screen dimensions. */
132 mGuestXRes = 640;
133 mGuestYRes = 480;
134 mPtrVRAM = NULL;
135 mBitsPerPixel = 0;
136 mBytesPerLine = 0;
137 mfSameSizeRequested = false;
138 mfUpdates = false;
139
140 int rc = RTCritSectInit(&mUpdateLock);
141 AssertMsg(rc == VINF_SUCCESS, ("Error from RTCritSectInit!\n"));
142
143 resizeGuest();
144 mfInitialized = true;
145#ifdef RT_OS_WINDOWS
146 HRESULT hr = CoCreateFreeThreadedMarshaler(this, m_pUnkMarshaler.asOutParam());
147 Log(("CoCreateFreeThreadedMarshaler hr %08X\n", hr)); NOREF(hr);
148#endif
149
150 rc = SDL_GetRendererInfo(mpRenderer, &mRenderInfo);
151 if (RT_SUCCESS(rc))
152 {
153 if (fShowSDLConfig)
154 RTPrintf("Render info:\n"
155 " Name: %s\n"
156 " Render flags: 0x%x\n"
157 " SDL video driver: %s\n",
158 mRenderInfo.name,
159 mRenderInfo.flags,
160 RTEnvGet("SDL_VIDEODRIVER"));
161 }
162
163 return rc;
164}
165
166VBoxSDLFB::~VBoxSDLFB()
167{
168 LogFlow(("VBoxSDLFB::~VBoxSDLFB\n"));
169 if (mSurfVRAM)
170 {
171 SDL_FreeSurface(mSurfVRAM);
172 mSurfVRAM = NULL;
173 }
174 RTCritSectDelete(&mUpdateLock);
175}
176
177/* static */
178bool VBoxSDLFB::init(bool fShowSDLConfig)
179{
180 LogFlow(("VBoxSDLFB::init\n"));
181
182 /* memorize the thread that inited us, that's the SDL thread */
183 gSdlNativeThread = RTThreadNativeSelf();
184
185#ifdef RT_OS_WINDOWS
186 /* default to DirectX if nothing else set */
187 if (!RTEnvExist("SDL_VIDEODRIVER"))
188 {
189 RTEnvSet("SDL_VIDEODRIVER", "directx");
190 }
191#endif
192#ifdef VBOXSDL_WITH_X11
193 /* On some X servers the mouse is stuck inside the bottom right corner.
194 * See http://wiki.clug.org.za/wiki/QEMU_mouse_not_working */
195 RTEnvSet("SDL_VIDEO_X11_DGAMOUSE", "0");
196#endif
197
198 int rc = SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE);
199 if (rc != 0)
200 {
201 RTPrintf("SDL Error: '%s'\n", SDL_GetError());
202 return false;
203 }
204 gfSdlInitialized = true;
205
206 RT_NOREF(fShowSDLConfig);
207 return true;
208}
209
210/**
211 * Terminate SDL
212 *
213 * @remarks must be called from the SDL thread!
214 */
215void VBoxSDLFB::uninit()
216{
217 if (gfSdlInitialized)
218 {
219 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
220 SDL_QuitSubSystem(SDL_INIT_VIDEO);
221 }
222}
223
224/**
225 * Returns the current framebuffer width in pixels.
226 *
227 * @returns COM status code
228 * @param width Address of result buffer.
229 */
230STDMETHODIMP VBoxSDLFB::COMGETTER(Width)(ULONG *width)
231{
232 LogFlow(("VBoxSDLFB::GetWidth\n"));
233 if (!width)
234 return E_INVALIDARG;
235 *width = mGuestXRes;
236 return S_OK;
237}
238
239/**
240 * Returns the current framebuffer height in pixels.
241 *
242 * @returns COM status code
243 * @param height Address of result buffer.
244 */
245STDMETHODIMP VBoxSDLFB::COMGETTER(Height)(ULONG *height)
246{
247 LogFlow(("VBoxSDLFB::GetHeight\n"));
248 if (!height)
249 return E_INVALIDARG;
250 *height = mGuestYRes;
251 return S_OK;
252}
253
254/**
255 * Return the current framebuffer color depth.
256 *
257 * @returns COM status code
258 * @param bitsPerPixel Address of result variable
259 */
260STDMETHODIMP VBoxSDLFB::COMGETTER(BitsPerPixel)(ULONG *bitsPerPixel)
261{
262 LogFlow(("VBoxSDLFB::GetBitsPerPixel\n"));
263 if (!bitsPerPixel)
264 return E_INVALIDARG;
265 /* get the information directly from the surface in use */
266 Assert(mSurfVRAM);
267 *bitsPerPixel = (ULONG)(mSurfVRAM ? mSurfVRAM->format->BitsPerPixel : 0);
268 return S_OK;
269}
270
271/**
272 * Return the current framebuffer line size in bytes.
273 *
274 * @returns COM status code.
275 * @param lineSize Address of result variable.
276 */
277STDMETHODIMP VBoxSDLFB::COMGETTER(BytesPerLine)(ULONG *bytesPerLine)
278{
279 LogFlow(("VBoxSDLFB::GetBytesPerLine\n"));
280 if (!bytesPerLine)
281 return E_INVALIDARG;
282 /* get the information directly from the surface */
283 Assert(mSurfVRAM);
284 *bytesPerLine = (ULONG)(mSurfVRAM ? mSurfVRAM->pitch : 0);
285 return S_OK;
286}
287
288STDMETHODIMP VBoxSDLFB::COMGETTER(PixelFormat) (BitmapFormat_T *pixelFormat)
289{
290 if (!pixelFormat)
291 return E_POINTER;
292 *pixelFormat = BitmapFormat_BGR;
293 return S_OK;
294}
295
296/**
297 * Returns by how many pixels the guest should shrink its
298 * video mode height values.
299 *
300 * @returns COM status code.
301 * @param heightReduction Address of result variable.
302 */
303STDMETHODIMP VBoxSDLFB::COMGETTER(HeightReduction)(ULONG *heightReduction)
304{
305 if (!heightReduction)
306 return E_POINTER;
307 *heightReduction = 0;
308 return S_OK;
309}
310
311/**
312 * Returns a pointer to an alpha-blended overlay used for displaying status
313 * icons above the framebuffer.
314 *
315 * @returns COM status code.
316 * @param aOverlay The overlay framebuffer.
317 */
318STDMETHODIMP VBoxSDLFB::COMGETTER(Overlay)(IFramebufferOverlay **aOverlay)
319{
320 if (!aOverlay)
321 return E_POINTER;
322 /* Not yet implemented */
323 *aOverlay = 0;
324 return S_OK;
325}
326
327/**
328 * Returns handle of window where framebuffer context is being drawn
329 *
330 * @returns COM status code.
331 * @param winId Handle of associated window.
332 */
333STDMETHODIMP VBoxSDLFB::COMGETTER(WinId)(LONG64 *winId)
334{
335 if (!winId)
336 return E_POINTER;
337#ifdef RT_OS_DARWIN
338 if (mWinId == 0) /* (In case it failed the first time.) */
339 mWinId = (intptr_t)VBoxSDLGetDarwinWindowId();
340#endif
341 *winId = mWinId;
342 return S_OK;
343}
344
345STDMETHODIMP VBoxSDLFB::COMGETTER(Capabilities)(ComSafeArrayOut(FramebufferCapabilities_T, aCapabilities))
346{
347 if (ComSafeArrayOutIsNull(aCapabilities))
348 return E_POINTER;
349
350 com::SafeArray<FramebufferCapabilities_T> caps;
351
352 if (mfUpdateImage)
353 {
354 caps.resize(2);
355 caps[0] = FramebufferCapabilities_UpdateImage;
356 caps[1] = FramebufferCapabilities_RenderCursor;
357 }
358 else
359 {
360 caps.resize(1);
361 caps[0] = FramebufferCapabilities_RenderCursor;
362 }
363
364 caps.detachTo(ComSafeArrayOutArg(aCapabilities));
365 return S_OK;
366}
367
368/**
369 * Notify framebuffer of an update.
370 *
371 * @returns COM status code
372 * @param x Update region upper left corner x value.
373 * @param y Update region upper left corner y value.
374 * @param w Update region width in pixels.
375 * @param h Update region height in pixels.
376 * @param finished Address of output flag whether the update
377 * could be fully processed in this call (which
378 * has to return immediately) or VBox should wait
379 * for a call to the update complete API before
380 * continuing with display updates.
381 */
382STDMETHODIMP VBoxSDLFB::NotifyUpdate(ULONG x, ULONG y,
383 ULONG w, ULONG h)
384{
385 /*
386 * The input values are in guest screen coordinates.
387 */
388 LogFlow(("VBoxSDLFB::NotifyUpdate: x = %d, y = %d, w = %d, h = %d\n",
389 x, y, w, h));
390
391#ifdef VBOXSDL_WITH_X11
392 /*
393 * SDL does not allow us to make this call from any other thread than
394 * the main SDL thread (which initialized the video mode). So we have
395 * to send an event to the main SDL thread and process it there. For
396 * sake of simplicity, we encode all information in the event parameters.
397 */
398 SDL_Event event;
399 event.type = SDL_USEREVENT;
400 event.user.code = mScreenId;
401 event.user.type = SDL_USER_EVENT_UPDATERECT;
402
403 SDL_Rect *pUpdateRect = (SDL_Rect *)RTMemAlloc(sizeof(SDL_Rect));
404 AssertPtrReturn(pUpdateRect, E_OUTOFMEMORY);
405 pUpdateRect->x = x;
406 pUpdateRect->y = y;
407 pUpdateRect->w = w;
408 pUpdateRect->h = h;
409 event.user.data1 = pUpdateRect;
410
411 event.user.data2 = NULL;
412 PushNotifyUpdateEvent(&event);
413#else /* !VBOXSDL_WITH_X11 */
414 update(x, y, w, h, true /* fGuestRelative */);
415#endif /* !VBOXSDL_WITH_X11 */
416
417 return S_OK;
418}
419
420STDMETHODIMP VBoxSDLFB::NotifyUpdateImage(ULONG aX,
421 ULONG aY,
422 ULONG aWidth,
423 ULONG aHeight,
424 ComSafeArrayIn(BYTE, aImage))
425{
426 LogFlow(("NotifyUpdateImage: %d,%d %dx%d\n", aX, aY, aWidth, aHeight));
427
428 com::SafeArray<BYTE> image(ComSafeArrayInArg(aImage));
429
430 /* Copy to mSurfVRAM. */
431 SDL_Rect srcRect;
432 SDL_Rect dstRect;
433 srcRect.x = 0;
434 srcRect.y = 0;
435 srcRect.w = (uint16_t)aWidth;
436 srcRect.h = (uint16_t)aHeight;
437 dstRect.x = (int16_t)aX;
438 dstRect.y = (int16_t)aY;
439 dstRect.w = (uint16_t)aWidth;
440 dstRect.h = (uint16_t)aHeight;
441
442 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
443 SDL_Surface *surfSrc = SDL_CreateRGBSurfaceFrom(image.raw(), aWidth, aHeight, 32, aWidth * 4,
444 Rmask, Gmask, Bmask, Amask);
445 if (surfSrc)
446 {
447 RTCritSectEnter(&mUpdateLock);
448 if (mfUpdates)
449 SDL_BlitSurface(surfSrc, &srcRect, mSurfVRAM, &dstRect);
450 RTCritSectLeave(&mUpdateLock);
451
452 SDL_FreeSurface(surfSrc);
453 }
454
455 return NotifyUpdate(aX, aY, aWidth, aHeight);
456}
457
458extern ComPtr<IDisplay> gpDisplay;
459
460STDMETHODIMP VBoxSDLFB::NotifyChange(ULONG aScreenId,
461 ULONG aXOrigin,
462 ULONG aYOrigin,
463 ULONG aWidth,
464 ULONG aHeight)
465{
466 LogRel(("NotifyChange: %d %d,%d %dx%d\n",
467 aScreenId, aXOrigin, aYOrigin, aWidth, aHeight));
468
469 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
470 if (!mfUpdateImage)
471 gpDisplay->QuerySourceBitmap(aScreenId, pSourceBitmap.asOutParam());
472
473 RTCritSectEnter(&mUpdateLock);
474
475 /* Disable screen updates. */
476 mfUpdates = false;
477
478 if (mfUpdateImage)
479 {
480 mGuestXRes = aWidth;
481 mGuestYRes = aHeight;
482 mPtrVRAM = NULL;
483 mBitsPerPixel = 0;
484 mBytesPerLine = 0;
485 }
486 else
487 {
488 /* Save the new bitmap. */
489 mpPendingSourceBitmap = pSourceBitmap;
490 }
491
492 RTCritSectLeave(&mUpdateLock);
493
494 SDL_Event event;
495 event.type = SDL_USEREVENT;
496 event.user.type = SDL_USER_EVENT_NOTIFYCHANGE;
497 event.user.code = mScreenId;
498
499 PushSDLEventForSure(&event);
500
501 RTThreadYield();
502
503 return S_OK;
504}
505
506/**
507 * Returns whether we like the given video mode.
508 *
509 * @returns COM status code
510 * @param width video mode width in pixels
511 * @param height video mode height in pixels
512 * @param bpp video mode bit depth in bits per pixel
513 * @param supported pointer to result variable
514 */
515STDMETHODIMP VBoxSDLFB::VideoModeSupported(ULONG width, ULONG height, ULONG bpp, BOOL *supported)
516{
517 RT_NOREF(bpp);
518
519 if (!supported)
520 return E_POINTER;
521
522 /* are constraints set? */
523 if ( ( (mMaxScreenWidth != ~(uint32_t)0)
524 && (width > mMaxScreenWidth))
525 || ( (mMaxScreenHeight != ~(uint32_t)0)
526 && (height > mMaxScreenHeight)))
527 {
528 /* nope, we don't want that (but still don't freak out if it is set) */
529#ifdef DEBUG
530 RTPrintf("VBoxSDL::VideoModeSupported: we refused mode %dx%dx%d\n", width, height, bpp);
531#endif
532 *supported = false;
533 }
534 else
535 {
536 /* anything will do */
537 *supported = true;
538 }
539 return S_OK;
540}
541
542STDMETHODIMP VBoxSDLFB::GetVisibleRegion(BYTE *aRectangles, ULONG aCount,
543 ULONG *aCountCopied)
544{
545 PRTRECT rects = (PRTRECT)aRectangles;
546
547 if (!rects)
548 return E_POINTER;
549
550 /// @todo
551
552 NOREF(aCount);
553 NOREF(aCountCopied);
554
555 return S_OK;
556}
557
558STDMETHODIMP VBoxSDLFB::SetVisibleRegion(BYTE *aRectangles, ULONG aCount)
559{
560 PRTRECT rects = (PRTRECT)aRectangles;
561
562 if (!rects)
563 return E_POINTER;
564
565 /// @todo
566
567 NOREF(aCount);
568
569 return S_OK;
570}
571
572STDMETHODIMP VBoxSDLFB::ProcessVHWACommand(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd)
573{
574 RT_NOREF(pCommand, enmCmd, fGuestCmd);
575 return E_NOTIMPL;
576}
577
578STDMETHODIMP VBoxSDLFB::Notify3DEvent(ULONG uType, ComSafeArrayIn(BYTE, aData))
579{
580 RT_NOREF(uType); ComSafeArrayNoRef(aData);
581 return E_NOTIMPL;
582}
583
584//
585// Internal public methods
586//
587
588/* This method runs on the main SDL thread. */
589void VBoxSDLFB::notifyChange(ULONG aScreenId)
590{
591 /* Disable screen updates. */
592 RTCritSectEnter(&mUpdateLock);
593
594 if (!mfUpdateImage && mpPendingSourceBitmap.isNull())
595 {
596 /* Do nothing. Change event already processed. */
597 RTCritSectLeave(&mUpdateLock);
598 return;
599 }
600
601 /* Release the current bitmap and keep the pending one. */
602 mpSourceBitmap = mpPendingSourceBitmap;
603 mpPendingSourceBitmap.setNull();
604
605 RTCritSectLeave(&mUpdateLock);
606
607 if (mpSourceBitmap.isNull())
608 {
609 mPtrVRAM = NULL;
610 mBitsPerPixel = 32;
611 mBytesPerLine = mGuestXRes * 4;
612 }
613 else
614 {
615 BYTE *pAddress = NULL;
616 ULONG ulWidth = 0;
617 ULONG ulHeight = 0;
618 ULONG ulBitsPerPixel = 0;
619 ULONG ulBytesPerLine = 0;
620 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
621
622 mpSourceBitmap->QueryBitmapInfo(&pAddress,
623 &ulWidth,
624 &ulHeight,
625 &ulBitsPerPixel,
626 &ulBytesPerLine,
627 &bitmapFormat);
628
629 if ( mGuestXRes == ulWidth
630 && mGuestYRes == ulHeight
631 && mBitsPerPixel == ulBitsPerPixel
632 && mBytesPerLine == ulBytesPerLine
633 && mPtrVRAM == pAddress
634 )
635 {
636 mfSameSizeRequested = true;
637 }
638 else
639 {
640 mfSameSizeRequested = false;
641 }
642
643 mGuestXRes = ulWidth;
644 mGuestYRes = ulHeight;
645 mPtrVRAM = pAddress;
646 mBitsPerPixel = ulBitsPerPixel;
647 mBytesPerLine = ulBytesPerLine;
648 }
649
650 resizeGuest();
651
652 gpDisplay->InvalidateAndUpdateScreen(aScreenId);
653}
654
655/**
656 * Method that does the actual resize of the guest framebuffer and
657 * then changes the SDL framebuffer setup.
658 */
659void VBoxSDLFB::resizeGuest()
660{
661 LogFlowFunc (("mGuestXRes: %d, mGuestYRes: %d\n", mGuestXRes, mGuestYRes));
662 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(),
663 ("Wrong thread! SDL is not threadsafe!\n"));
664
665 RTCritSectEnter(&mUpdateLock);
666
667 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
668
669 /* first free the current surface */
670 if (mSurfVRAM)
671 {
672 SDL_FreeSurface(mSurfVRAM);
673 mSurfVRAM = NULL;
674 }
675
676 if (mPtrVRAM)
677 {
678 /* Create a source surface from the source bitmap. */
679 mSurfVRAM = SDL_CreateRGBSurfaceFrom(mPtrVRAM, mGuestXRes, mGuestYRes, mBitsPerPixel,
680 mBytesPerLine, Rmask, Gmask, Bmask, Amask);
681 LogFlow(("VBoxSDL:: using the source bitmap\n"));
682 }
683 else
684 {
685 mSurfVRAM = SDL_CreateRGBSurface(SDL_SWSURFACE, mGuestXRes, mGuestYRes, 32,
686 Rmask, Gmask, Bmask, Amask);
687 LogFlow(("VBoxSDL:: using SDL_SWSURFACE\n"));
688 }
689 LogFlow(("VBoxSDL:: created VRAM surface %p\n", mSurfVRAM));
690
691 if (mfSameSizeRequested)
692 {
693 mfSameSizeRequested = false;
694 LogFlow(("VBoxSDL:: the same resolution requested, skipping the resize.\n"));
695 }
696 else
697 {
698 /* now adjust the SDL resolution */
699 resizeSDL();
700 }
701
702 /* Enable screen updates. */
703 mfUpdates = true;
704
705 RTCritSectLeave(&mUpdateLock);
706
707 repaint();
708}
709
710/**
711 * Sets SDL video mode. This is independent from guest video
712 * mode changes.
713 *
714 * @remarks Must be called from the SDL thread!
715 */
716void VBoxSDLFB::resizeSDL(void)
717{
718 LogFlow(("VBoxSDL:resizeSDL\n"));
719
720 const int cDisplays = SDL_GetNumVideoDisplays();
721 if (cDisplays > 0)
722 {
723 for (int d = 0; d < cDisplays; d++)
724 {
725 const int cDisplayModes = SDL_GetNumDisplayModes(d);
726 for (int m = 0; m < cDisplayModes; m++)
727 {
728 SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
729 if (SDL_GetDisplayMode(d, m, &mode) != 0)
730 {
731 RTPrintf("Display #%d, mode %d:\t\t%i bpp\t%i x %i",
732 SDL_BITSPERPIXEL(mode.format), mode.w, mode.h);
733 }
734
735 if (m == 0)
736 {
737 /*
738 * according to the SDL documentation, the API guarantees that
739 * the modes are sorted from larger to smaller, so we just
740 * take the first entry as the maximum.
741 */
742 mMaxScreenWidth = mode.w;
743 mMaxScreenHeight = mode.h;
744 }
745
746 /* Keep going. */
747 }
748 }
749 }
750 else
751 AssertFailed(); /** @todo */
752
753 uint32_t newWidth;
754 uint32_t newHeight;
755
756 /* reset the centering offsets */
757 mCenterXOffset = 0;
758 mCenterYOffset = 0;
759
760 /* we either have a fixed SDL resolution or we take the guest's */
761 if (mFixedSDLWidth != ~(uint32_t)0)
762 {
763 newWidth = mFixedSDLWidth;
764 newHeight = mFixedSDLHeight;
765 }
766 else
767 {
768 newWidth = RT_MIN(mGuestXRes, mMaxScreenWidth);
769 newHeight = RT_MIN(mGuestYRes, mMaxScreenHeight);
770 }
771
772 /* we don't have any extra space by default */
773 mTopOffset = 0;
774
775 int sdlWindowFlags = SDL_WINDOW_SHOWN;
776 if (mfResizable)
777 sdlWindowFlags |= SDL_WINDOW_RESIZABLE;
778 if (!mpWindow)
779 {
780 SDL_DisplayMode desktop_mode;
781 int x = 40 + mScreenId * 20;
782 int y = 40 + mScreenId * 15;
783
784 SDL_GetDesktopDisplayMode(mScreenId, &desktop_mode);
785 /* create new window */
786
787 char szTitle[64];
788 RTStrPrintf(szTitle, sizeof(szTitle), "SDL window %d", mScreenId);
789 mpWindow = SDL_CreateWindow(szTitle, x, y,
790 newWidth, newHeight, sdlWindowFlags);
791 mpRenderer = SDL_CreateRenderer(mpWindow, -1, 0 /* SDL_RendererFlags */);
792 if (mpRenderer)
793 {
794 SDL_GetRendererInfo(mpRenderer, &mRenderInfo);
795
796 mpTexture = SDL_CreateTexture(mpRenderer, desktop_mode.format,
797 SDL_TEXTUREACCESS_STREAMING, newWidth, newHeight);
798 if (!mpTexture)
799 AssertReleaseFailed();
800 }
801 else
802 AssertReleaseFailed();
803
804 if (12320 == g_cbIco64x01)
805 {
806 gWMIcon = SDL_CreateRGBSurface(0 /* Flags, must be 0 */, 64, 64, 24, 0xff, 0xff00, 0xff0000, 0);
807 /** @todo make it as simple as possible. No PNM interpreter here... */
808 if (gWMIcon)
809 {
810 memcpy(gWMIcon->pixels, g_abIco64x01+32, g_cbIco64x01-32);
811 SDL_SetWindowIcon(mpWindow, gWMIcon);
812 }
813 }
814 }
815 else
816 {
817 int w, h;
818 uint32_t format;
819 int access;
820
821 /* resize current window */
822 SDL_GetWindowSize(mpWindow, &w, &h);
823
824 if (w != (int)newWidth || h != (int)newHeight)
825 SDL_SetWindowSize(mpWindow, newWidth, newHeight);
826
827 SDL_QueryTexture(mpTexture, &format, &access, &w, &h);
828 SDL_DestroyTexture(mpTexture);
829 mpTexture = SDL_CreateTexture(mpRenderer, format, access, newWidth, newHeight);
830 if (!mpTexture)
831 AssertReleaseFailed();
832 }
833}
834
835/**
836 * Update specified framebuffer area. The coordinates can either be
837 * relative to the guest framebuffer or relative to the screen.
838 *
839 * @remarks Must be called from the SDL thread on Linux!
840 * @param x left column
841 * @param y top row
842 * @param w width in pixels
843 * @param h height in pixels
844 * @param fGuestRelative flag whether the above values are guest relative or screen relative;
845 */
846void VBoxSDLFB::update(int x, int y, int w, int h, bool fGuestRelative)
847{
848#ifdef VBOXSDL_WITH_X11
849 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
850#endif
851 RTCritSectEnter(&mUpdateLock);
852 Log3Func(("mfUpdates=%RTbool %d,%d %dx%d\n", mfUpdates, x, y, w, h));
853 if (!mfUpdates)
854 {
855 RTCritSectLeave(&mUpdateLock);
856 return;
857 }
858
859 Assert(mSurfVRAM);
860 if (!mSurfVRAM)
861 {
862 RTCritSectLeave(&mUpdateLock);
863 return;
864 }
865
866 /* this is how many pixels we have to cut off from the height for this specific blit */
867 int const yCutoffGuest = 0;
868
869 /**
870 * If we get a SDL window relative update, we
871 * just perform a full screen update to keep things simple.
872 *
873 * @todo improve
874 */
875 if (!fGuestRelative)
876 {
877 x = 0;
878 w = mGuestXRes;
879 y = 0;
880 h = mGuestYRes;
881 }
882
883 SDL_Rect srcRect;
884 srcRect.x = x;
885 srcRect.y = y + yCutoffGuest;
886 srcRect.w = w;
887 srcRect.h = RT_MAX(0, h - yCutoffGuest);
888
889 /*
890 * Destination rectangle is just offset by the label height.
891 * There are two cases though: label height is added to the
892 * guest resolution (mTopOffset == mLabelHeight; yCutoffGuest == 0)
893 * or the label cuts off a portion of the guest screen (mTopOffset == 0;
894 * yCutoffGuest >= 0)
895 */
896 SDL_Rect dstRect;
897 dstRect.x = x + mCenterXOffset;
898 dstRect.y = y + yCutoffGuest + mTopOffset + mCenterYOffset;
899 dstRect.w = w;
900 dstRect.h = RT_MAX(0, h - yCutoffGuest);
901
902 /* Calculate the offset within the VRAM to update the streaming texture directly. */
903 uint8_t const *pbOff = (uint8_t *)mSurfVRAM->pixels
904 + (srcRect.y * mBytesPerLine) + (srcRect.x * (mBitsPerPixel / 8));
905 SDL_UpdateTexture(mpTexture, &srcRect, pbOff, mSurfVRAM->pitch);
906 SDL_RenderCopy(mpRenderer, mpTexture, NULL, NULL);
907
908 RTCritSectLeave(&mUpdateLock);
909
910 SDL_RenderPresent(mpRenderer);
911}
912
913/**
914 * Repaint the whole framebuffer
915 *
916 * @remarks Must be called from the SDL thread!
917 */
918void VBoxSDLFB::repaint()
919{
920 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
921 LogFlow(("VBoxSDLFB::repaint\n"));
922 int w, h;
923 uint32_t format;
924 int access;
925 SDL_QueryTexture(mpTexture, &format, &access, &w, &h);
926 update(0, 0, w, h, false /* fGuestRelative */);
927}
928
929/**
930 * Toggle fullscreen mode
931 *
932 * @remarks Must be called from the SDL thread!
933 */
934void VBoxSDLFB::setFullscreen(bool fFullscreen)
935{
936 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
937 LogFlow(("VBoxSDLFB::SetFullscreen: fullscreen: %d\n", fFullscreen));
938 mfFullscreen = fFullscreen;
939 /* only change the SDL resolution, do not touch the guest framebuffer */
940 resizeSDL();
941 repaint();
942}
943
944/**
945 * Return the geometry of the host. This isn't very well tested but it seems
946 * to work at least on Linux hosts.
947 */
948void VBoxSDLFB::getFullscreenGeometry(uint32_t *width, uint32_t *height)
949{
950 SDL_DisplayMode dm;
951 int rc = SDL_GetDesktopDisplayMode(0, &dm); /** @BUGBUG Handle multi monitor setups! */
952 if (rc == 0)
953 {
954 *width = dm.w;
955 *height = dm.w;
956 }
957}
958
959int VBoxSDLFB::setWindowTitle(const char *pcszTitle)
960{
961 SDL_SetWindowTitle(mpWindow, pcszTitle);
962
963 return VINF_SUCCESS;
964}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use