VirtualBox

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

Last change on this file was 99494, checked in by vboxsync, 13 months ago

FE/VBoxSDL: Make it work with macOS, bugref:9449

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.6 KB
Line 
1/* $Id: Framebuffer.cpp 99494 2023-04-20 19:52:23Z 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
338 *winId = mWinId;
339 return S_OK;
340}
341
342STDMETHODIMP VBoxSDLFB::COMGETTER(Capabilities)(ComSafeArrayOut(FramebufferCapabilities_T, aCapabilities))
343{
344 if (ComSafeArrayOutIsNull(aCapabilities))
345 return E_POINTER;
346
347 com::SafeArray<FramebufferCapabilities_T> caps;
348
349 if (mfUpdateImage)
350 {
351 caps.resize(2);
352 caps[0] = FramebufferCapabilities_UpdateImage;
353 caps[1] = FramebufferCapabilities_RenderCursor;
354 }
355 else
356 {
357 caps.resize(1);
358 caps[0] = FramebufferCapabilities_RenderCursor;
359 }
360
361 caps.detachTo(ComSafeArrayOutArg(aCapabilities));
362 return S_OK;
363}
364
365/**
366 * Notify framebuffer of an update.
367 *
368 * @returns COM status code
369 * @param x Update region upper left corner x value.
370 * @param y Update region upper left corner y value.
371 * @param w Update region width in pixels.
372 * @param h Update region height in pixels.
373 * @param finished Address of output flag whether the update
374 * could be fully processed in this call (which
375 * has to return immediately) or VBox should wait
376 * for a call to the update complete API before
377 * continuing with display updates.
378 */
379STDMETHODIMP VBoxSDLFB::NotifyUpdate(ULONG x, ULONG y,
380 ULONG w, ULONG h)
381{
382 /*
383 * The input values are in guest screen coordinates.
384 */
385 LogFlow(("VBoxSDLFB::NotifyUpdate: x = %d, y = %d, w = %d, h = %d\n",
386 x, y, w, h));
387
388#if defined(VBOXSDL_WITH_X11) || defined(RT_OS_DARWIN)
389 /*
390 * SDL does not allow us to make this call from any other thread than
391 * the main SDL thread (which initialized the video mode). So we have
392 * to send an event to the main SDL thread and process it there. For
393 * sake of simplicity, we encode all information in the event parameters.
394 */
395 SDL_Event event;
396 event.type = SDL_USEREVENT;
397 event.user.code = mScreenId;
398 event.user.type = SDL_USER_EVENT_UPDATERECT;
399
400 SDL_Rect *pUpdateRect = (SDL_Rect *)RTMemAlloc(sizeof(SDL_Rect));
401 AssertPtrReturn(pUpdateRect, E_OUTOFMEMORY);
402 pUpdateRect->x = x;
403 pUpdateRect->y = y;
404 pUpdateRect->w = w;
405 pUpdateRect->h = h;
406 event.user.data1 = pUpdateRect;
407
408 event.user.data2 = NULL;
409 PushNotifyUpdateEvent(&event);
410#else /* !VBOXSDL_WITH_X11 */
411 update(x, y, w, h, true /* fGuestRelative */);
412#endif /* !VBOXSDL_WITH_X11 */
413
414 return S_OK;
415}
416
417STDMETHODIMP VBoxSDLFB::NotifyUpdateImage(ULONG aX,
418 ULONG aY,
419 ULONG aWidth,
420 ULONG aHeight,
421 ComSafeArrayIn(BYTE, aImage))
422{
423 LogFlow(("NotifyUpdateImage: %d,%d %dx%d\n", aX, aY, aWidth, aHeight));
424
425 com::SafeArray<BYTE> image(ComSafeArrayInArg(aImage));
426
427 /* Copy to mSurfVRAM. */
428 SDL_Rect srcRect;
429 SDL_Rect dstRect;
430 srcRect.x = 0;
431 srcRect.y = 0;
432 srcRect.w = (uint16_t)aWidth;
433 srcRect.h = (uint16_t)aHeight;
434 dstRect.x = (int16_t)aX;
435 dstRect.y = (int16_t)aY;
436 dstRect.w = (uint16_t)aWidth;
437 dstRect.h = (uint16_t)aHeight;
438
439 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
440 SDL_Surface *surfSrc = SDL_CreateRGBSurfaceFrom(image.raw(), aWidth, aHeight, 32, aWidth * 4,
441 Rmask, Gmask, Bmask, Amask);
442 if (surfSrc)
443 {
444 RTCritSectEnter(&mUpdateLock);
445 if (mfUpdates)
446 SDL_BlitSurface(surfSrc, &srcRect, mSurfVRAM, &dstRect);
447 RTCritSectLeave(&mUpdateLock);
448
449 SDL_FreeSurface(surfSrc);
450 }
451
452 return NotifyUpdate(aX, aY, aWidth, aHeight);
453}
454
455extern ComPtr<IDisplay> gpDisplay;
456
457STDMETHODIMP VBoxSDLFB::NotifyChange(ULONG aScreenId,
458 ULONG aXOrigin,
459 ULONG aYOrigin,
460 ULONG aWidth,
461 ULONG aHeight)
462{
463 LogRel(("NotifyChange: %d %d,%d %dx%d\n",
464 aScreenId, aXOrigin, aYOrigin, aWidth, aHeight));
465
466 ComPtr<IDisplaySourceBitmap> pSourceBitmap;
467 if (!mfUpdateImage)
468 gpDisplay->QuerySourceBitmap(aScreenId, pSourceBitmap.asOutParam());
469
470 RTCritSectEnter(&mUpdateLock);
471
472 /* Disable screen updates. */
473 mfUpdates = false;
474
475 if (mfUpdateImage)
476 {
477 mGuestXRes = aWidth;
478 mGuestYRes = aHeight;
479 mPtrVRAM = NULL;
480 mBitsPerPixel = 0;
481 mBytesPerLine = 0;
482 }
483 else
484 {
485 /* Save the new bitmap. */
486 mpPendingSourceBitmap = pSourceBitmap;
487 }
488
489 RTCritSectLeave(&mUpdateLock);
490
491 SDL_Event event;
492 event.type = SDL_USEREVENT;
493 event.user.type = SDL_USER_EVENT_NOTIFYCHANGE;
494 event.user.code = mScreenId;
495
496 PushSDLEventForSure(&event);
497
498 RTThreadYield();
499
500 return S_OK;
501}
502
503/**
504 * Returns whether we like the given video mode.
505 *
506 * @returns COM status code
507 * @param width video mode width in pixels
508 * @param height video mode height in pixels
509 * @param bpp video mode bit depth in bits per pixel
510 * @param supported pointer to result variable
511 */
512STDMETHODIMP VBoxSDLFB::VideoModeSupported(ULONG width, ULONG height, ULONG bpp, BOOL *supported)
513{
514 RT_NOREF(bpp);
515
516 if (!supported)
517 return E_POINTER;
518
519 /* are constraints set? */
520 if ( ( (mMaxScreenWidth != ~(uint32_t)0)
521 && (width > mMaxScreenWidth))
522 || ( (mMaxScreenHeight != ~(uint32_t)0)
523 && (height > mMaxScreenHeight)))
524 {
525 /* nope, we don't want that (but still don't freak out if it is set) */
526#ifdef DEBUG
527 RTPrintf("VBoxSDL::VideoModeSupported: we refused mode %dx%dx%d\n", width, height, bpp);
528#endif
529 *supported = false;
530 }
531 else
532 {
533 /* anything will do */
534 *supported = true;
535 }
536 return S_OK;
537}
538
539STDMETHODIMP VBoxSDLFB::GetVisibleRegion(BYTE *aRectangles, ULONG aCount,
540 ULONG *aCountCopied)
541{
542 PRTRECT rects = (PRTRECT)aRectangles;
543
544 if (!rects)
545 return E_POINTER;
546
547 /// @todo
548
549 NOREF(aCount);
550 NOREF(aCountCopied);
551
552 return S_OK;
553}
554
555STDMETHODIMP VBoxSDLFB::SetVisibleRegion(BYTE *aRectangles, ULONG aCount)
556{
557 PRTRECT rects = (PRTRECT)aRectangles;
558
559 if (!rects)
560 return E_POINTER;
561
562 /// @todo
563
564 NOREF(aCount);
565
566 return S_OK;
567}
568
569STDMETHODIMP VBoxSDLFB::ProcessVHWACommand(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd)
570{
571 RT_NOREF(pCommand, enmCmd, fGuestCmd);
572 return E_NOTIMPL;
573}
574
575STDMETHODIMP VBoxSDLFB::Notify3DEvent(ULONG uType, ComSafeArrayIn(BYTE, aData))
576{
577 RT_NOREF(uType); ComSafeArrayNoRef(aData);
578 return E_NOTIMPL;
579}
580
581//
582// Internal public methods
583//
584
585/* This method runs on the main SDL thread. */
586void VBoxSDLFB::notifyChange(ULONG aScreenId)
587{
588 /* Disable screen updates. */
589 RTCritSectEnter(&mUpdateLock);
590
591 if (!mfUpdateImage && mpPendingSourceBitmap.isNull())
592 {
593 /* Do nothing. Change event already processed. */
594 RTCritSectLeave(&mUpdateLock);
595 return;
596 }
597
598 /* Release the current bitmap and keep the pending one. */
599 mpSourceBitmap = mpPendingSourceBitmap;
600 mpPendingSourceBitmap.setNull();
601
602 RTCritSectLeave(&mUpdateLock);
603
604 if (mpSourceBitmap.isNull())
605 {
606 mPtrVRAM = NULL;
607 mBitsPerPixel = 32;
608 mBytesPerLine = mGuestXRes * 4;
609 }
610 else
611 {
612 BYTE *pAddress = NULL;
613 ULONG ulWidth = 0;
614 ULONG ulHeight = 0;
615 ULONG ulBitsPerPixel = 0;
616 ULONG ulBytesPerLine = 0;
617 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;
618
619 mpSourceBitmap->QueryBitmapInfo(&pAddress,
620 &ulWidth,
621 &ulHeight,
622 &ulBitsPerPixel,
623 &ulBytesPerLine,
624 &bitmapFormat);
625
626 if ( mGuestXRes == ulWidth
627 && mGuestYRes == ulHeight
628 && mBitsPerPixel == ulBitsPerPixel
629 && mBytesPerLine == ulBytesPerLine
630 && mPtrVRAM == pAddress
631 )
632 {
633 mfSameSizeRequested = true;
634 }
635 else
636 {
637 mfSameSizeRequested = false;
638 }
639
640 mGuestXRes = ulWidth;
641 mGuestYRes = ulHeight;
642 mPtrVRAM = pAddress;
643 mBitsPerPixel = ulBitsPerPixel;
644 mBytesPerLine = ulBytesPerLine;
645 }
646
647 resizeGuest();
648
649 gpDisplay->InvalidateAndUpdateScreen(aScreenId);
650}
651
652/**
653 * Method that does the actual resize of the guest framebuffer and
654 * then changes the SDL framebuffer setup.
655 */
656void VBoxSDLFB::resizeGuest()
657{
658 LogFlowFunc (("mGuestXRes: %d, mGuestYRes: %d\n", mGuestXRes, mGuestYRes));
659 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(),
660 ("Wrong thread! SDL is not threadsafe!\n"));
661
662 RTCritSectEnter(&mUpdateLock);
663
664 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
665
666 /* first free the current surface */
667 if (mSurfVRAM)
668 {
669 SDL_FreeSurface(mSurfVRAM);
670 mSurfVRAM = NULL;
671 }
672
673 if (mPtrVRAM)
674 {
675 /* Create a source surface from the source bitmap. */
676 mSurfVRAM = SDL_CreateRGBSurfaceFrom(mPtrVRAM, mGuestXRes, mGuestYRes, mBitsPerPixel,
677 mBytesPerLine, Rmask, Gmask, Bmask, Amask);
678 LogFlow(("VBoxSDL:: using the source bitmap\n"));
679 }
680 else
681 {
682 mSurfVRAM = SDL_CreateRGBSurface(SDL_SWSURFACE, mGuestXRes, mGuestYRes, 32,
683 Rmask, Gmask, Bmask, Amask);
684 LogFlow(("VBoxSDL:: using SDL_SWSURFACE\n"));
685 }
686 LogFlow(("VBoxSDL:: created VRAM surface %p\n", mSurfVRAM));
687
688 if (mfSameSizeRequested)
689 {
690 mfSameSizeRequested = false;
691 LogFlow(("VBoxSDL:: the same resolution requested, skipping the resize.\n"));
692 }
693 else
694 {
695 /* now adjust the SDL resolution */
696 resizeSDL();
697 }
698
699 /* Enable screen updates. */
700 mfUpdates = true;
701
702 RTCritSectLeave(&mUpdateLock);
703
704 repaint();
705}
706
707/**
708 * Sets SDL video mode. This is independent from guest video
709 * mode changes.
710 *
711 * @remarks Must be called from the SDL thread!
712 */
713void VBoxSDLFB::resizeSDL(void)
714{
715 LogFlow(("VBoxSDL:resizeSDL\n"));
716
717 const int cDisplays = SDL_GetNumVideoDisplays();
718 if (cDisplays > 0)
719 {
720 for (int d = 0; d < cDisplays; d++)
721 {
722 const int cDisplayModes = SDL_GetNumDisplayModes(d);
723 for (int m = 0; m < cDisplayModes; m++)
724 {
725 SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
726 if (SDL_GetDisplayMode(d, m, &mode) != 0)
727 {
728 RTPrintf("Display #%d, mode %d:\t\t%i bpp\t%i x %i",
729 SDL_BITSPERPIXEL(mode.format), mode.w, mode.h);
730 }
731
732 if (m == 0)
733 {
734 /*
735 * according to the SDL documentation, the API guarantees that
736 * the modes are sorted from larger to smaller, so we just
737 * take the first entry as the maximum.
738 */
739 mMaxScreenWidth = mode.w;
740 mMaxScreenHeight = mode.h;
741 }
742
743 /* Keep going. */
744 }
745 }
746 }
747 else
748 AssertFailed(); /** @todo */
749
750 uint32_t newWidth;
751 uint32_t newHeight;
752
753 /* reset the centering offsets */
754 mCenterXOffset = 0;
755 mCenterYOffset = 0;
756
757 /* we either have a fixed SDL resolution or we take the guest's */
758 if (mFixedSDLWidth != ~(uint32_t)0)
759 {
760 newWidth = mFixedSDLWidth;
761 newHeight = mFixedSDLHeight;
762 }
763 else
764 {
765 newWidth = RT_MIN(mGuestXRes, mMaxScreenWidth);
766 newHeight = RT_MIN(mGuestYRes, mMaxScreenHeight);
767 }
768
769 /* we don't have any extra space by default */
770 mTopOffset = 0;
771
772 int sdlWindowFlags = SDL_WINDOW_SHOWN;
773 if (mfResizable)
774 sdlWindowFlags |= SDL_WINDOW_RESIZABLE;
775 if (!mpWindow)
776 {
777 SDL_DisplayMode desktop_mode;
778 int x = 40 + mScreenId * 20;
779 int y = 40 + mScreenId * 15;
780
781 SDL_GetDesktopDisplayMode(mScreenId, &desktop_mode);
782 /* create new window */
783
784 char szTitle[64];
785 RTStrPrintf(szTitle, sizeof(szTitle), "SDL window %d", mScreenId);
786 mpWindow = SDL_CreateWindow(szTitle, x, y,
787 newWidth, newHeight, sdlWindowFlags);
788 mpRenderer = SDL_CreateRenderer(mpWindow, -1, 0 /* SDL_RendererFlags */);
789 if (mpRenderer)
790 {
791 SDL_GetRendererInfo(mpRenderer, &mRenderInfo);
792
793 mpTexture = SDL_CreateTexture(mpRenderer, desktop_mode.format,
794 SDL_TEXTUREACCESS_STREAMING, newWidth, newHeight);
795 if (!mpTexture)
796 AssertReleaseFailed();
797 }
798 else
799 AssertReleaseFailed();
800
801 if (12320 == g_cbIco64x01)
802 {
803 gWMIcon = SDL_CreateRGBSurface(0 /* Flags, must be 0 */, 64, 64, 24, 0xff, 0xff00, 0xff0000, 0);
804 /** @todo make it as simple as possible. No PNM interpreter here... */
805 if (gWMIcon)
806 {
807 memcpy(gWMIcon->pixels, g_abIco64x01+32, g_cbIco64x01-32);
808 SDL_SetWindowIcon(mpWindow, gWMIcon);
809 }
810 }
811 }
812 else
813 {
814 int w, h;
815 uint32_t format;
816 int access;
817
818 /* resize current window */
819 SDL_GetWindowSize(mpWindow, &w, &h);
820
821 if (w != (int)newWidth || h != (int)newHeight)
822 SDL_SetWindowSize(mpWindow, newWidth, newHeight);
823
824 SDL_QueryTexture(mpTexture, &format, &access, &w, &h);
825 SDL_DestroyTexture(mpTexture);
826 mpTexture = SDL_CreateTexture(mpRenderer, format, access, newWidth, newHeight);
827 if (!mpTexture)
828 AssertReleaseFailed();
829 }
830}
831
832/**
833 * Update specified framebuffer area. The coordinates can either be
834 * relative to the guest framebuffer or relative to the screen.
835 *
836 * @remarks Must be called from the SDL thread on Linux!
837 * @param x left column
838 * @param y top row
839 * @param w width in pixels
840 * @param h height in pixels
841 * @param fGuestRelative flag whether the above values are guest relative or screen relative;
842 */
843void VBoxSDLFB::update(int x, int y, int w, int h, bool fGuestRelative)
844{
845#if defined(VBOXSDL_WITH_X11) || defined(RT_OS_DARWIN)
846 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
847#endif
848 RTCritSectEnter(&mUpdateLock);
849 Log3Func(("mfUpdates=%RTbool %d,%d %dx%d\n", mfUpdates, x, y, w, h));
850 if (!mfUpdates)
851 {
852 RTCritSectLeave(&mUpdateLock);
853 return;
854 }
855
856 Assert(mSurfVRAM);
857 if (!mSurfVRAM)
858 {
859 RTCritSectLeave(&mUpdateLock);
860 return;
861 }
862
863 /* this is how many pixels we have to cut off from the height for this specific blit */
864 int const yCutoffGuest = 0;
865
866 /**
867 * If we get a SDL window relative update, we
868 * just perform a full screen update to keep things simple.
869 *
870 * @todo improve
871 */
872 if (!fGuestRelative)
873 {
874 x = 0;
875 w = mGuestXRes;
876 y = 0;
877 h = mGuestYRes;
878 }
879
880 SDL_Rect srcRect;
881 srcRect.x = x;
882 srcRect.y = y + yCutoffGuest;
883 srcRect.w = w;
884 srcRect.h = RT_MAX(0, h - yCutoffGuest);
885
886 /*
887 * Destination rectangle is just offset by the label height.
888 * There are two cases though: label height is added to the
889 * guest resolution (mTopOffset == mLabelHeight; yCutoffGuest == 0)
890 * or the label cuts off a portion of the guest screen (mTopOffset == 0;
891 * yCutoffGuest >= 0)
892 */
893 SDL_Rect dstRect;
894 dstRect.x = x + mCenterXOffset;
895 dstRect.y = y + yCutoffGuest + mTopOffset + mCenterYOffset;
896 dstRect.w = w;
897 dstRect.h = RT_MAX(0, h - yCutoffGuest);
898
899 /* Calculate the offset within the VRAM to update the streaming texture directly. */
900 uint8_t const *pbOff = (uint8_t *)mSurfVRAM->pixels
901 + (srcRect.y * mBytesPerLine) + (srcRect.x * (mBitsPerPixel / 8));
902 SDL_UpdateTexture(mpTexture, &srcRect, pbOff, mSurfVRAM->pitch);
903 SDL_RenderCopy(mpRenderer, mpTexture, NULL, NULL);
904
905 RTCritSectLeave(&mUpdateLock);
906
907 SDL_RenderPresent(mpRenderer);
908}
909
910/**
911 * Repaint the whole framebuffer
912 *
913 * @remarks Must be called from the SDL thread!
914 */
915void VBoxSDLFB::repaint()
916{
917 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
918 LogFlow(("VBoxSDLFB::repaint\n"));
919 int w, h;
920 uint32_t format;
921 int access;
922 SDL_QueryTexture(mpTexture, &format, &access, &w, &h);
923 update(0, 0, w, h, false /* fGuestRelative */);
924}
925
926/**
927 * Toggle fullscreen mode
928 *
929 * @remarks Must be called from the SDL thread!
930 */
931void VBoxSDLFB::setFullscreen(bool fFullscreen)
932{
933 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
934 LogFlow(("VBoxSDLFB::SetFullscreen: fullscreen: %d\n", fFullscreen));
935 mfFullscreen = fFullscreen;
936 /* only change the SDL resolution, do not touch the guest framebuffer */
937 resizeSDL();
938 repaint();
939}
940
941/**
942 * Return the geometry of the host. This isn't very well tested but it seems
943 * to work at least on Linux hosts.
944 */
945void VBoxSDLFB::getFullscreenGeometry(uint32_t *width, uint32_t *height)
946{
947 SDL_DisplayMode dm;
948 int rc = SDL_GetDesktopDisplayMode(0, &dm); /** @BUGBUG Handle multi monitor setups! */
949 if (rc == 0)
950 {
951 *width = dm.w;
952 *height = dm.w;
953 }
954}
955
956int VBoxSDLFB::setWindowTitle(const char *pcszTitle)
957{
958 SDL_SetWindowTitle(mpWindow, pcszTitle);
959
960 return VINF_SUCCESS;
961}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use