VirtualBox

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

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

FE/VBoxBFE: Some very simple SDL based display output and add more devices, bugref:10397

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.4 KB
Line 
1/* $Id: Framebuffer.cpp 99546 2023-04-27 12:33:12Z 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#define LOG_GROUP LOG_GROUP_GUI
29
30#include <iprt/asm.h>
31#include <iprt/stream.h>
32#include <iprt/env.h>
33#include <iprt/errcore.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36#include <iprt/semaphore.h>
37#include <VBox/log.h>
38
39#include "Framebuffer.h"
40#include "Display.h"
41#include "Ico64x01.h"
42
43#include <SDL.h>
44
45static bool gfSdlInitialized = false; /**< if SDL was initialized */
46static SDL_Surface *gWMIcon = NULL; /**< the application icon */
47static RTNATIVETHREAD gSdlNativeThread = NIL_RTNATIVETHREAD; /**< the SDL thread */
48
49DECL_HIDDEN_DATA(RTSEMEVENT) g_EventSemSDLEvents;
50DECL_HIDDEN_DATA(volatile int32_t) g_cNotifyUpdateEventsPending;
51
52/**
53 * Ensure that an SDL event is really enqueued. Try multiple times if necessary.
54 */
55int PushSDLEventForSure(SDL_Event *event)
56{
57 int ntries = 10;
58 for (; ntries > 0; ntries--)
59 {
60 int rc = SDL_PushEvent(event);
61 RTSemEventSignal(g_EventSemSDLEvents);
62 if (rc == 1)
63 return 0;
64 Log(("PushSDLEventForSure: waiting for 2ms (rc = %d)\n", rc));
65 RTThreadSleep(2);
66 }
67 LogRel(("WARNING: Failed to enqueue SDL event %d.%d!\n",
68 event->type, event->type == SDL_USEREVENT ? event->user.type : 0));
69 return -1;
70}
71
72#if defined(VBOXSDL_WITH_X11) || defined(RT_OS_DARWIN)
73/**
74 * Special SDL_PushEvent function for NotifyUpdate events. These events may occur in bursts
75 * so make sure they don't flood the SDL event queue.
76 */
77void PushNotifyUpdateEvent(SDL_Event *event)
78{
79 int rc = SDL_PushEvent(event);
80 bool fSuccess = (rc == 1);
81
82 RTSemEventSignal(g_EventSemSDLEvents);
83 AssertMsg(fSuccess, ("SDL_PushEvent returned SDL error\n"));
84 /* A global counter is faster than SDL_PeepEvents() */
85 if (fSuccess)
86 ASMAtomicIncS32(&g_cNotifyUpdateEventsPending);
87 /* In order to not flood the SDL event queue, yield the CPU or (if there are already many
88 * events queued) even sleep */
89 if (g_cNotifyUpdateEventsPending > 96)
90 {
91 /* Too many NotifyUpdate events, sleep for a small amount to give the main thread time
92 * to handle these events. The SDL queue can hold up to 128 events. */
93 Log(("PushNotifyUpdateEvent: Sleep 1ms\n"));
94 RTThreadSleep(1);
95 }
96 else
97 RTThreadYield();
98}
99#endif /* VBOXSDL_WITH_X11 */
100
101
102//
103// Constructor / destructor
104//
105
106Framebuffer::Framebuffer(Display *pDisplay, 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(("Framebuffer::Framebuffer\n"));
113
114 m_pDisplay = pDisplay;
115 mScreenId = uScreenId;
116 mfUpdateImage = fUpdateImage;
117 mpWindow = NULL;
118 mpTexture = NULL;
119 mpRenderer = NULL;
120 mSurfVRAM = NULL;
121 mfInitialized = false;
122 mfFullscreen = fFullscreen;
123 mfKeepHostRes = fKeepHostRes;
124 mTopOffset = 0;
125 mfResizable = fResizable;
126 mfShowSDLConfig = fShowSDLConfig;
127 mFixedSDLWidth = u32FixedWidth;
128 mFixedSDLHeight = u32FixedHeight;
129 mFixedSDLBPP = u32FixedBPP;
130 mCenterXOffset = 0;
131 mCenterYOffset = 0;
132 /* Start with standard screen dimensions. */
133 mGuestXRes = 640;
134 mGuestYRes = 480;
135 mPtrVRAM = NULL;
136 mBitsPerPixel = 0;
137 mBytesPerLine = 0;
138 mfSameSizeRequested = false;
139 mfUpdates = false;
140
141 int rc = RTCritSectInit(&mUpdateLock);
142 AssertMsg(rc == VINF_SUCCESS, ("Error from RTCritSectInit!\n"));
143
144 resizeGuest();
145 mfInitialized = true;
146
147 rc = SDL_GetRendererInfo(mpRenderer, &mRenderInfo);
148 if (RT_SUCCESS(rc))
149 {
150 if (fShowSDLConfig)
151 RTPrintf("Render info:\n"
152 " Name: %s\n"
153 " Render flags: 0x%x\n"
154 " SDL video driver: %s\n",
155 mRenderInfo.name,
156 mRenderInfo.flags,
157 RTEnvGet("SDL_VIDEODRIVER"));
158 }
159}
160
161
162Framebuffer::~Framebuffer()
163{
164 LogFlow(("Framebuffer::~Framebuffer\n"));
165 if (mSurfVRAM)
166 {
167 SDL_FreeSurface(mSurfVRAM);
168 mSurfVRAM = NULL;
169 }
170 RTCritSectDelete(&mUpdateLock);
171}
172
173/* static */
174bool Framebuffer::init(bool fShowSDLConfig)
175{
176 LogFlow(("VBoxSDLFB::init\n"));
177
178 /* memorize the thread that inited us, that's the SDL thread */
179 gSdlNativeThread = RTThreadNativeSelf();
180
181#ifdef RT_OS_WINDOWS
182 /* default to DirectX if nothing else set */
183 if (!RTEnvExist("SDL_VIDEODRIVER"))
184 {
185 RTEnvSet("SDL_VIDEODRIVER", "directx");
186 }
187#endif
188#ifdef VBOXSDL_WITH_X11
189 /* On some X servers the mouse is stuck inside the bottom right corner.
190 * See http://wiki.clug.org.za/wiki/QEMU_mouse_not_working */
191 RTEnvSet("SDL_VIDEO_X11_DGAMOUSE", "0");
192#endif
193
194 /* create SDL event semaphore */
195 int vrc = RTSemEventCreate(&g_EventSemSDLEvents);
196 AssertReleaseRC(vrc);
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 Framebuffer::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 */
230uint32_t Framebuffer::getWidth()
231{
232 LogFlow(("Framebuffer::getWidth\n"));
233 return mGuestXRes;
234}
235
236/**
237 * Returns the current framebuffer height in pixels.
238 *
239 * @returns COM status code
240 * @param height Address of result buffer.
241 */
242uint32_t Framebuffer::getHeight()
243{
244 LogFlow(("Framebuffer::getHeight\n"));
245 return mGuestYRes;
246}
247
248/**
249 * Return the current framebuffer color depth.
250 *
251 * @returns COM status code
252 * @param bitsPerPixel Address of result variable
253 */
254uint32_t Framebuffer::getBitsPerPixel()
255{
256 LogFlow(("Framebuffer::getBitsPerPixel\n"));
257 /* get the information directly from the surface in use */
258 Assert(mSurfVRAM);
259 return mSurfVRAM ? mSurfVRAM->format->BitsPerPixel : 0;
260}
261
262/**
263 * Return the current framebuffer line size in bytes.
264 *
265 * @returns COM status code.
266 * @param lineSize Address of result variable.
267 */
268uint32_t Framebuffer::getBytesPerLine()
269{
270 LogFlow(("Framebuffer::getBytesPerLine\n"));
271 /* get the information directly from the surface */
272 Assert(mSurfVRAM);
273 return mSurfVRAM ? mSurfVRAM->pitch : 0;
274}
275
276/**
277 * Return the current framebuffer pixel data buffer.
278 *
279 * @returns COM status code.
280 * @param lineSize Address of result variable.
281 */
282uint8_t *Framebuffer::getPixelData()
283{
284 LogFlow(("Framebuffer::getPixelData\n"));
285 /* get the information directly from the surface */
286 Assert(mSurfVRAM);
287 return mSurfVRAM ? (uint8_t *)mSurfVRAM->pixels : NULL;
288}
289
290/**
291 * Notify framebuffer of an update.
292 *
293 * @returns COM status code
294 * @param x Update region upper left corner x value.
295 * @param y Update region upper left corner y value.
296 * @param w Update region width in pixels.
297 * @param h Update region height in pixels.
298 * @param finished Address of output flag whether the update
299 * could be fully processed in this call (which
300 * has to return immediately) or VBox should wait
301 * for a call to the update complete API before
302 * continuing with display updates.
303 */
304int Framebuffer::notifyUpdate(uint32_t x, uint32_t y,
305 uint32_t w, uint32_t h)
306{
307 /*
308 * The input values are in guest screen coordinates.
309 */
310 LogFlow(("Framebuffer::notifyUpdate: x = %d, y = %d, w = %d, h = %d\n",
311 x, y, w, h));
312
313#if defined(VBOXSDL_WITH_X11) || defined(RT_OS_DARWIN)
314 /*
315 * SDL does not allow us to make this call from any other thread than
316 * the main SDL thread (which initialized the video mode). So we have
317 * to send an event to the main SDL thread and process it there. For
318 * sake of simplicity, we encode all information in the event parameters.
319 */
320 SDL_Event event;
321 event.type = SDL_USEREVENT;
322 event.user.code = mScreenId;
323 event.user.type = SDL_USER_EVENT_UPDATERECT;
324
325 SDL_Rect *pUpdateRect = (SDL_Rect *)RTMemAlloc(sizeof(SDL_Rect));
326 AssertPtrReturn(pUpdateRect, VERR_NO_MEMORY);
327 pUpdateRect->x = x;
328 pUpdateRect->y = y;
329 pUpdateRect->w = w;
330 pUpdateRect->h = h;
331 event.user.data1 = pUpdateRect;
332
333 event.user.data2 = NULL;
334 PushNotifyUpdateEvent(&event);
335#else /* !VBOXSDL_WITH_X11 */
336 update(x, y, w, h, true /* fGuestRelative */);
337#endif /* !VBOXSDL_WITH_X11 */
338
339 return VINF_SUCCESS;
340}
341
342
343int Framebuffer::NotifyUpdateImage(uint32_t aX,
344 uint32_t aY,
345 uint32_t aWidth,
346 uint32_t aHeight,
347 void *pvImage)
348{
349 LogFlow(("NotifyUpdateImage: %d,%d %dx%d\n", aX, aY, aWidth, aHeight));
350
351 /* Copy to mSurfVRAM. */
352 SDL_Rect srcRect;
353 SDL_Rect dstRect;
354 srcRect.x = 0;
355 srcRect.y = 0;
356 srcRect.w = (uint16_t)aWidth;
357 srcRect.h = (uint16_t)aHeight;
358 dstRect.x = (int16_t)aX;
359 dstRect.y = (int16_t)aY;
360 dstRect.w = (uint16_t)aWidth;
361 dstRect.h = (uint16_t)aHeight;
362
363 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
364 SDL_Surface *surfSrc = SDL_CreateRGBSurfaceFrom(pvImage, aWidth, aHeight, 32, aWidth * 4,
365 Rmask, Gmask, Bmask, Amask);
366 if (surfSrc)
367 {
368 RTCritSectEnter(&mUpdateLock);
369 if (mfUpdates)
370 SDL_BlitSurface(surfSrc, &srcRect, mSurfVRAM, &dstRect);
371 RTCritSectLeave(&mUpdateLock);
372
373 SDL_FreeSurface(surfSrc);
374 }
375
376 return notifyUpdate(aX, aY, aWidth, aHeight);
377}
378
379int Framebuffer::notifyChange(uint32_t aScreenId,
380 uint32_t aXOrigin,
381 uint32_t aYOrigin,
382 uint32_t aWidth,
383 uint32_t aHeight)
384{
385 LogRel(("NotifyChange: %d %d,%d %dx%d\n",
386 aScreenId, aXOrigin, aYOrigin, aWidth, aHeight));
387
388 RTCritSectEnter(&mUpdateLock);
389
390 /* Disable screen updates. */
391 mfUpdates = false;
392
393 mGuestXRes = aWidth;
394 mGuestYRes = aHeight;
395 mPtrVRAM = NULL;
396 mBitsPerPixel = 0;
397 mBytesPerLine = 0;
398
399 RTCritSectLeave(&mUpdateLock);
400
401 SDL_Event event;
402 event.type = SDL_USEREVENT;
403 event.user.type = SDL_USER_EVENT_NOTIFYCHANGE;
404 event.user.code = mScreenId;
405
406 PushSDLEventForSure(&event);
407
408 RTThreadYield();
409
410 return VINF_SUCCESS;
411}
412
413//
414// Internal public methods
415//
416
417/* This method runs on the main SDL thread. */
418void Framebuffer::notifyChange(uint32_t aScreenId)
419{
420 /* Disable screen updates. */
421 RTCritSectEnter(&mUpdateLock);
422
423 mPtrVRAM = NULL;
424 mBitsPerPixel = 32;
425 mBytesPerLine = mGuestXRes * 4;
426
427 RTCritSectLeave(&mUpdateLock);
428
429 resizeGuest();
430
431 m_pDisplay->i_invalidateAndUpdateScreen(aScreenId);
432}
433
434/**
435 * Method that does the actual resize of the guest framebuffer and
436 * then changes the SDL framebuffer setup.
437 */
438void Framebuffer::resizeGuest()
439{
440 LogFlowFunc (("mGuestXRes: %d, mGuestYRes: %d\n", mGuestXRes, mGuestYRes));
441 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(),
442 ("Wrong thread! SDL is not threadsafe!\n"));
443
444 RTCritSectEnter(&mUpdateLock);
445
446 const uint32_t Rmask = 0x00FF0000, Gmask = 0x0000FF00, Bmask = 0x000000FF, Amask = 0;
447
448 /* first free the current surface */
449 if (mSurfVRAM)
450 {
451 SDL_FreeSurface(mSurfVRAM);
452 mSurfVRAM = NULL;
453 }
454
455 if (mPtrVRAM)
456 {
457 /* Create a source surface from the source bitmap. */
458 mSurfVRAM = SDL_CreateRGBSurfaceFrom(mPtrVRAM, mGuestXRes, mGuestYRes, mBitsPerPixel,
459 mBytesPerLine, Rmask, Gmask, Bmask, Amask);
460 LogFlow(("Framebuffer:: using the source bitmap\n"));
461 }
462 else
463 {
464 mSurfVRAM = SDL_CreateRGBSurface(SDL_SWSURFACE, mGuestXRes, mGuestYRes, 32,
465 Rmask, Gmask, Bmask, Amask);
466 LogFlow(("Framebuffer:: using SDL_SWSURFACE\n"));
467 }
468 LogFlow(("Framebuffer:: created VRAM surface %p\n", mSurfVRAM));
469
470 if (mfSameSizeRequested)
471 {
472 mfSameSizeRequested = false;
473 LogFlow(("Framebuffer:: the same resolution requested, skipping the resize.\n"));
474 }
475 else
476 {
477 /* now adjust the SDL resolution */
478 resizeSDL();
479 }
480
481 /* Enable screen updates. */
482 mfUpdates = true;
483
484 RTCritSectLeave(&mUpdateLock);
485
486 repaint();
487}
488
489/**
490 * Sets SDL video mode. This is independent from guest video
491 * mode changes.
492 *
493 * @remarks Must be called from the SDL thread!
494 */
495void Framebuffer::resizeSDL(void)
496{
497 LogFlow(("VBoxSDL:resizeSDL\n"));
498
499 const int cDisplays = SDL_GetNumVideoDisplays();
500 if (cDisplays > 0)
501 {
502 for (int d = 0; d < cDisplays; d++)
503 {
504 const int cDisplayModes = SDL_GetNumDisplayModes(d);
505 for (int m = 0; m < cDisplayModes; m++)
506 {
507 SDL_DisplayMode mode = { SDL_PIXELFORMAT_UNKNOWN, 0, 0, 0, 0 };
508 if (SDL_GetDisplayMode(d, m, &mode) != 0)
509 {
510 RTPrintf("Display #%d, mode %d:\t\t%i bpp\t%i x %i",
511 SDL_BITSPERPIXEL(mode.format), mode.w, mode.h);
512 }
513
514 if (m == 0)
515 {
516 /*
517 * according to the SDL documentation, the API guarantees that
518 * the modes are sorted from larger to smaller, so we just
519 * take the first entry as the maximum.
520 */
521 mMaxScreenWidth = mode.w;
522 mMaxScreenHeight = mode.h;
523 }
524
525 /* Keep going. */
526 }
527 }
528 }
529 else
530 AssertFailed(); /** @todo */
531
532 uint32_t newWidth;
533 uint32_t newHeight;
534
535 /* reset the centering offsets */
536 mCenterXOffset = 0;
537 mCenterYOffset = 0;
538
539 /* we either have a fixed SDL resolution or we take the guest's */
540 if (mFixedSDLWidth != ~(uint32_t)0)
541 {
542 newWidth = mFixedSDLWidth;
543 newHeight = mFixedSDLHeight;
544 }
545 else
546 {
547 newWidth = RT_MIN(mGuestXRes, mMaxScreenWidth);
548 newHeight = RT_MIN(mGuestYRes, mMaxScreenHeight);
549 }
550
551 /* we don't have any extra space by default */
552 mTopOffset = 0;
553
554 int sdlWindowFlags = SDL_WINDOW_SHOWN;
555 if (mfResizable)
556 sdlWindowFlags |= SDL_WINDOW_RESIZABLE;
557 if (!mpWindow)
558 {
559 SDL_DisplayMode desktop_mode;
560 int x = 40 + mScreenId * 20;
561 int y = 40 + mScreenId * 15;
562
563 SDL_GetDesktopDisplayMode(mScreenId, &desktop_mode);
564 /* create new window */
565
566 char szTitle[64];
567 RTStrPrintf(szTitle, sizeof(szTitle), "SDL window %d", mScreenId);
568 mpWindow = SDL_CreateWindow(szTitle, x, y,
569 newWidth, newHeight, sdlWindowFlags);
570 mpRenderer = SDL_CreateRenderer(mpWindow, -1, 0 /* SDL_RendererFlags */);
571 if (mpRenderer)
572 {
573 SDL_GetRendererInfo(mpRenderer, &mRenderInfo);
574
575 mpTexture = SDL_CreateTexture(mpRenderer, desktop_mode.format,
576 SDL_TEXTUREACCESS_STREAMING, newWidth, newHeight);
577 if (!mpTexture)
578 AssertReleaseFailed();
579 }
580 else
581 AssertReleaseFailed();
582
583 if (12320 == g_cbIco64x01)
584 {
585 gWMIcon = SDL_CreateRGBSurface(0 /* Flags, must be 0 */, 64, 64, 24, 0xff, 0xff00, 0xff0000, 0);
586 /** @todo make it as simple as possible. No PNM interpreter here... */
587 if (gWMIcon)
588 {
589 memcpy(gWMIcon->pixels, g_abIco64x01+32, g_cbIco64x01-32);
590 SDL_SetWindowIcon(mpWindow, gWMIcon);
591 }
592 }
593 }
594 else
595 {
596 int w, h;
597 uint32_t format;
598 int access;
599
600 /* resize current window */
601 SDL_GetWindowSize(mpWindow, &w, &h);
602
603 if (w != (int)newWidth || h != (int)newHeight)
604 SDL_SetWindowSize(mpWindow, newWidth, newHeight);
605
606 SDL_QueryTexture(mpTexture, &format, &access, &w, &h);
607 SDL_DestroyTexture(mpTexture);
608 mpTexture = SDL_CreateTexture(mpRenderer, format, access, newWidth, newHeight);
609 if (!mpTexture)
610 AssertReleaseFailed();
611 }
612}
613
614/**
615 * Update specified framebuffer area. The coordinates can either be
616 * relative to the guest framebuffer or relative to the screen.
617 *
618 * @remarks Must be called from the SDL thread on Linux!
619 * @param x left column
620 * @param y top row
621 * @param w width in pixels
622 * @param h height in pixels
623 * @param fGuestRelative flag whether the above values are guest relative or screen relative;
624 */
625void Framebuffer::update(int x, int y, int w, int h, bool fGuestRelative)
626{
627#if defined(VBOXSDL_WITH_X11) || defined(RT_OS_DARWIN)
628 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
629#endif
630 RTCritSectEnter(&mUpdateLock);
631 Log3Func(("mfUpdates=%RTbool %d,%d %dx%d\n", mfUpdates, x, y, w, h));
632 if (!mfUpdates)
633 {
634 RTCritSectLeave(&mUpdateLock);
635 return;
636 }
637
638 Assert(mSurfVRAM);
639 if (!mSurfVRAM)
640 {
641 RTCritSectLeave(&mUpdateLock);
642 return;
643 }
644
645 /* this is how many pixels we have to cut off from the height for this specific blit */
646 int const yCutoffGuest = 0;
647
648 /**
649 * If we get a SDL window relative update, we
650 * just perform a full screen update to keep things simple.
651 *
652 * @todo improve
653 */
654 if (!fGuestRelative)
655 {
656 x = 0;
657 w = mGuestXRes;
658 y = 0;
659 h = mGuestYRes;
660 }
661
662 SDL_Rect srcRect;
663 srcRect.x = x;
664 srcRect.y = y + yCutoffGuest;
665 srcRect.w = w;
666 srcRect.h = RT_MAX(0, h - yCutoffGuest);
667
668 /*
669 * Destination rectangle is just offset by the label height.
670 * There are two cases though: label height is added to the
671 * guest resolution (mTopOffset == mLabelHeight; yCutoffGuest == 0)
672 * or the label cuts off a portion of the guest screen (mTopOffset == 0;
673 * yCutoffGuest >= 0)
674 */
675 SDL_Rect dstRect;
676 dstRect.x = x + mCenterXOffset;
677 dstRect.y = y + yCutoffGuest + mTopOffset + mCenterYOffset;
678 dstRect.w = w;
679 dstRect.h = RT_MAX(0, h - yCutoffGuest);
680
681 /* Calculate the offset within the VRAM to update the streaming texture directly. */
682 uint8_t const *pbOff = (uint8_t *)mSurfVRAM->pixels
683 + (srcRect.y * mBytesPerLine) + (srcRect.x * (mBitsPerPixel / 8));
684 SDL_UpdateTexture(mpTexture, &srcRect, pbOff, mSurfVRAM->pitch);
685 SDL_RenderCopy(mpRenderer, mpTexture, NULL, NULL);
686
687 RTCritSectLeave(&mUpdateLock);
688
689 SDL_RenderPresent(mpRenderer);
690}
691
692/**
693 * Repaint the whole framebuffer
694 *
695 * @remarks Must be called from the SDL thread!
696 */
697void Framebuffer::repaint()
698{
699 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
700 LogFlow(("Framebuffer::repaint\n"));
701 int w, h;
702 uint32_t format;
703 int access;
704 SDL_QueryTexture(mpTexture, &format, &access, &w, &h);
705 update(0, 0, w, h, false /* fGuestRelative */);
706}
707
708/**
709 * Toggle fullscreen mode
710 *
711 * @remarks Must be called from the SDL thread!
712 */
713void Framebuffer::setFullscreen(bool fFullscreen)
714{
715 AssertMsg(gSdlNativeThread == RTThreadNativeSelf(), ("Wrong thread! SDL is not threadsafe!\n"));
716 LogFlow(("Framebuffer::SetFullscreen: fullscreen: %d\n", fFullscreen));
717 mfFullscreen = fFullscreen;
718 /* only change the SDL resolution, do not touch the guest framebuffer */
719 resizeSDL();
720 repaint();
721}
722
723/**
724 * Return the geometry of the host. This isn't very well tested but it seems
725 * to work at least on Linux hosts.
726 */
727void Framebuffer::getFullscreenGeometry(uint32_t *width, uint32_t *height)
728{
729 SDL_DisplayMode dm;
730 int rc = SDL_GetDesktopDisplayMode(0, &dm); /** @BUGBUG Handle multi monitor setups! */
731 if (rc == 0)
732 {
733 *width = dm.w;
734 *height = dm.w;
735 }
736}
737
738int Framebuffer::setWindowTitle(const char *pcszTitle)
739{
740 SDL_SetWindowTitle(mpWindow, pcszTitle);
741
742 return VINF_SUCCESS;
743}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use