VirtualBox

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

Last change on this file since 98429 was 98371, checked in by vboxsync, 23 months ago

FE/SDL: bugref:9449. Some cleanup for MacOS build. Still getting some compilation errors in objective-c files.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette