diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m')
-rw-r--r-- | src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m | 3283 |
1 files changed, 3283 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m new file mode 100644 index 00000000..6c643f9c --- /dev/null +++ b/src/VBox/HostServices/SharedOpenGL/render/renderspu_cocoa_helper.m @@ -0,0 +1,3283 @@ +/* $Id: renderspu_cocoa_helper.m $ */ +/** @file + * VirtualBox OpenGL Cocoa Window System Helper Implementation. + * + * This source file is shared between the SharedOpenGL HGCM service and the + * SVGA3d emulation. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/** @page pg_opengl_cocoa OpenGL - Cocoa Window System Helper + * + * How this works: + * In general it is not so easy like on the other platforms, cause Cocoa + * doesn't support any clipping of already painted stuff. In Mac OS X there is + * the concept of translucent canvas's e.g. windows and there it is just + * painted what should be visible to the user. Unfortunately this isn't the + * concept of chromium. Therefor I reroute all OpenGL operation from the guest + * to a frame buffer object (FBO). This is a OpenGL extension, which is + * supported by all OS X versions we support (AFAIC tell). Of course the guest + * doesn't know that and we have to make sure that the OpenGL state always is + * in the right state to paint into the FBO and not to the front/back buffer. + * Several functions below (like cocoaBindFramebufferEXT, cocoaGetIntegerv, + * ...) doing this. When a swap or finish is triggered by the guest, the + * content (which is already bound to an texture) is painted on the screen + * within a separate OpenGL context. This allows the usage of the same + * resources (texture ids, buffers ...) but at the same time having an + * different internal OpenGL state. Another advantage is that we can paint a + * thumbnail of the current output in a much more smaller (GPU accelerated + * scale) version on a third context and use glReadPixels to get the actual + * data. glReadPixels is a very slow operation, but as we just use a much more + * smaller image, we can handle it (anyway this is only done 5 times per + * second). + * + * Other things to know: + * - If the guest request double buffering, we have to make sure there are two + * buffers. We use the same FBO with 2 color attachments. Also glDrawBuffer + * and glReadBuffer is intercepted to make sure it is painted/read to/from + * the correct buffers. On swap our buffers are swapped and not the + * front/back buffer. + * - If the guest request a depth/stencil buffer, a combined render buffer for + * this is created. + * - If the size of the guest OpenGL window changes, all FBO's, textures, ... + * need to be recreated. + * - We need to track any changes to the parent window + * (create/destroy/move/resize). The various classes like OverlayHelperView, + * OverlayWindow, ... are there for. + * - The HGCM service runs on a other thread than the Main GUI. Keeps this + * always in mind (see e.g. performSelectorOnMainThread in renderFBOToView) + * - We make heavy use of late binding. We can not be sure that the GUI (or any + * other third party GUI), overwrite our NSOpenGLContext. So we always ask if + * this is our own one, before use. Really neat concept of Objective-C/Cocoa + * ;) + * + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef IN_VMSVGA3D +# define LOG_GROUP LOG_GROUP_DEV_VMSVGA +#endif +#include "renderspu_cocoa_helper.h" + +#import <Cocoa/Cocoa.h> +#undef PVM /* sys/param.h (included via Cocoa.h) pollutes the namespace with this define. */ + +#ifndef IN_VMSVGA3D +# include "chromium.h" /* For the visual bits of chromium */ +#endif + +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/time.h> +#include <iprt/thread.h> + +#include <VBox/VBoxOGL.h> +#include <VBox/log.h> + +#ifdef IN_VMSVGA3D +# include "DevVGA-SVGA3d-cocoa.h" +# include <OpenGL/OpenGL.h> +# include <OpenGL/gl3.h> +# include <OpenGL/gl3ext.h> +# include <OpenGL/glext.h> +#else +# include <cr_vreg.h> +# include <cr_error.h> +# include <cr_blitter.h> +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +# include <cr_pixeldata.h> +# endif +# include "renderspu.h" +#endif + + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/* Debug macros */ +/** @def FBO + * Disable this to see how the output is without the FBO in the middle of the processing chain. */ +#define FBO 1 +/** @def SHOW_WINDOW_BACKGROUND + * Define this to see the window background even if the window is clipped. */ +/** @def DEBUG_VERBOSE + * Define this to get some debug info about the messages flow. */ +#if 0 || defined(DOXYGEN_RUNNING) +# define SHOW_WINDOW_BACKGROUND 1 +# define DEBUG_VERBOSE +#endif + +#ifdef DEBUG_VERBOSE +# error "should be disabled!" +# ifdef IN_VMSVGA3D +# define DEBUG_INFO(text) do { LogRel(text); AssertFailed(); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); AssertFailed(); } while (0) +# else +# define DEBUG_INFO(text) do { LogRel(text); AssertFailed(); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); AssertFailed(); } while (0) +# endif + +# define DEBUG_MSG(text) do { LogRel(text); } while (0) +# define DEBUG_MSG_1(text) do { LogRel(text); } while (0) + +#else + +# ifdef IN_VMSVGA3D +# define DEBUG_INFO(text) do { LogRel(text); } while (0) +# define DEBUG_WARN(text) do { LogRel(text); } while (0) +# else +# define DEBUG_INFO(text) do { crInfo text; } while (0) +# define DEBUG_WARN(text) do { crWarning text; } while (0) +#endif +# define DEBUG_MSG(text) do {} while (0) +# define DEBUG_MSG_1(text) do {} while (0) + +#endif +#ifdef IN_VMSVGA3D +# define DEBUG_MSG_NOT_VMSVGA3D(a_TextArgs) do {} while (0) +# define COCOA_LOG_FLOW(a_TextArgs) LogFlow(a_TextArgs) +#else +# define DEBUG_MSG_NOT_VMSVGA3D(a_TextArgs) DEBUG_MSG(a_TextArgs) +# define COCOA_LOG_FLOW(a_TextArgs) DEBUG_MSG(a_TextArgs) +#endif + + +#define DEBUG_FUNC_ENTER() DEBUG_MSG(("==>%s\n", __PRETTY_FUNCTION__)) +#define DEBUG_FUNC_LEAVE() DEBUG_MSG(("<==%s\n", __PRETTY_FUNCTION__)) + +#define DEBUG_GL_SAVE_STATE() \ + do { \ + glPushAttrib(GL_ALL_ATTRIB_BITS); \ + glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); \ + glMatrixMode(GL_PROJECTION); \ + glPushMatrix(); \ + glMatrixMode(GL_TEXTURE); \ + glPushMatrix(); \ + glMatrixMode(GL_COLOR); \ + glPushMatrix(); \ + glMatrixMode(GL_MODELVIEW); \ + glPushMatrix(); \ + } while (0) + +#define DEBUG_GL_RESTORE_STATE() \ + do { \ + glMatrixMode(GL_MODELVIEW); \ + glPopMatrix(); \ + glMatrixMode(GL_COLOR); \ + glPopMatrix(); \ + glMatrixMode(GL_TEXTURE); \ + glPopMatrix(); \ + glMatrixMode(GL_PROJECTION); \ + glPopMatrix(); \ + glPopClientAttrib(); \ + glPopAttrib(); \ + } while (0) + +#ifdef VBOX_STRICT +# define DEBUG_CLEAR_GL_ERRORS() \ + do { \ + while (glGetError() != GL_NO_ERROR) \ + { /* nothing */ } \ + } while (0) +# define DEBUG_CHECK_GL_ERROR(a_szOp) \ + do { \ + GLenum iGlCheckErr = glGetError(); \ + if (RT_UNLIKELY(iGlCheckErr != GL_NO_ERROR)) \ + AssertMsgFailed((a_szOp ": iGlCheckErr=%#x\n", iGlCheckErr)); \ + } while (0) +#else +# define DEBUG_CLEAR_GL_ERRORS() do {} while (0) +# define DEBUG_CHECK_GL_ERROR(a_szOp) do {} while (0) +#endif + +/* Whether we control NSView automatic content zooming on Retina/HiDPI displays. */ +#define VBOX_WITH_CONFIGURABLE_HIDPI_SCALING 1 + + +#ifdef IN_VMSVGA3D + +/* + * VMSVGA3D compatibility glue. + */ +typedef struct WindowInfo WindowInfo; + +# define CR_RGB_BIT RT_BIT_32(0) + +# define CR_ALPHA_BIT RT_BIT_32(1) +# define CR_DEPTH_BIT RT_BIT_32(2) +# define CR_STENCIL_BIT RT_BIT_32(3) +# define CR_ACCUM_BIT RT_BIT_32(4) +# define CR_DOUBLE_BIT RT_BIT_32(5) +# define CR_STEREO_BIT RT_BIT_32(6) +# define CR_MULTISAMPLE_BIT RT_BIT_32(7) + +# define CR_OVERLAY_BIT RT_BIT_32(8) +# define CR_PBUFFER_BIT RT_BIT_32(9) +# define VMSVGA3D_NON_DEFAULT_PROFILE_BIT RT_BIT_32(31) +# define CR_ALL_BITS UINT32_C(0x800003ff) + +#endif /* IN_VMSVGA3D */ + + +/** + * Works functions that creates a NSOpenGLPixelFormat instance. + * + * @returns Instance. + * @param fVisParams Context flags. + */ +static NSOpenGLPixelFormat *vboxCreatePixelFormat(GLbitfield fVisParams) +{ + NSOpenGLPixelFormatAttribute attribs[24] = + { +#ifdef IN_VMSVGA3D + NSOpenGLPFAOpenGLProfile, (NSOpenGLPixelFormatAttribute)0, +#endif + NSOpenGLPFAAccelerated, + NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)24 + }; +#ifndef IN_VMSVGA3D + int i = 3; +#else + int i = 3+2; + if (fVisParams & VMSVGA3D_NON_DEFAULT_PROFILE_BIT) + attribs[1] = VBOX_VMSVGA3D_DEFAULT_OGL_PROFILE >= 3.2 ? NSOpenGLProfileVersionLegacy : NSOpenGLProfileVersion3_2Core; + else + attribs[1] = VBOX_VMSVGA3D_DEFAULT_OGL_PROFILE >= 3.2 ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy; +#endif + + if (fVisParams & CR_ALPHA_BIT) + { + COCOA_LOG_FLOW((" CR_ALPHA_BIT requested\n")); + attribs[i++] = NSOpenGLPFAAlphaSize; + attribs[i++] = 8; + } + if (fVisParams & CR_DEPTH_BIT) + { + COCOA_LOG_FLOW((" CR_DEPTH_BIT requested\n")); + attribs[i++] = NSOpenGLPFADepthSize; + attribs[i++] = 24; + } + if (fVisParams & CR_STENCIL_BIT) + { + COCOA_LOG_FLOW((" CR_STENCIL_BIT requested\n")); + attribs[i++] = NSOpenGLPFAStencilSize; + attribs[i++] = 8; + } + if (fVisParams & CR_ACCUM_BIT) + { + COCOA_LOG_FLOW((" CR_ACCUM_BIT requested\n")); + attribs[i++] = NSOpenGLPFAAccumSize; + if (fVisParams & CR_ALPHA_BIT) + attribs[i++] = 32; + else + attribs[i++] = 24; + } + if (fVisParams & CR_MULTISAMPLE_BIT) + { + COCOA_LOG_FLOW((" CR_MULTISAMPLE_BIT requested\n")); + attribs[i++] = NSOpenGLPFASampleBuffers; + attribs[i++] = 1; + attribs[i++] = NSOpenGLPFASamples; + attribs[i++] = 4; + } + if (fVisParams & CR_DOUBLE_BIT) + { + COCOA_LOG_FLOW((" CR_DOUBLE_BIT requested\n")); + attribs[i++] = NSOpenGLPFADoubleBuffer; + } + if (fVisParams & CR_STEREO_BIT) + { + /* We don't support that. + COCOA_LOG_FLOW((" CR_STEREO_BIT requested\n")); + attribs[i++] = NSOpenGLPFAStereo; + */ + } + + if (VBoxOglIsOfflineRenderingAppropriate()) + { + COCOA_LOG_FLOW((" Offline rendering is enabled\n")); + attribs[i++] = NSOpenGLPFAAllowOfflineRenderers; + } + + /* Mark the end */ + attribs[i++] = 0; + + /* Instantiate the pixel format object. */ + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; +} + + +static NSOpenGLContext *vboxCtxGetCurrent(void) +{ +#ifdef IN_VMSVGA3D + return [NSOpenGLContext currentContext]; +#else + GET_CONTEXT(pCtxInfo); + if (pCtxInfo) + { + Assert(pCtxInfo->context); + return pCtxInfo->context; + } + return nil; +#endif +} + +static bool vboxCtxSyncCurrentInfo(void) +{ +#ifdef IN_VMSVGA3D + return false; +#else + bool fAdjusted = false; + GET_CONTEXT(pCtxInfo); + NSOpenGLContext *pCtx = [NSOpenGLContext currentContext]; + NSView *pView = pCtx ? [pCtx view] : nil; + if (pCtxInfo) + { + WindowInfo *pWinInfo = pCtxInfo->currentWindow; + Assert(pWinInfo); + if ( pCtxInfo->context != pCtx + || pWinInfo->window != pView) + { + renderspu_SystemMakeCurrent(pWinInfo, 0, pCtxInfo); + fAdjusted = true; + } + } + else if (pCtx) + { + [NSOpenGLContext clearCurrentContext]; + fAdjusted = true; + } + + return fAdjusted; +#endif +} + + +/** + * State carrying structure for use with vboxCtxEnter and vboxCtxLeave + */ +typedef struct VBOX_CR_RENDER_CTX_INFO +{ + bool fIsValid; + NSOpenGLContext *pCtx; + NSView *pView; +} VBOX_CR_RENDER_CTX_INFO; +/** Pointer to render context info for use with vboxCtxEnter/Leave. */ +typedef VBOX_CR_RENDER_CTX_INFO *PVBOX_CR_RENDER_CTX_INFO; + +static void vboxCtxEnter(NSOpenGLContext *pNewCtx, PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + NSOpenGLContext *pOldCtx = vboxCtxGetCurrent(); + NSView *pOldView = pOldCtx ? [pOldCtx view] : nil; + NSView *pNewView = [pNewCtx view]; + + Assert(pNewCtx); + + if ( pOldCtx != pNewCtx + || pOldView != pNewView) + { + if (pOldCtx != nil) + glFlush(); + + DEBUG_CLEAR_GL_ERRORS(); + [pNewCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + + pCtxInfo->fIsValid = true; + pCtxInfo->pCtx = pOldCtx; + /** @todo r=bird: Why do we save the NEW VIEW here? vboxCtxLeave calls it 'pOldView'. Bug? */ + pCtxInfo->pView = pNewView; + } + else + { + /* No context switch necessary. */ + pCtxInfo->fIsValid = false; + } +} + +static void vboxCtxLeave(PVBOX_CR_RENDER_CTX_INFO pCtxInfo) +{ + if (pCtxInfo->fIsValid) + { + NSOpenGLContext *pOldCtx = pCtxInfo->pCtx; + NSView *pOldView = pCtxInfo->pView; + + glFlush(); + if (pOldCtx != nil) + { + /* vboxCtxEnter saves the new view, not the old. So, what we actually + do here is switching the view of the old context to that of the new + one (wrt vboxCtxEnter) before making it current. */ + /** @todo r=bird: Figure out what we really want to do here, and either rename + * pOldView or fix the code. */ + if ([pOldCtx view] != pOldView) + { + DEBUG_CLEAR_GL_ERRORS(); + [pOldCtx setView: pOldView]; + DEBUG_CHECK_GL_ERROR("setView"); + } + + DEBUG_CLEAR_GL_ERRORS(); + [pOldCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + +#ifdef VBOX_STRICT + { + NSOpenGLContext *pTstOldCtx = [NSOpenGLContext currentContext]; + NSView *pTstOldView = pTstOldCtx ? [pTstOldCtx view] : nil; + Assert(pTstOldCtx == pOldCtx); + Assert(pTstOldView == pOldView); + } +#endif + } + else + { + [NSOpenGLContext clearCurrentContext]; + } + } +} + + +/** + * Custom OpenGL context class. + * + * This implementation doesn't allow to set a view to the context, but save the + * view for later use. Also it saves a copy of the pixel format used to create + * that context for later use. + */ +@interface OverlayOpenGLContext: NSOpenGLContext +{ +@private + NSOpenGLPixelFormat *m_pPixelFormat; + NSView *m_pView; +} +- (NSOpenGLPixelFormat *)openGLPixelFormat; +@end + +/** + * Abstrack task class. + */ +@interface VBoxTask : NSObject +{ +} +- (void)run; +@end + +@implementation VBoxTask +/** Run method that the child classes must reimplement. + * This will abort the process. */ +- (void)run +{ + AssertReleaseFailed(); +} +@end + + +/** + * Generic task class for executing a given method select. + */ +@interface VBoxTaskPerformSelector : VBoxTask +{ +@private + id m_Object; + SEL m_Selector; + id m_Arg; +} +- (id)initWithObject:(id)aObject selector:(SEL)aSelector arg:(id)aArg; +- (void)run; +- (void)dealloc; +@end + +@implementation VBoxTaskPerformSelector + +/** + * Initializes a VBoxTaskPerformSelector. + * + * @param aObject The object (reference not consumed). + * @param aSelector The method selector. + * @param aArg The method argument (reference not consumed). + */ +- (id)initWithObject:(id)aObject selector:(SEL)aSelector arg:(id)aArg +{ + self = [super init]; + if (self) + { + [aObject retain]; + m_Object = aObject; + m_Selector = aSelector; + if (aArg != nil) + [aArg retain]; + m_Arg = aArg; + } + + return self; +} + +- (void)run +{ + [m_Object performSelector:m_Selector withObject:m_Arg]; +} + +- (void)dealloc +{ + [m_Object release]; + if (m_Arg != nil) + [m_Arg release]; + + [super dealloc]; +} +@end + + +/** + * + */ +@interface VBoxTaskComposite : VBoxTask +{ +@private + NSUInteger m_CurIndex; + RTCRITSECT m_Lock; + NSMutableArray *m_pArray; +} +- (id)init; +- (void)add:(VBoxTask *)pTask; +- (void)run; +- (void)dealloc; +@end + +@implementation VBoxTaskComposite +- (id)init +{ + self = [super init]; + + if (self) + { + int rc = RTCritSectInit(&m_Lock); + if (!RT_SUCCESS(rc)) + { + DEBUG_WARN(("RTCritSectInit failed %d\n", rc)); + return nil; + } + + m_CurIndex = 0; + + m_pArray = [[NSMutableArray alloc] init]; + } + + return self; +} + +/** + * Adds a task to the composite task object. + * + * @param pTask Task to add. Reference is NOT consumed. + */ +- (void)add:(VBoxTask *)pTask +{ + [pTask retain]; + int rc = RTCritSectEnter(&m_Lock); + if (RT_SUCCESS(rc)) + { + [m_pArray addObject:pTask]; + RTCritSectLeave(&m_Lock); + } + else + { + DEBUG_WARN(("RTCritSectEnter failed %d\n", rc)); + [pTask release]; + } +} + +- (void)run +{ + for (;;) + { + /* + * Dequeue a task. + */ + int rc = RTCritSectEnter(&m_Lock); + if (RT_FAILURE(rc)) + { + DEBUG_WARN(("RTCritSectEnter failed %d\n", rc)); + break; + } + + NSUInteger count = [m_pArray count]; + Assert(m_CurIndex <= count); + if (m_CurIndex == count) + { + [m_pArray removeAllObjects]; + m_CurIndex = 0; + RTCritSectLeave(&m_Lock); + break; + } + + VBoxTask *pTask = (VBoxTask *)[m_pArray objectAtIndex:m_CurIndex]; + Assert(pTask != nil); + + ++m_CurIndex; + + /* + * Remove the first 1025 empty entires. + */ + if (m_CurIndex > 1024) + { + NSRange range; + range.location = 0; + range.length = m_CurIndex; + [m_pArray removeObjectsInRange:range]; + m_CurIndex = 0; + } + RTCritSectLeave(&m_Lock); + + /* + * Run the task and release it. + */ + [pTask run]; + [pTask release]; + } +} + +- (void)dealloc +{ + NSUInteger count = [m_pArray count]; + for (;m_CurIndex < count; ++m_CurIndex) + { + VBoxTask *pTask = (VBoxTask*)[m_pArray objectAtIndex:m_CurIndex]; + DEBUG_WARN(("dealloc with non-empty tasks! %p\n", pTask)); + [pTask release]; + } + + [m_pArray release]; + RTCritSectDelete(&m_Lock); + + [super dealloc]; +} +@end + + +/** + * + * + */ +@interface VBoxMainThreadTaskRunner : NSObject +{ +@private + VBoxTaskComposite *m_pTasks; +} +- (id)init; +- (void)add:(VBoxTask *)pTask; +- (void)addObj:(id)aObject selector:(SEL)aSelector arg:(id)aArg; +- (void)runTasks; +- (bool)runTasksSyncIfPossible; +- (void)dealloc; ++ (VBoxMainThreadTaskRunner *) globalInstance; +@end + +@implementation VBoxMainThreadTaskRunner +- (id)init +{ + self = [super init]; + if (self) + { + m_pTasks = [[VBoxTaskComposite alloc] init]; + } + + return self; +} + ++ (VBoxMainThreadTaskRunner *) globalInstance +{ + static dispatch_once_t s_DispatchOnce; + static VBoxMainThreadTaskRunner *s_pRunner = nil; + dispatch_once(&s_DispatchOnce, ^{ + s_pRunner = [[VBoxMainThreadTaskRunner alloc] init]; + }); + return s_pRunner; +} + +- (void)add:(VBoxTask *)pTask +{ + DEBUG_FUNC_ENTER(); + [m_pTasks add:pTask]; + /** @todo r=bird: Unbalanced [self retain]. */ + [self retain]; + + if (![self runTasksSyncIfPossible]) + { + DEBUG_MSG(("task will be processed async\n")); + [self performSelectorOnMainThread:@selector(runTasks) withObject:nil waitUntilDone:NO]; + } + + DEBUG_FUNC_LEAVE(); +} + +/** + * Adds a task calling an object method (selector). + * + * @param aObject The object (reference not consumed).. + * @param aSelector The method selector. + * @param aArg The method argument (reference not consumed). + */ +- (void)addObj:(id)aObject selector:(SEL)aSelector arg:(id)aArg +{ + VBoxTaskPerformSelector *pSelTask = [[VBoxTaskPerformSelector alloc] initWithObject:aObject selector:aSelector arg:aArg]; + [self add:pSelTask]; + [pSelTask release]; +} + + +/** + * Internal method for running the pending tasks. + */ +- (void)runTasks +{ + if ([NSThread isMainThread]) + { + [m_pTasks run]; + /** @todo r=bird: This release and the retain in the add method aren't + * necessarily balanced if there are more than one call to add(). + * + * This could probably end up deleting the singleton prematurely and leave + * globalInstance() returning pointers to a stale object in freed memory, + * quite possibly causing crashes or/and heap corruption. */ + [self release]; + } + else + { + DEBUG_WARN(("run tasks called not on main thread!\n")); +#ifndef DEBUG_VERBOSE + AssertFailed(); +#endif + [self performSelectorOnMainThread:@selector(runTasks) withObject:nil waitUntilDone:YES]; + } +} + +/** + * Callback for calling runTasks via renderspuCalloutClient. + * @param pvUser The VBoxMainThreadTaskRunner singleton. + */ +static DECLCALLBACK(void) VBoxMainThreadTaskRunner_RcdRunCallback(void *pvUser) +{ + DEBUG_FUNC_ENTER(); + VBoxMainThreadTaskRunner *pRunner = (VBoxMainThreadTaskRunner *)pvUser; + Assert(pRunner == [VBoxMainThreadTaskRunner globalInstance]); + [pRunner runTasks]; + DEBUG_FUNC_LEAVE(); +} + +/** + * Runs pending tasks synchronously, if possible in the current context. + * + * @returns true if executed tasks, false if not possible. + */ +- (bool)runTasksSyncIfPossible +{ +#ifndef IN_VMSVGA3D + /* + * Call on main thread (?) via renderspuCalloutClient (whatever that is). + */ + if (renderspuCalloutAvailable()) + { + Assert(![NSThread isMainThread]); + renderspuCalloutClient(VBoxMainThreadTaskRunner_RcdRunCallback, self); + return true; + } +#endif + + /* + * Run directly if on main thread. + */ + if ([NSThread isMainThread]) + { + [self runTasks]; + return true; + } + + /* Not possible. */ + return false; +} + +- (void)dealloc +{ + /** @todo r=bird: WTF is the point of the deallocator. The object is a singelton + * stored in an inaccessible static variable! */ + [m_pTasks release]; + m_pTasks = nil; + + [super dealloc]; +} + +@end + +#ifndef IN_VMSVGA3D +@class DockOverlayView; +#endif + +/** + * The custom view class. + * + * This is the main class of the cocoa OpenGL implementation. It manages a + * frame buffer object for the rendering of the guest applications. The guest + * applications render in this frame buffer which is bound to an OpenGL texture. + * To display the guest content, a secondary shared OpenGL context of the main + * OpenGL context is created. The secondary context is marked as non-opaque and + * the texture is displayed on an object which is composed out of the several + * visible region rectangles. + */ +@interface OverlayView : NSView +{ +@private + NSView *m_pParentView; + NSWindow *m_pOverlayWin; + + NSOpenGLContext *m_pGLCtx; + NSOpenGLContext *m_pSharedGLCtx; + RTTHREAD m_Thread; + + GLuint m_FBOId; + +#ifndef IN_VMSVGA3D + /** The corresponding dock tile view of this OpenGL view & all helper + * members. */ + DockOverlayView *m_DockTileView; + + GLfloat m_FBOThumbScaleX; + GLfloat m_FBOThumbScaleY; + uint64_t m_msDockUpdateTS; +#endif + + /** @name For clipping + * @remarks appears to be unused and a complete waste of time + heap. + * @{ */ + GLint m_cClipRects; + GLint *m_paClipRects; + /** @} */ + + /** @name Position/Size tracking + * @{ */ + NSPoint m_Pos; + NSSize m_Size; + /** @} */ + + /** This is necessary for clipping on the root window */ + NSRect m_RootRect; + float m_yInvRootOffset; + +#ifndef IN_VMSVGA3D + CR_BLITTER *m_pBlitter; + WindowInfo *m_pWinInfo; +#endif + bool m_fNeedViewportUpdate; + bool m_fNeedCtxUpdate; + bool m_fDataVisible; + bool m_fCleanupNeeded; + bool m_fEverSized; +} +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo + fVisParams:(GLbitfield) fVisParams; +- (void)setGLCtx:(NSOpenGLContext*)pCtx; +- (NSOpenGLContext *)glCtx; + +- (void)setParentView: (NSView *)view; +- (NSView *)parentView; +- (void)setOverlayWin: (NSWindow *)win; +- (NSWindow *)overlayWin; + +- (void)vboxSetPos:(NSPoint)pos; +- (void)vboxSetPosUI:(NSPoint)pos; +- (void)vboxSetPosUIObj:(NSValue *)pPos; +- (NSPoint)pos; +- (bool)isEverSized; +- (void)vboxDestroy; +- (void)vboxSetSizeUI:(NSSize)size; +- (void)vboxSetSizeUIObj:(NSValue *)pSize; +- (void)vboxSetSize:(NSSize)size; +- (NSSize)size; +- (void)updateViewportCS; +#ifdef VBOX_WITH_CONFIGURABLE_HIDPI_SCALING +- (NSRect)safeConvertRectToBacking:(NSRect *)pRect; +- (CGFloat)safeGetBackingScaleFactor; +#endif +- (NSRect)safeConvertToScreen:(NSRect *)pRect; +- (void)vboxReshapePerform; +- (void)vboxReshapeOnResizePerform; +- (void)vboxReshapeOnReparentPerform; + +#ifndef IN_VMSVGA3D +- (void)createDockTile; +- (void)deleteDockTile; +#endif + +- (void)makeCurrentFBO; +- (void)swapFBO; +- (void)vboxSetVisible:(GLboolean)fVisible; +- (void)vboxSetVisibleUIObj:(NSNumber *)pVisible; +- (void)vboxSetVisibleUI:(GLboolean)fVisible; +- (void)vboxTryDraw; +- (void)vboxTryDrawUI; +- (void)vboxReparent:(NSView *)pParentView; +- (void)vboxReparentUI:(NSView *)pParentView; +- (void)vboxPresent:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +- (void)vboxPresentCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +#ifndef IN_VMSVGA3D +- (void)vboxPresentToDockTileCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +#endif +- (void)vboxPresentToViewCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor; +- (void)presentComposition:(const VBOXVR_SCR_COMPOSITOR_ENTRY *)pChangedEntry; +#ifndef IN_VMSVGA3D +- (void)vboxBlitterSyncWindow; +#endif + +- (void)clearVisibleRegions; +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint *)paRects; +- (GLboolean)vboxNeedsEmptyPresent; + +#ifndef IN_VMSVGA3D +- (NSView *)dockTileScreen; +- (void)reshapeDockTile; +#endif +- (void)cleanupData; +@end + +/** + * Helper view. + * + * This view is added as a sub view of the parent view to track + * main window changes. Whenever the main window is changed + * (which happens on fullscreen/seamless entry/exit) the overlay + * window is informed & can add them self as a child window + * again. + */ +@class OverlayWindow; +@interface OverlayHelperView: NSView +{ +@private + OverlayWindow *m_pOverlayWindow; +} +-(id)initWithOverlayWindow:(NSRect)frame overlayWindow:(OverlayWindow *)pOverlayWindow; +@end + +/** + * Custom window class. + * + * This is the overlay window which contains our custom NSView. + * Its a direct child of the Qt Main window. It marks its background + * transparent & non opaque to make clipping possible. It also disable mouse + * events and handle frame change events of the parent view. + */ +@interface OverlayWindow : NSWindow +{ +@private + NSView *m_pParentView; + OverlayView *m_pOverlayView; + OverlayHelperView *m_pOverlayHelperView; + NSThread *m_Thread; +} +- (id)initWithParentView:(NSView *)pParentView overlayView:(OverlayView *)pOverlayView; +- (void)parentWindowFrameChanged:(NSNotification *)note; +- (void)parentWindowChanged:(NSWindow *)pWindow; +@end + + +#ifndef IN_VMSVGA3D +/** + * Dock overlay view class. + */ +@interface DockOverlayView: NSView +{ + NSBitmapImageRep *m_ThumbBitmap; + NSImage *m_ThumbImage; + NSLock *m_Lock; +} +- (void)dealloc; +- (void)cleanup; +- (void)lock; +- (void)unlock; +- (void)setFrame:(NSRect)frame; +- (void)drawRect:(NSRect)aRect; +- (NSBitmapImageRep *)thumbBitmap; +- (NSImage *)thumbImage; +@end + +@implementation DockOverlayView +- (id)init +{ + DEBUG_FUNC_ENTER(); + self = [super init]; + if (self) + { + /* + * We need a lock cause the thumb image could be accessed from the main + * thread when someone is calling display on the dock tile & from the + * OpenGL thread when the thumbnail is updated. + */ + m_Lock = [[NSLock alloc] init]; + } + + DEBUG_FUNC_LEAVE(); + + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + + [self cleanup]; + [m_Lock release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +- (void)cleanup +{ + DEBUG_FUNC_ENTER(); + + if (m_ThumbImage != nil) + { + [m_ThumbImage release]; + m_ThumbImage = nil; + } + + if (m_ThumbBitmap != nil) + { + [m_ThumbBitmap release]; + m_ThumbBitmap = nil; + } + + DEBUG_FUNC_LEAVE(); +} + +- (void)lock +{ + DEBUG_FUNC_ENTER(); + [m_Lock lock]; + DEBUG_FUNC_LEAVE(); +} + +- (void)unlock +{ + DEBUG_FUNC_ENTER(); + [m_Lock unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (void)setFrame:(NSRect)frame +{ + DEBUG_FUNC_ENTER(); + [super setFrame:frame]; + + [self lock]; + [self cleanup]; + + if ( frame.size.width > 0 + && frame.size.height > 0) + { + /* Create a buffer for our thumbnail image. Its in the size of this view. */ + m_ThumbBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL + pixelsWide:frame.size.width + pixelsHigh:frame.size.height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:NSAlphaFirstBitmapFormat + bytesPerRow:frame.size.width * 4 + bitsPerPixel:8 * 4 + ]; + m_ThumbImage = [[NSImage alloc] initWithSize:[m_ThumbBitmap size]]; + [m_ThumbImage addRepresentation:m_ThumbBitmap]; + } + + [self unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (BOOL)isFlipped +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return YES; +} + +- (void)drawRect:(NSRect)aRect +{ + NSRect frame; + DEBUG_FUNC_ENTER(); + [self lock]; + +#ifdef SHOW_WINDOW_BACKGROUND + [[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.7] set]; + frame = [self frame]; + [NSBezierPath fillRect:NSMakeRect(0, 0, frame.size.width, frame.size.height)]; +#endif /* SHOW_WINDOW_BACKGROUND */ + if (m_ThumbImage != nil) + [m_ThumbImage drawAtPoint:NSMakePoint(0, 0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; + + [self unlock]; + DEBUG_FUNC_LEAVE(); +} + +- (NSBitmapImageRep *)thumbBitmap +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return m_ThumbBitmap; +} + +- (NSImage *)thumbImage +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + return m_ThumbImage; +} +@end +#endif /* !IN_VMSVGA3D */ + + +/******************************************************************************** +* +* OverlayOpenGLContext class implementation +* +********************************************************************************/ +@implementation OverlayOpenGLContext + +-(id)initWithFormat:(NSOpenGLPixelFormat *)format shareContext:(NSOpenGLContext *)share +{ + DEBUG_FUNC_ENTER(); + + m_pPixelFormat = NULL; + m_pView = NULL; + + self = [super initWithFormat:format shareContext:share]; + Assert(self != nil); + if (self) + m_pPixelFormat = format; + + DEBUG_MSG(("OCTX(%p): init OverlayOpenGLContext\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): dealloc OverlayOpenGLContext\n", (void *)self)); + + [m_pPixelFormat release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +-(bool)isDoubleBuffer +{ + DEBUG_FUNC_ENTER(); + + GLint val; + [m_pPixelFormat getValues:&val forAttribute:NSOpenGLPFADoubleBuffer forVirtualScreen:0]; + + DEBUG_FUNC_LEAVE(); + return val == GL_TRUE ? YES : NO; +} + +-(void)setView:(NSView *)view +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): setView: new view: %p\n", (void *)self, (void *)view)); + +#if 1 /* def FBO */ + m_pView = view;; +#else + [super setView: view]; +#endif + + DEBUG_FUNC_LEAVE(); +} + +-(NSView *)view +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); +#if 1 /* def FBO */ + return m_pView; +#else + return [super view]; +#endif +} + +-(void)clearDrawable +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OCTX(%p): clearDrawable\n", (void *)self)); + + m_pView = NULL;; + [super clearDrawable]; + + DEBUG_FUNC_LEAVE(); +} + +-(NSOpenGLPixelFormat *)openGLPixelFormat +{ + DEBUG_FUNC_ENTER(); + DEBUG_FUNC_LEAVE(); + + return m_pPixelFormat; +} + +@end /* @implementation OverlayOpenGLContext */ + + +/******************************************************************************** +* +* OverlayHelperView class implementation +* +********************************************************************************/ +@implementation OverlayHelperView + +-(id)initWithOverlayWindow:(NSRect)frame overlayWindow:(OverlayWindow *)pOverlayWindow +{ + DEBUG_FUNC_ENTER(); + + self = [super initWithFrame:frame]; +#ifdef IN_VMSVGA3D + self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; +#endif + + m_pOverlayWindow = pOverlayWindow; + + DEBUG_MSG(("OHVW(%p): init OverlayHelperView\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +-(void)viewDidMoveToWindow +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OHVW(%p): viewDidMoveToWindow: new win: %p\n", (void *)self, (void *)[self window])); + + [m_pOverlayWindow parentWindowChanged:[self window]]; + + DEBUG_FUNC_LEAVE(); +} + +@end + + +/******************************************************************************** +* +* OverlayWindow class implementation +* +********************************************************************************/ +@implementation OverlayWindow + +- (id)initWithParentView:(NSView *)pParentView overlayView:(OverlayView *)pOverlayView +{ + DEBUG_FUNC_ENTER(); + NSWindow *pParentWin = nil; + + self = [super initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + if (self) + { + m_pParentView = pParentView; + m_pOverlayView = pOverlayView; + m_Thread = [NSThread currentThread]; + + [m_pOverlayView setOverlayWin: self]; + +#ifdef IN_VMSVGA3D + NSRect frame = [pParentView frame]; + frame.origin.x = frame.origin.x = 0; + m_pOverlayHelperView = [[OverlayHelperView alloc] initWithOverlayWindow:frame + overlayWindow:self]; +#else + m_pOverlayHelperView = [[OverlayHelperView alloc] initWithOverlayWindow:NSZeroRect + overlayWindow:self]; +#endif + + /* Add the helper view as a child of the parent view to get notifications */ + [pParentView addSubview:m_pOverlayHelperView]; + + /* Make sure this window is transparent */ +#ifdef SHOW_WINDOW_BACKGROUND + /* For debugging */ + [self setBackgroundColor:[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.7]]; +#else + [self setBackgroundColor:[NSColor clearColor]]; +#endif + [self setOpaque:NO]; + [self setAlphaValue:.999]; + + /* Disable mouse events for this window */ + [self setIgnoresMouseEvents:YES]; + + pParentWin = [m_pParentView window]; + + /* Initial set the position to the parents view top/left (Compiz fix). */ + [self setFrameOrigin: + [pParentWin convertBaseToScreen: + [m_pParentView convertPoint:NSZeroPoint toView:nil]]]; + + /* Set the overlay view as our content view */ + [self setContentView:m_pOverlayView]; + + /* Add ourself as a child to the parent views window. Note: this has to + * be done last so that everything else is setup in + * parentWindowChanged. */ + [pParentWin addChildWindow:self ordered:NSWindowAbove]; + } + + DEBUG_MSG(("OWIN(%p): init OverlayWindow\n", (void *)self)); + DEBUG_FUNC_LEAVE(); + return self; +} + +- (void)dealloc +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): dealloc OverlayWindow\n", (void *)self)); + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [m_pOverlayHelperView removeFromSuperview]; + [m_pOverlayHelperView release]; + + [super dealloc]; + + DEBUG_FUNC_LEAVE(); +} + +- (void)parentWindowFrameChanged:(NSNotification *)pNote +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): parentWindowFrameChanged\n", (void *)self)); + + /* + * Reposition this window with the help of the OverlayView. Perform the + * call in the OpenGL thread. + */ + /* + [m_pOverlayView performSelector:@selector(vboxReshapePerform) onThread:m_Thread withObject:nil waitUntilDone:YES]; + */ + + if ([m_pOverlayView isEverSized]) + { + if ([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } + + DEBUG_FUNC_LEAVE(); +} + +- (void)parentWindowChanged:(NSWindow *)pWindow +{ + DEBUG_FUNC_ENTER(); + DEBUG_MSG(("OWIN(%p): parentWindowChanged\n", (void *)self)); + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + if (pWindow != nil) + { + /* Ask to get notifications when our parent window frame changes. */ + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(parentWindowFrameChanged:) + name:NSWindowDidResizeNotification + object:pWindow]; + + /* Add us self as child window */ + [pWindow addChildWindow:self ordered:NSWindowAbove]; + + /* + * Reshape the overlay view after a short waiting time to let the main + * window resize itself properly. + */ + /* + [m_pOverlayView performSelector:@selector(vboxReshapePerform) withObject:nil afterDelay:0.2]; + [NSTimer scheduledTimerWithTimeInterval:0.2 target:m_pOverlayView selector:@selector(vboxReshapePerform) userInfo:nil repeats:NO]; + */ + + if ([m_pOverlayView isEverSized]) + { + if ([NSThread isMainThread]) + [m_pOverlayView vboxReshapePerform]; + else + [self performSelectorOnMainThread:@selector(vboxReshapePerform) withObject:nil waitUntilDone:NO]; + } + } + + DEBUG_FUNC_LEAVE(); +} + +@end /* @implementation OverlayWindow */ + + + +/******************************************************************************** +* +* OverlayView class implementation +* +********************************************************************************/ +@implementation OverlayView + +- (id)initWithFrame:(NSRect)frame thread:(RTTHREAD)aThread parentView:(NSView *)pParentView winInfo:(WindowInfo *)pWinInfo + fVisParams:(GLbitfield) fVisParams +{ + COCOA_LOG_FLOW(("%s: self=%p aThread=%p pParentView=%p pWinInfo=%p fVisParams=%#x\n", __PRETTY_FUNCTION__, (void *)self, + (void *)aThread, (void *)pParentView, (void *)pWinInfo, fVisParams)); + + m_pParentView = pParentView; + /* Make some reasonable defaults */ + m_pGLCtx = nil; + m_pSharedGLCtx = nil; + m_Thread = aThread; + m_FBOId = 0; + m_cClipRects = 0; + m_paClipRects = NULL; + m_Pos = NSZeroPoint; + m_Size = NSMakeSize(1, 1); + m_RootRect = NSMakeRect(0, 0, m_Size.width, m_Size.height); + m_yInvRootOffset = 0; +#ifndef IN_VMSVGA3D + m_pBlitter = nil; + m_pWinInfo = pWinInfo; +#endif + m_fNeedViewportUpdate = true; + m_fNeedCtxUpdate = true; + m_fDataVisible = false; + m_fCleanupNeeded = false; + m_fEverSized = false; + + self = [super initWithFrame:frame]; +#if defined(VBOX_WITH_CONFIGURABLE_HIDPI_SCALING) && !defined(IN_VMSVGA3D) + /* Always allocate HiDPI-ready backing store for NSView, so we will be able change HiDPI scaling option in runtime. */ + crDebug("HiDPI: Allocate big backing store for NSView. Up-scaling is currently %s.", render_spu.fUnscaledHiDPI ? "OFF" : "ON"); + [self performSelector:@selector(setWantsBestResolutionOpenGLSurface:) withObject: (id)YES]; +#endif + + COCOA_LOG_FLOW(("%s: returns self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + return self; +} + +- (void)cleanupData +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + +#ifndef IN_VMSVGA3D + [self deleteDockTile]; +#endif + + [self setGLCtx:nil]; + + if (m_pSharedGLCtx) + { + if ([m_pSharedGLCtx view] == self) + [m_pSharedGLCtx clearDrawable]; + + [m_pSharedGLCtx release]; + m_pSharedGLCtx = nil; + + +#ifndef IN_VMSVGA3D + CrBltTerm(m_pBlitter); + RTMemFree(m_pBlitter); + m_pBlitter = nil; +#endif + } + + [self clearVisibleRegions]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)dealloc +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + [self cleanupData]; + [super dealloc]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)drawRect:(NSRect)aRect +{ + COCOA_LOG_FLOW(("%s: self=%p aRect=%d,%d %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)aRect.origin.x, (int)aRect.origin.y, + (int)aRect.size.width, (int)aRect.size.height)); + + [self vboxTryDrawUI]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)setGLCtx:(NSOpenGLContext *)pCtx +{ + COCOA_LOG_FLOW(("%s: self=%p pCtx=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCtx, m_pGLCtx)); + + /* + * Only do something if the context changes. + */ + if (m_pGLCtx != pCtx) + { + /* Ensure the context drawable is cleared to avoid holding a reference to inexistent view. */ + if (m_pGLCtx) + { +#ifdef IN_VMSVGA3D + Assert(!pCtx); +#endif + [m_pGLCtx clearDrawable]; + [m_pGLCtx release]; + /*[m_pGLCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ + } + + m_pGLCtx = pCtx; + if (pCtx) + [pCtx retain]; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSOpenGLContext *)glCtx +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pGLCtx)); + return m_pGLCtx; +} + +- (NSView *)parentView +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pParentView)); + return m_pParentView; +} + +- (void)setParentView:(NSView *)pView +{ + COCOA_LOG_FLOW(("%s: self=%p pView=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pView, m_pParentView)); + + m_pParentView = pView; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)setOverlayWin:(NSWindow *)pWin +{ + COCOA_LOG_FLOW(("%s: self=%p pWin=%p (old=%p)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pWin, m_pOverlayWin)); + + m_pOverlayWin = pWin; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSWindow *)overlayWin +{ + COCOA_LOG_FLOW(("%s: self=%p returns %p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pOverlayWin)); + return m_pOverlayWin; +} + +- (void)vboxSetPosUI:(NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y, + (int)m_Pos.x, (int)m_Pos.y)); + + DEBUG_MSG(("vboxSetPosUI: [%d, %d].\n", (int)pos.x, (int)pos.y)); + + m_Pos = pos; + + if (m_fEverSized) + [self vboxReshapePerform]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetPosUIObj:(NSValue *)pPos +{ + COCOA_LOG_FLOW(("%s: self=%p pPos=%p (%d,%d) (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, pPos, + (int)[pPos pointValue].x, (int)[pPos pointValue].y, (int)m_Pos.x, (int)m_Pos.y)); + + NSPoint pos = [pPos pointValue]; + [self vboxSetPosUI:pos]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetPos:(NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p pos=%d,%d (old pos=%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (int)pos.x, (int)pos.y, + (int)m_Pos.x, (int)m_Pos.y)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSValue *pPos = [NSValue valueWithPoint:pos]; + [pRunner addObj:self selector:@selector(vboxSetPosUIObj:) arg:pPos]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSPoint)pos +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)m_Pos.x, (int)m_Pos.y)); + return m_Pos; +} + +- (bool)isEverSized +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d\n", __PRETTY_FUNCTION__, (void *)self, m_fEverSized)); + return m_fEverSized; +} + +- (void)vboxDestroy +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + BOOL fIsMain = [NSThread isMainThread]; + NSWindow *pWin = nil; + + Assert(fIsMain); + + /* Hide the view early. */ + [self setHidden: YES]; + + pWin = [self window]; + [[NSNotificationCenter defaultCenter] removeObserver:pWin]; + [pWin setContentView: nil]; + [[pWin parentWindow] removeChildWindow: pWin]; + + if (fIsMain) + [pWin release]; + else + { + /* We can NOT run synchronously with the main thread since this may lead to a deadlock, + caused by main thread waiting xpcom thread, xpcom thread waiting to main hgcm thread, + and main hgcm thread waiting for us, this is why use waitUntilDone:NO, + which should cause no harm. */ + [pWin performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + } + + [self cleanupData]; + + if (fIsMain) + [self release]; + else + { + /* We can NOT run synchronously with the main thread since this may lead to a deadlock, + caused by main thread waiting xpcom thread, xpcom thread waiting to main hgcm thread, + and main hgcm thread waiting for us, this is why use waitUntilDone:NO. + We need to avoid concurrency though, so we cleanup some data right away via a cleanupData call. */ + [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; + } + +#ifndef IN_VMSVGA3D + renderspuWinRelease(m_pWinInfo); +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSizeUIObj:(NSValue *)pSize +{ + COCOA_LOG_FLOW(("%s: self=%p pSize=%p (%d,%d)\n", __PRETTY_FUNCTION__, (void *)self, (void *)pSize, + (int)[pSize sizeValue].width, (int)[pSize sizeValue].height)); + + NSSize size = [pSize sizeValue]; + [self vboxSetSizeUI:size]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSizeUI:(NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height)); + + m_Size = size; + m_fEverSized = true; + + DEBUG_MSG(("OVIW(%p): vboxSetSize: new size: %dx%d\n", (void *)self, (int)m_Size.width, (int)m_Size.height)); + [self vboxReshapeOnResizePerform]; + + /* ensure window contents is updated after that */ + [self vboxTryDrawUI]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetSize:(NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p size=%d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)size.width, (int)size.height)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSValue *pSize = [NSValue valueWithSize:size]; + [pRunner addObj:self selector:@selector(vboxSetSizeUIObj:) arg:pSize]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (NSSize)size +{ + COCOA_LOG_FLOW(("%s: self=%p returns %d,%d\n", __PRETTY_FUNCTION__, (void *)self, (int)m_Size.width, (int)m_Size.height)); + return m_Size; +} + +- (void)updateViewportCS +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + /* Update the viewport for our OpenGL view. */ + [m_pSharedGLCtx update]; + +#ifndef IN_VMSVGA3D + [self vboxBlitterSyncWindow]; +#endif + + /* Clear background to transparent. */ + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReshapeOnResizePerform +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + [self vboxReshapePerform]; +#ifndef IN_VMSVGA3D + [self createDockTile]; +#endif + + /* have to rebind GL_TEXTURE_RECTANGLE_ARB as m_FBOTexId could be changed in updateFBO call */ + m_fNeedViewportUpdate = true; +#if 0 + pCurCtx = [NSOpenGLContext currentContext]; + if (pCurCtx && pCurCtx == m_pGLCtx && (pCurView = [pCurCtx view]) == self) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } + else + { + /* do it in a lazy way */ + m_fNeedCtxUpdate = true; + } +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReshapeOnReparentPerform +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + [self vboxReshapePerform]; +#ifndef IN_VMSVGA3D + [self createDockTile]; +#endif + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifdef VBOX_WITH_CONFIGURABLE_HIDPI_SCALING +- (NSRect)safeConvertRectToBacking:(NSRect *)pRect +{ + NSRect resultingRect = NSZeroRect; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + if ([pWindow respondsToSelector:@selector(convertRectToBacking:)]) + { + NSMethodSignature *pSignature = [pWindow methodSignatureForSelector:@selector(convertRectToBacking:)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(convertRectToBacking:)]; + [pInvocation setTarget:pWindow]; + [pInvocation setArgument:pRect atIndex:2]; + [pInvocation invoke]; + [pInvocation getReturnValue:&resultingRect]; + + DEBUG_MSG(("safeConvertRectToBacking: convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; + } + } + } + } + else + /* Should never happen. */ + DEBUG_WARN(("safeConvertRectToBacking: parent widget has no window.\n")); + + resultingRect = *pRect; + + DEBUG_MSG(("safeConvertRectToBacking (reurn as is): convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; +} + + +- (CGFloat)safeGetBackingScaleFactor +{ + /* Assume its default value. */ + CGFloat backingScaleFactor = 1.; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + NSScreen *pScreen = [pWindow screen]; + if (pScreen) + { + if ([pScreen respondsToSelector:@selector(backingScaleFactor)]) + { + NSMethodSignature *pSignature = [pScreen methodSignatureForSelector:@selector(backingScaleFactor)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(backingScaleFactor)]; + [pInvocation setTarget:pScreen]; + [pInvocation invoke]; + [pInvocation getReturnValue:&backingScaleFactor]; + + DEBUG_MSG(("safeGetBackingScaleFactor: %d\n", (int)backingScaleFactor)); + + return backingScaleFactor; + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: unable to create invocation for backingScaleFactor method signature.\n")); + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: unable to create method signature for backingScaleFactor selector.\n")); + } + else + DEBUG_WARN(("safeGetBackingScaleFactor: NSScreen does not respond to backingScaleFactor selector.\n")); + } + else + /* Should never happen. */ + DEBUG_WARN(("safeGetBackingScaleFactor: parent window has no screen.\n")); + } + else + /* Should never happen. */ + DEBUG_WARN(("safeGetBackingScaleFactor: parent widget has no window.\n")); + + return backingScaleFactor; +} + +#endif + + +- (NSRect)safeConvertToScreen:(NSRect *)pRect +{ + NSRect resultingRect = NSZeroRect; + + NSWindow *pWindow = [m_pParentView window]; + if (pWindow) + { + if ([pWindow respondsToSelector:@selector(convertRectToScreen:)]) + { + NSMethodSignature *pSignature = [pWindow methodSignatureForSelector:@selector(convertRectToScreen:)]; + if (pSignature) + { + NSInvocation *pInvocation = [NSInvocation invocationWithMethodSignature:pSignature]; + if (pInvocation) + { + [pInvocation setSelector:@selector(convertRectToScreen:)]; + [pInvocation setTarget:pWindow]; + [pInvocation setArgument:pRect atIndex:2]; + [pInvocation invoke]; + [pInvocation getReturnValue:&resultingRect]; + + DEBUG_MSG(("safeConvertToScreen: convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; + } + } + } + + /* If we failed, let's use deprecated @selector(convertBaseToScreen:). It is a bit hacky, + * but what to do if we stick to SDK 10.6. */ + resultingRect.origin = [[m_pParentView window] convertBaseToScreen:pRect->origin]; + resultingRect.size = pRect->size; + } + else + /* Should never happen. */ + DEBUG_WARN(("safeConvertToScreen: parent widget has no window.\n")); + + DEBUG_MSG(("safeConvertToScreen (deprecated method): convert [X, Y, WxH]: [%d, %d, %dx%d] -> [%d, %d, %dx%d]\n", + (int)pRect ->origin.x, (int)pRect ->origin.y, (int)pRect ->size.width, (int)pRect ->size.width, + (int)resultingRect.origin.x, (int)resultingRect.origin.y, (int)resultingRect.size.width, (int)resultingRect.size.width)); + + return resultingRect; +} + +- (void)vboxReshapePerform +{ +#ifndef IN_VMSVGA3D + COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView)); +#else + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); +#endif + + /* NOTE: Please consider the next naming convention for variables. + * + * Rectangle variables: + * + * <object to represent><coordinate system>: + * <object to represent>: + * parentFrame - a frame of the parent container (NSView) object + * childFrame - a frame required to display guest content + * windowFrame - resulting window frame constructed as an intersection of parentFrame and childFrame + * <coordinate system>: + * VCS - View Coordinate System + * WCS - Window Coordinate System + * SCS - Screen Coordinate System + * + * The same convention applied to offset variables naming as well which are of format: + * + * <object to represent><coordinate><coordinate system>. + * + * https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html + */ + + NSRect parentFrameVCS, parentFrameWCS, parentFrameSCS; + NSRect childFrameWCS, childFrameSCS; + NSRect windowFrameSCS; + + CGFloat childFrameXWCS, childFrameYWCS; + + /* We need to construct a new window frame (windowFrameSCS) for entire NSWindow object in + * screen coordinates. In order to make 3D overlay window to do not overlap Cocoa and Qt GUI elements (titlebar, + * Qt statusbar, scroll bars etc) let's do the next. Get parent view visible area (parentFrameSCS) in (NS)Screen + * coordinates. Then get the area required to diaplay guest content (childFrameSCS) in (NS)Screen coordinates as well. + * The intersection of these two areas in screen coordinates will be a new frame for entire NSWindow object. */ + + parentFrameVCS = [m_pParentView frame]; + parentFrameWCS = [m_pParentView convertRect:parentFrameVCS toView:nil]; + parentFrameSCS = [self safeConvertToScreen:&parentFrameWCS]; + + /* Choose childFrame origin in a bit special way. Its pop-left corner should stick to its parent top-left corner. */ + childFrameXWCS = parentFrameWCS.origin.x + m_Pos.x; + childFrameYWCS = parentFrameWCS.origin.y - m_Pos.y - (m_Size.height - parentFrameWCS.size.height); + childFrameWCS = NSMakeRect(childFrameXWCS, childFrameYWCS, m_Size.width, m_Size.height); + childFrameSCS = [self safeConvertToScreen:&childFrameWCS]; + + windowFrameSCS = NSIntersectionRect(parentFrameSCS, childFrameSCS); + + DEBUG_MSG(("vboxReshapePerform: a new overlay frame [%d, %d, %dx%d] has been constructed from intersection of window frame " + "[%d, %d, %dx%d] and guest content rectangle [%d, %d, %dx%d]; m_Pos=[%d, %d], m_Size=%dx%d.\n", + (int)windowFrameSCS.origin.x, (int)windowFrameSCS.origin.y, (int)windowFrameSCS.size.width, (int)windowFrameSCS.size.width, + (int)parentFrameSCS.origin.x, (int)parentFrameSCS.origin.y, (int)parentFrameSCS.size.width, (int)parentFrameSCS.size.width, + (int)childFrameSCS .origin.x, (int)childFrameSCS .origin.y, (int)childFrameSCS .size.width, (int)childFrameSCS .size.width, + (int)m_Pos.x, (int)m_Pos.y, (int)m_Size.width, (int)m_Size.height)); + + /** @todo galitsyn: drop this! + * Later we have to correct the texture position in the case the window is + * out of the parents window frame. So save the shift values for later use. */ + m_RootRect.origin.x = windowFrameSCS.origin.x - childFrameSCS.origin.x; + m_RootRect.origin.y = childFrameSCS.size.height + childFrameSCS.origin.y - (windowFrameSCS.size.height + windowFrameSCS.origin.y); + m_RootRect.size = windowFrameSCS.size; + m_yInvRootOffset = windowFrameSCS.origin.y - childFrameSCS.origin.y; + + DEBUG_MSG(("vboxReshapePerform: [%#p]: m_RootRect pos[%d : %d] size[%d : %d]\n", + (void *)self, (int)m_RootRect.origin.x, (int)m_RootRect.origin.y, (int)m_RootRect.size.width, (int)m_RootRect.size.height)); + + /* Set the new frame. */ + [[self window] setFrame:windowFrameSCS display:YES]; + +#ifndef IN_VMSVGA3D + /* Inform the dock tile view as well. */ + [self reshapeDockTile]; +#endif + + /* Make sure the context is updated accordingly. */ + /* [self updateViewport]; */ + if (m_pSharedGLCtx) + { + VBOX_CR_RENDER_CTX_INFO CtxInfo; + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self updateViewportCS]; + + vboxCtxLeave(&CtxInfo); + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifndef IN_VMSVGA3D + +- (void)createDockTile +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + NSView *pDockScreen = nil; + + [self deleteDockTile]; + + /* Is there a dock tile preview enabled in the GUI? If so setup a + * additional thumbnail view for the dock tile. */ + pDockScreen = [self dockTileScreen]; + if (pDockScreen) + { + m_DockTileView = [[DockOverlayView alloc] init]; + [self reshapeDockTile]; + [pDockScreen addSubview:m_DockTileView]; + } + + COCOA_LOG_FLOW(("%s: returns - m_DockTileView\n", __PRETTY_FUNCTION__, (void *)m_DockTileView)); +} + +- (void)deleteDockTile +{ + COCOA_LOG_FLOW(("%s: self=%p - m_DockTileView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_DockTileView)); + + if (m_DockTileView != nil) + { + [m_DockTileView removeFromSuperview]; + [m_DockTileView release]; + m_DockTileView = nil; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#endif /* !IN_VMSVGA3D */ + +- (void)makeCurrentFBO +{ + COCOA_LOG_FLOW(("%s: self=%p - m_pGLCtx=%p m_fNeedCtxUpdate=%d\n", __PRETTY_FUNCTION__, (void *)self, + (void *)m_pGLCtx, m_fNeedCtxUpdate)); + + if (m_pGLCtx) + { + NSOpenGLContext *pPrevCtx = [NSOpenGLContext currentContext]; + +#ifdef IN_VMSVGA3D + /* Always flush before flush. glXMakeCurrent and wglMakeCurrent does this + implicitly, seemingly NSOpenGLContext::makeCurrentContext doesn't. */ + if (pPrevCtx != nil) + { + DEBUG_CLEAR_GL_ERRORS(); + glFlush(); + DEBUG_CHECK_GL_ERROR("glFlush"); + } +#endif + + if ([m_pGLCtx view] != self) + { +#ifndef IN_VMSVGA3D + /* We change the active view, so flush first */ + if (pPrevCtx != nil) + glFlush(); +#endif + DEBUG_CLEAR_GL_ERRORS(); + [m_pGLCtx setView: self]; + DEBUG_CHECK_GL_ERROR("setView"); + } + +#if 0 + if (pPrevCtx != m_pGLCtx) +#endif + { + DEBUG_CLEAR_GL_ERRORS(); + [m_pGLCtx makeCurrentContext]; + DEBUG_CHECK_GL_ERROR("makeCurrentContext"); + Assert([NSOpenGLContext currentContext] == m_pGLCtx); + Assert([m_pGLCtx view] == self); + } + + if (m_fNeedCtxUpdate == true) + { + [m_pGLCtx update]; + m_fNeedCtxUpdate = false; + } + + if (!m_FBOId) + { + glGenFramebuffersEXT(1, &m_FBOId); + Assert(m_FBOId); + } + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (bool)vboxSharedCtxCreate +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + if (m_pSharedGLCtx) + { + COCOA_LOG_FLOW(("%s: returns true (m_pSharedGLCtx=%p)\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx)); + return true; + } + +#ifndef IN_VMSVGA3D + Assert(!m_pBlitter); + m_pBlitter = RTMemAlloc(sizeof(*m_pBlitter)); + if (RT_UNLIKELY(!m_pBlitter)) + { + DEBUG_WARN(("m_pBlitter allocation failed")); + COCOA_LOG_FLOW(("%s: returns false - m_pBlitter allocation failed\n", __PRETTY_FUNCTION__)); + return false; + } + + int rc = CrBltInit(m_pBlitter, NULL, false /*fCreateNewCtx*/, false /*fForceDrawBlt*/, + &render_spu.GlobalShaders, &render_spu.blitterDispatch); + if (RT_FAILURE(rc)) + { + DEBUG_WARN(("CrBltInit failed, rc %d", rc)); + RTMemFree(m_pBlitter); + m_pBlitter = NULL; + + COCOA_LOG_FLOW(("%s: returns false - CrBltInit failed with rc=%Rrc\n", __PRETTY_FUNCTION__, rc)); + return false; + } + + COCOA_LOG_FLOW(("%s: blitter (%p) created successfully for view 0x%p\n", (void *)m_pBlitter, (void *)self)); +#endif /* !IN_VMSVGA3D */ + + /* Create a shared context out of the main context. Use the same pixel format. */ + NSOpenGLPixelFormat *pPixelFormat = [(OverlayOpenGLContext *)m_pGLCtx openGLPixelFormat]; + NSOpenGLContext *pSharedGLCtx = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:m_pGLCtx]; + + /* Set the new context as non opaque */ + GLint opaque = 0; + [pSharedGLCtx setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + + /* Set this view as the drawable for the new context */ + [pSharedGLCtx setView:self]; + m_fNeedViewportUpdate = true; + + m_pSharedGLCtx = pSharedGLCtx; + + COCOA_LOG_FLOW(("%s: returns true - new m_pSharedGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)m_pSharedGLCtx)); + return true; +} + +- (void)vboxTryDraw +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + glFlush(); + + /* Issue to the gui thread. */ + [self performSelectorOnMainThread:@selector(vboxTryDrawUI) withObject:nil waitUntilDone:NO]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisible:(GLboolean)fVisible +{ + COCOA_LOG_FLOW(("%s: self=%p fVisible=%d\n", __PRETTY_FUNCTION__, (void *)self, fVisible)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + NSNumber *pVisObj = [NSNumber numberWithBool:fVisible]; + [pRunner addObj:self selector:@selector(vboxSetVisibleUIObj:) arg:pVisObj]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisibleUI:(GLboolean)fVisible +{ + COCOA_LOG_FLOW(("%s: self=%p fVisible=%d\n", __PRETTY_FUNCTION__, (void *)self, fVisible)); + + [self setHidden: !fVisible]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxSetVisibleUIObj:(NSNumber *)pVisibleObj +{ + COCOA_LOG_FLOW(("%s: self=%p pVisibleObj=%p(%d)\n", __PRETTY_FUNCTION__, + (void *)self, (void *)pVisibleObj, [pVisibleObj boolValue])); + + BOOL fVisible = [pVisibleObj boolValue]; + [self vboxSetVisibleUI:fVisible]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReparent:(NSView *)pParentView +{ + COCOA_LOG_FLOW(("%s: self=%p pParentView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pParentView)); + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner addObj:self selector:@selector(vboxReparentUI:) arg:pParentView]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxReparentUI:(NSView *)pParentView +{ + COCOA_LOG_FLOW(("%s: self=%p pParentView=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pParentView)); + + /* Make sure the window is removed from any previous parent window. */ + if ([[self overlayWin] parentWindow] != nil) + { + [[[self overlayWin] parentWindow] removeChildWindow:[self overlayWin]]; + } + + /* Set the new parent view */ + [self setParentView: pParentView]; + + /* Add the overlay window as a child to the new parent window */ + if (pParentView != nil) + { + [[pParentView window] addChildWindow:[self overlayWin] ordered:NSWindowAbove]; + if ([self isEverSized]) + [self vboxReshapeOnReparentPerform]; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxTryDrawUI +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + + if ([self isHidden]) + { + COCOA_LOG_FLOW(("%s: returns - request to draw on a hidden view\n", __PRETTY_FUNCTION__)); + return; + } + + if ([[self overlayWin] parentWindow] == nil) + { + COCOA_LOG_FLOW(("%s: returns - request to draw a view w/o a parent\n", __PRETTY_FUNCTION__)); + return; + } + +#ifdef IN_VMSVGA3D + if (!m_pSharedGLCtx) + { + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + if (![self vboxSharedCtxCreate]) + { + COCOA_LOG_FLOW(("%s: returns - vboxSharedCtxCreate failed\n", __PRETTY_FUNCTION__)); + return; + } + Assert(m_pSharedGLCtx); + } +#endif + + const VBOXVR_SCR_COMPOSITOR *pCompositor = NULL; +#ifndef IN_VMSVGA3D + int rc = renderspuVBoxCompositorLock(m_pWinInfo, &pCompositor); + if (RT_FAILURE(rc)) + { + COCOA_LOG_FLOW(("%s: returns - renderspuVBoxCompositorLock failed (%Rrc)\n", __PRETTY_FUNCTION__, rc)); + return; + } + + if (!pCompositor && !m_fCleanupNeeded) + { + renderspuVBoxCompositorUnlock(m_pWinInfo); + COCOA_LOG_FLOW(("%s: returns - noCompositorUI\n", __PRETTY_FUNCTION__)); + return; + } + + VBOXVR_SCR_COMPOSITOR TmpCompositor; + if (pCompositor) + { + if (!m_pSharedGLCtx) + { + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + renderspuVBoxCompositorRelease(m_pWinInfo); + if (![self vboxSharedCtxCreate]) + { + COCOA_LOG_FLOW(("%s: returns - vboxSharedCtxCreate failed\n", __PRETTY_FUNCTION__)); + return; + } + + Assert(m_pSharedGLCtx); + + pCompositor = renderspuVBoxCompositorAcquire(m_pWinInfo); + Assert(!m_fDataVisible); + Assert(!m_fCleanupNeeded); + if (!pCompositor) + { + COCOA_LOG_FLOW(("%s: returns - Failed to reacquire compositor\n", __PRETTY_FUNCTION__)); + return; + } + } + } + else + { + DEBUG_MSG(("%s: NeedCleanup\n", __PRETTY_FUNCTION__)); + Assert(m_fCleanupNeeded); + CrVrScrCompositorInit(&TmpCompositor, NULL); + pCompositor = &TmpCompositor; + } +#endif /* !IN_VMSVGA3D */ + + + if ([self lockFocusIfCanDraw]) + { + COCOA_LOG_FLOW(("%s: Calling vboxPresent\n", __PRETTY_FUNCTION__)); + [self vboxPresent:pCompositor]; + [self unlockFocus]; + } +#ifndef IN_VMSVGA3D /** @todo VMSVGA3 */ + else if (!m_pWinInfo->visible) + { + COCOA_LOG_FLOW(("%s: NotVisible\n", __PRETTY_FUNCTION__)); + m_fCleanupNeeded = false; + } +#endif + else + { + COCOA_LOG_FLOW(("%s: Reschedule\n", __PRETTY_FUNCTION__)); + [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(vboxTryDrawUI) userInfo:nil repeats:NO]; + } + +#ifndef IN_VMSVGA3D + renderspuVBoxCompositorUnlock(m_pWinInfo); +#endif + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)swapFBO +{ + COCOA_LOG_FLOW(("%s: self=%p - m_pGLCtx=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)m_pGLCtx)); + [m_pGLCtx flushBuffer]; + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxPresent:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + /*DEBUG_MSG(("OVIW(%p): renderFBOToView\n", (void *)self));*/ +#ifndef IN_VMSVGA3D + AssertPtr(pCompositor); +#endif + + VBOX_CR_RENDER_CTX_INFO CtxInfo; + vboxCtxEnter(m_pSharedGLCtx, &CtxInfo); + + [self vboxPresentCS:pCompositor]; + + vboxCtxLeave(&CtxInfo); + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)vboxPresentCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + if ([m_pSharedGLCtx view] != self) + { + COCOA_LOG_FLOW(("%s: Not current view of shared ctx! Switching... (self=%p, view=%p, m_pSharedGLCtx)\n", + __PRETTY_FUNCTION__, (void *)self, (void *)[m_pSharedGLCtx view], (void *)m_pSharedGLCtx)); + [m_pSharedGLCtx setView: self]; + m_fNeedViewportUpdate = true; + } + + if (m_fNeedViewportUpdate) + { + [self updateViewportCS]; + m_fNeedViewportUpdate = false; + } + + m_fCleanupNeeded = false; + +#ifndef IN_VMSVGA3D + /* Render FBO content to the dock tile when necessary. */ + [self vboxPresentToDockTileCS:pCompositor]; +#endif + + /* change to #if 0 to see thumbnail image */ +#if 1 + [self vboxPresentToViewCS:pCompositor]; +#else + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + [m_pSharedGLCtx flushBuffer]; +#endif + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +DECLINLINE(void) vboxNSRectToRect(const NSRect *pR, RTRECT *pRect) +{ + pRect->xLeft = (int)pR->origin.x; + pRect->yTop = (int)pR->origin.y; + pRect->xRight = (int)(pR->origin.x + pR->size.width); + pRect->yBottom = (int)(pR->origin.y + pR->size.height); +} + +DECLINLINE(void) vboxNSRectToRectUnstretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) +{ + pRect->xLeft = (int)(pR->origin.x / xStretch); + pRect->yTop = (int)(pR->origin.y / yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) / xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) / yStretch); +} + +DECLINLINE(void) vboxNSRectToRectStretched(const NSRect *pR, RTRECT *pRect, float xStretch, float yStretch) +{ + pRect->xLeft = (int)(pR->origin.x * xStretch); + pRect->yTop = (int)(pR->origin.y * yStretch); + pRect->xRight = (int)((pR->origin.x + pR->size.width) * xStretch); + pRect->yBottom = (int)((pR->origin.y + pR->size.height) * yStretch); +} + +- (void)vboxPresentToViewCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + NSRect r = [self frame]; + COCOA_LOG_FLOW(("%s: self=%p - r={%d,%d %d,%d}\n", __PRETTY_FUNCTION__, (void *)self, + (int)r.origin.x, (int)r.origin.y, (int)r.size.width, (int)r.size.height)); + +#if 1 /* Set to 0 to see the docktile instead of the real output */ + float backingStretchFactor = 1.; +# if defined(VBOX_WITH_CONFIGURABLE_HIDPI_SCALING) && !defined(IN_VMSVGA3D) + /* Adjust viewport according to current NSView's backing store parameters. */ + if (render_spu.fUnscaledHiDPI) + { + /* Update stretch factor in order to satisfy current NSView's backing store parameters. */ + backingStretchFactor = [self safeGetBackingScaleFactor]; + } + + NSRect regularBounds = [self bounds]; + NSRect backingBounds = [self safeConvertRectToBacking:®ularBounds]; + glViewport(0, 0, backingBounds.size.width, backingBounds.size.height); + + //crDebug("HiDPI: vboxPresentToViewCS: up-scaling is %s (backingStretchFactor=%d).", + // render_spu.fUnscaledHiDPI ? "OFF" : "ON", (int)backingStretchFactor); +# endif + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + + /* Clear background to transparent */ + glClear(GL_COLOR_BUFFER_BIT); + + m_fDataVisible = false; + +# ifndef IN_VMSVGA3D + float xStretch; + float yStretch; + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + const RTRECT *paSrcRegions, *paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + const CR_TEXDATA *pTexData; + PCRTRECT pSrcRect = &paSrcRegions[i]; + PCRTRECT pDstRect = &paDstRegions[i]; + RTRECT DstRect, RestrictDstRect; + RTRECT SrcRect, RestrictSrcRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + if (VBoxRectIsZero(&DstRect)) + continue; + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch / backingStretchFactor, yStretch / backingStretchFactor); + VBoxRectTranslate(&RestrictSrcRect, + -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags | CRBLT_F_NOALPHA); + + m_fDataVisible = true; + } + CrBltLeave(m_pBlitter); + } + else + { + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); +# ifndef DEBUG_VERBOSE + AssertMsgFailed(("CrBltEnter failed rc %Rrc", rc)); +# endif + } + } + else + { + AssertMsgFailed(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %Rrc\n", rc)); + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + } + } +# endif /* !IN_VMSVGA3D */ +#endif + + /* + glFinish(); + */ + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + [m_pSharedGLCtx flushBuffer]; + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +- (void)presentComposition:(PCVBOXVR_SCR_COMPOSITOR_ENTRY)pChangedEntry +{ + COCOA_LOG_FLOW(("%s: self=%p pChangedEntry=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pChangedEntry)); + [self vboxTryDraw]; +} + +#ifndef IN_VMSVGA3D +- (void)vboxBlitterSyncWindow +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + CR_BLITTER_WINDOW WinInfo; + NSRect r; + + if (!m_pBlitter) + return; + + RT_ZERO(WinInfo); + + r = [self frame]; + WinInfo.width = r.size.width; + WinInfo.height = r.size.height; + + Assert(WinInfo.width == m_RootRect.size.width); + Assert(WinInfo.height == m_RootRect.size.height); + + /*CrBltMuralSetCurrentInfo(m_pBlitter, NULL);*/ + + CrBltMuralSetCurrentInfo(m_pBlitter, &WinInfo); + CrBltCheckUpdateViewport(m_pBlitter); +} +#endif /* !IN_VMSVGA3D */ + +#ifndef IN_VMSVGA3D +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL +static int g_cVBoxTgaCtr = 0; +# endif +- (void)vboxPresentToDockTileCS:(PCVBOXVR_SCR_COMPOSITOR)pCompositor +{ + COCOA_LOG_FLOW(("%s: self=%p pCompositor=%p\n", __PRETTY_FUNCTION__, (void *)self, (void *)pCompositor)); + NSRect r = [self frame]; + NSRect rr = NSZeroRect; + NSDockTile *pDT = nil; + float xStretch; + float yStretch; + + if ([m_DockTileView thumbBitmap] != nil) + { + /* + * Only update after at least 200 ms, cause glReadPixels is + * heavy performance wise. + */ + uint64_t msTS = RTTimeSystemMilliTS(); + VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter; + const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry; + + if (msTS - m_msDockUpdateTS > 200) + { + m_msDockUpdateTS = msTS; +# if 0 + /** @todo check this for optimization */ + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, myTextureName); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_STORAGE_HINT_APPLE, + GL_STORAGE_SHARED_APPLE); + glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, GL_TRUE); + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, + sizex, sizey, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, myImagePtr); + glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, + 0, 0, 0, 0, 0, image_width, image_height); + glFlush(); + /* Do other work processing here, using a double or triple buffer */ + glGetTexImage(GL_TEXTURE_RECTANGLE_ARB, 0, GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, pixels); +# endif + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glDrawBuffer(GL_BACK); + + /* Clear background to transparent */ + glClear(GL_COLOR_BUFFER_BIT); + + rr = [m_DockTileView frame]; + + CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch); + + CrVrScrCompositorConstIterInit(pCompositor, &CIter); + while ((pEntry = CrVrScrCompositorConstIterNext(&CIter)) != NULL) + { + uint32_t cRegions; + PCRTRECT paSrcRegions; + PCRTRECT paDstRegions; + int rc = CrVrScrCompositorEntryRegionsGet(pCompositor, pEntry, &cRegions, &paSrcRegions, &paDstRegions, NULL); + uint32_t fFlags = CrVrScrCompositorEntryFlagsCombinedGet(pCompositor, pEntry); + if (RT_SUCCESS(rc)) + { + rc = CrBltEnter(m_pBlitter); + if (RT_SUCCESS(rc)) + { + uint32_t i; + for (i = 0; i < cRegions; ++i) + { + const CR_TEXDATA *pTexData; + PCRTRECT pSrcRect = &paSrcRegions[i]; + PCRTRECT pDstRect = &paDstRegions[i]; + RTRECT DstRect, RestrictDstRect; + RTRECT SrcRect, RestrictSrcRect; + + vboxNSRectToRect(&m_RootRect, &RestrictDstRect); + VBoxRectIntersected(&RestrictDstRect, pDstRect, &DstRect); + + VBoxRectTranslate(&DstRect, -RestrictDstRect.xLeft, -RestrictDstRect.yTop); + + VBoxRectScale(&DstRect, m_FBOThumbScaleX, m_FBOThumbScaleY); + + if (VBoxRectIsZero(&DstRect)) + continue; + + vboxNSRectToRectUnstretched(&m_RootRect, &RestrictSrcRect, xStretch, yStretch); + VBoxRectTranslate(&RestrictSrcRect, + -CrVrScrCompositorEntryRectGet(pEntry)->xLeft, + -CrVrScrCompositorEntryRectGet(pEntry)->yTop); + VBoxRectIntersected(&RestrictSrcRect, pSrcRect, &SrcRect); + + if (VBoxRectIsZero(&SrcRect)) + continue; + + pSrcRect = &SrcRect; + pDstRect = &DstRect; + + pTexData = CrVrScrCompositorEntryTexGet(pEntry); + + CrBltBlitTexMural(m_pBlitter, true, CrTdTexGet(pTexData), pSrcRect, pDstRect, 1, fFlags); + } + CrBltLeave(m_pBlitter); + } + else + { + DEBUG_WARN(("CrBltEnter failed rc %d", rc)); +# ifndef DEBUG_VERBOSE + AssertMsgFailed(("CrBltEnter failed rc %Rrc", rc)); +# endif + } + } + else + { + DEBUG_MSG_1(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %d\n", rc)); + AssertMsgFailed(("BlitStretched: CrVrScrCompositorEntryRegionsGet failed rc %Rrc\n", rc)); + } + } + + glFinish(); + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + glReadBuffer(GL_BACK); + + /* Here the magic of reading the FBO content in our own buffer + * happens. We have to lock this access, in the case the dock + * is updated currently. */ + [m_DockTileView lock]; + glReadPixels(0, m_RootRect.size.height - rr.size.height, rr.size.width, rr.size.height, + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8, + [[m_DockTileView thumbBitmap] bitmapData]); + [m_DockTileView unlock]; + +# ifdef VBOX_WITH_CRDUMPER_THUMBNAIL + ++g_cVBoxTgaCtr; + crDumpNamedTGAF((GLint)rr.size.width, (GLint)rr.size.height, + [[m_DockTileView thumbBitmap] bitmapData], "/Users/leo/vboxdumps/dump%d.tga", g_cVBoxTgaCtr); +# endif + + pDT = [[NSApplication sharedApplication] dockTile]; + + /* Send a display message to the dock tile in the main thread */ + [[[NSApplication sharedApplication] dockTile] performSelectorOnMainThread:@selector(display) withObject:nil + waitUntilDone:NO]; + } + } +} +#endif /* !IN_VMSVGA3D */ + +- (void)clearVisibleRegions +{ + if (m_paClipRects) + { + RTMemFree(m_paClipRects); + m_paClipRects = NULL; + } + m_cClipRects = 0; +} + +- (GLboolean)vboxNeedsEmptyPresent +{ + if (m_fDataVisible) + { + m_fCleanupNeeded = true; + return GL_TRUE; + } + + return GL_FALSE; +} + +- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint *)paRects +{ + COCOA_LOG_FLOW(("%s: self=%p cRects=%d paRects=%p\n", __PRETTY_FUNCTION__, (void *)self, cRects, (void *)paRects)); + GLint cOldRects = m_cClipRects; + + [self clearVisibleRegions]; + + if (cRects > 0) + { +#ifdef DEBUG_poetzsch + int i = 0; + for (i = 0; i < cRects; ++i) + DEBUG_MSG_1(("OVIW(%p): setVisibleRegions: %d - %d %d %d %d\n", (void *)self, i, paRects[i * 4], paRects[i * 4 + 1], paRects[i * 4 + 2], paRects[i * 4 + 3])); +#endif + + m_paClipRects = (GLint *)RTMemDup(paRects, sizeof(GLint) * 4 * cRects); + m_cClipRects = cRects; + } + + COCOA_LOG_FLOW(("%s: returns\n", __PRETTY_FUNCTION__)); +} + +#ifndef IN_VMSVGA3D + +- (NSView *)dockTileScreen +{ + COCOA_LOG_FLOW(("%s: self=%p\n", __PRETTY_FUNCTION__, (void *)self)); + NSView *pContentView = [[[NSApplication sharedApplication] dockTile] contentView]; + NSView *pScreenContent = nil; + + /* + * First try the new variant which checks if this window is within the + * screen which is previewed in the dock. + */ + if ([pContentView respondsToSelector:@selector(screenContentWithParentView:)]) + pScreenContent = [pContentView performSelector:@selector(screenContentWithParentView:) withObject:(id)m_pParentView]; + /* + * If it fails, fall back to the old variant (VBox...). + */ + else if ([pContentView respondsToSelector:@selector(screenContent)]) + pScreenContent = [pContentView performSelector:@selector(screenContent)]; + + COCOA_LOG_FLOW(("%s: returns %p (pContentView=%p)\n", __PRETTY_FUNCTION__, (void *)pScreenContent, (void *)pContentView)); + return pScreenContent; +} + +- (void)reshapeDockTile +{ + COCOA_LOG_FLOW(("%s:\n", __PRETTY_FUNCTION__)); + NSRect newFrame = NSZeroRect; + NSView *pView = [self dockTileScreen]; + if (pView != nil) + { + NSRect dockFrame = [pView frame]; + /** @todo This is not correct, we should use framebuffer size here, while + * parent view frame size may differ in case of scrolling. */ + NSRect parentFrame = [m_pParentView frame]; + + m_FBOThumbScaleX = (float)dockFrame.size.width / parentFrame.size.width; + m_FBOThumbScaleY = (float)dockFrame.size.height / parentFrame.size.height; + newFrame = NSMakeRect((int)(m_Pos.x * m_FBOThumbScaleX), + (int)(dockFrame.size.height - (m_Pos.y + m_Size.height - m_yInvRootOffset) * m_FBOThumbScaleY), + (int)(m_Size.width * m_FBOThumbScaleX), + (int)(m_Size.height * m_FBOThumbScaleY)); + /* + NSRect newFrame = NSMakeRect ((int)roundf(m_Pos.x * m_FBOThumbScaleX), (int)roundf(dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (int)roundf(m_Size.width * m_FBOThumbScaleX), (int)roundf(m_Size.height * m_FBOThumbScaleY)); + NSRect newFrame = NSMakeRect ((m_Pos.x * m_FBOThumbScaleX), (dockFrame.size.height - (m_Pos.y + m_Size.height) * m_FBOThumbScaleY), (m_Size.width * m_FBOThumbScaleX), (m_Size.height * m_FBOThumbScaleY)); + printf ("%f %f %f %f - %f %f\n", newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height, m_Size.height, m_FBOThumbScaleY); + */ + [m_DockTileView setFrame: newFrame]; + } + COCOA_LOG_FLOW(("%s: returns - newFrame={%d,%d %d,%d} pView=%d\n", __PRETTY_FUNCTION__, (int)newFrame.origin.x, + (int)newFrame.origin.y, (int)newFrame.size.width, (int)newFrame.size.height, (void *)pView)); +} + +#endif /* !IN_VMSVGA3D */ + +@end /* @implementation OverlayView */ + + +/******************************************************************************** +* +* OpenGL context management +* +********************************************************************************/ +void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx) +{ + COCOA_LOG_FLOW(("cocoaGLCtxCreate: ppCtx=%p fVisParams=%#x pSharedCtx=%p\n", (void *)ppCtx, fVisParams, (void *)pSharedCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + NSOpenGLPixelFormat *pFmt = vboxCreatePixelFormat(fVisParams); + if (pFmt) + { + *ppCtx = [[OverlayOpenGLContext alloc] initWithFormat:pFmt shareContext:pSharedCtx]; + Assert(*ppCtx); + + /* Enable multi threaded OpenGL engine */ + /* + CGLContextObj cglCtx = [*ppCtx CGLContextObj]; + CGLError err = CGLEnable(cglCtx, kCGLCEMPEngine); + if (err != kCGLNoError) + printf ("Couldn't enable MT OpenGL engine!\n"); + */ + } + else + { + AssertFailed(); + *ppCtx = NULL; + } + + [pPool release]; + COCOA_LOG_FLOW(("cocoaGLCtxCreate: returns *ppCtx=%p\n", (void *)*ppCtx)); +} + +void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx) +{ + COCOA_LOG_FLOW(("cocoaGLCtxDestroy: pCtx=%p\n", (void *)pCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [pCtx release]; + /*[pCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/ + + [pPool release]; + COCOA_LOG_FLOW(("cocoaGLCtxDestroy: returns\n")); +} + +/******************************************************************************** +* +* View management +* +********************************************************************************/ +static OverlayView *vboxViewCreate(WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams) +{ + COCOA_LOG_FLOW(("vboxViewCreate: pWinInfo=%p pParentView=%p fVisParams=%#x\n", pWinInfo, (void *)pParentView, fVisParams)); + + /* Create our worker view. */ + OverlayView *pView = [[OverlayView alloc] initWithFrame:NSZeroRect + thread:RTThreadSelf() + parentView:pParentView + winInfo:pWinInfo + fVisParams:fVisParams]; + + if (pView) + { + /* We need a real window as container for the view */ + [[OverlayWindow alloc] initWithParentView:pParentView overlayView:pView]; + /* Return the freshly created overlay view */ + COCOA_LOG_FLOW(("vboxViewCreate: returns %p\n", (void *)pView)); + return pView; + } + + COCOA_LOG_FLOW(("vboxViewCreate: returns NULL\n")); + return NULL; +} + +#ifndef IN_VMSVGA3D + +typedef struct CR_RCD_CREATEVIEW +{ + WindowInfo *pWinInfo; + NSView *pParentView; + GLbitfield fVisParams; + /* out */ + OverlayView *pView; +} CR_RCD_CREATEVIEW; + +static DECLCALLBACK(void) vboxRcdCreateView(void *pvCb) +{ + CR_RCD_CREATEVIEW *pCreateView = (CR_RCD_CREATEVIEW *)pvCb; + pCreateView->pView = vboxViewCreate(pCreateView->pWinInfo, pCreateView->pParentView, pCreateView->fVisParams); + COCOA_LOG_FLOW(("vboxRcdCreateView: returns pView=%p\n", (void *)pCreateView->pView)); +} + +#endif /* !IN_VMSVGA3D */ + +void cocoaViewCreate(NativeNSViewRef *ppView, WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams) +{ + COCOA_LOG_FLOW(("cocoaViewCreate: ppView=%p pWinInfo=%p pParentView=%p fVisParams=%#x\n", + (void *)ppView, (void *)pWinInfo, (void *)pParentView, fVisParams)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + /* make sure all tasks are run, to preserve the order */ + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner runTasksSyncIfPossible]; + +#ifndef IN_VMSVGA3D + renderspuWinRetain(pWinInfo); + + if (renderspuCalloutAvailable()) + { + CR_RCD_CREATEVIEW CreateView; + CreateView.pWinInfo = pWinInfo; + CreateView.pParentView = pParentView; + CreateView.fVisParams = fVisParams; + CreateView.pView = NULL; + renderspuCalloutClient(vboxRcdCreateView, &CreateView); + *ppView = CreateView.pView; + } + else +#endif + { + DEBUG_MSG_NOT_VMSVGA3D(("no callout available on createWindow\n")); +#if 0 + dispatch_sync(dispatch_get_main_queue(), ^{ +#endif + *ppView = vboxViewCreate(pWinInfo, pParentView, fVisParams); +#if 0 + }); +#endif + } + +#ifndef IN_VMSVGA3D + if (!*ppView) + renderspuWinRelease(pWinInfo); +#endif + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewCreate: returns *ppView=%p\n", (void *)*ppView)); +} + +#ifndef IN_VMSVGA3D +void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView) +{ + COCOA_LOG_FLOW(("cocoaViewReparent: pView=%p pParentView=%p\n", (void *)pView, (void *)pParentView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + OverlayView *pOView = (OverlayView *)pView; + if (pOView) + [pOView vboxReparent:pParentView]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewReparent: returns\n")); +} +#endif /* !IN_VMSVGA3D */ + +void cocoaViewDestroy(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewDestroy: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner addObj:pView selector:@selector(vboxDestroy) arg:nil]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewDestroy: returns\n")); +} + +#ifndef IN_VMSVGA3D +void cocoaViewShow(NativeNSViewRef pView, GLboolean fShowIt) +{ + COCOA_LOG_FLOW(("cocoaViewShow: pView=%p fShowIt=%d\n", (void *)pView, fShowIt)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView vboxSetVisible:fShowIt]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewShow: returns\n")); +} +#endif /* IN_VMSVGA3D */ + +void cocoaViewDisplay(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewDisplay: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + +#ifndef IN_VMSVGA3D + DEBUG_WARN(("cocoaViewDisplay should never happen!\n")); + DEBUG_MSG_1(("cocoaViewDisplay %p\n", (void *)pView)); +#endif + [(OverlayView *)pView swapFBO]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewDisplay: returns\n")); +} + +void cocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y) +{ + COCOA_LOG_FLOW(("cocoaViewSetPosition: pView=%p pParentView=%p x=%d y=%d\n", (void *)pView, (void *)pParentView, x, y)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView vboxSetPos:NSMakePoint(x, y)]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetPosition: returns\n")); +} + +void cocoaViewSetSize(NativeNSViewRef pView, int cx, int cy) +{ + COCOA_LOG_FLOW(("cocoaViewSetSize: pView=%p cx=%d cy=%d\n", (void *)pView, cx, cy)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + OverlayView *pOverlayView = (OverlayView *)pView; + + [pOverlayView vboxSetSize:NSMakeSize(cx, cy)]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetSize: returns\n")); +} + +#ifndef IN_VMSVGA3D + +typedef struct CR_RCD_GETGEOMETRY +{ + OverlayView *pView; + NSRect rect; +} CR_RCD_GETGEOMETRY; + +static DECLCALLBACK(void) vboxRcdGetGeomerty(void *pvUser) +{ + CR_RCD_GETGEOMETRY *pGetGeometry = (CR_RCD_GETGEOMETRY *)pvUser; + pGetGeometry->rect = [[pGetGeometry->pView window] frame]; + COCOA_LOG_FLOW(("vboxRcdGetGeomerty: (x,y)=(%d,%d) (cx,cy)=(%d,%d)\n", pGetGeometry->rect.origin.x, pGetGeometry->rect.origin.y, + pGetGeometry->rect.size.width, pGetGeometry->rect.size.height)); +} + +void cocoaViewGetGeometry(NativeNSViewRef pView, int *px, int *py, int *pcx, int *pcy) +{ + COCOA_LOG_FLOW(("cocoaViewGetGeometry: pView=%p px=%p py=%p pcx=%p pcy=%p\n", + (void *)pView, (void *)px, (void *)py, (void *)pcx, (void *)pcy)); + NSAutoreleasePool *pPool; + pPool = [[NSAutoreleasePool alloc] init]; + + /* make sure all tasks are run, to preserve the order */ + VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance]; + [pRunner runTasksSyncIfPossible]; + + NSRect frame; +#ifndef IN_VMSVGA3D + if (renderspuCalloutAvailable()) + { + CR_RCD_GETGEOMETRY GetGeometry; + GetGeometry.pView = (OverlayView *)pView; + renderspuCalloutClient(vboxRcdGetGeomerty, &GetGeometry); + frame = GetGeometry.rect; + } + else +#endif + { + DEBUG_MSG_NOT_VMSVGA3D(("no callout available on getGeometry\n")); + frame = [[pView window] frame]; + } + + *px = frame.origin.x; + *py = frame.origin.y; + *pcx = frame.size.width; + *pcy = frame.size.height; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewGetGeometry: returns *px=%d, *py=%d, *pcx=%d, *pcy=%d\n", *px, *py, *pcx, *pcy)); +} + +void cocoaViewPresentComposition(NativeNSViewRef pView, PCVBOXVR_SCR_COMPOSITOR_ENTRY pChangedEntry) +{ + COCOA_LOG_FLOW(("cocoaViewPresentComposition: pView=%p pChangedEntry=%p\n", (void *)pView, (void *)pChangedEntry)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + NSOpenGLContext *pCtx; + +# ifdef IN_VMSVGA3D + Assert([(OverlayView *)pView glCtx]); + +# else + /* The view may not necesserily have a GL context set. */ + pCtx = [(OverlayView *)pView glCtx]; + if (!pCtx) + { + ContextInfo *pCtxInfo = renderspuDefaultSharedContextAcquire(); + if (!pCtxInfo) + { + DEBUG_WARN(("renderspuDefaultSharedContextAcquire returned NULL")); + + [pPool release]; + DEBUG_FUNC_LEAVE(); + return; + } + + pCtx = pCtxInfo->context; + + [(OverlayView *)pView setGLCtx:pCtx]; + } +# endif + + [(OverlayView *)pView presentComposition:pChangedEntry]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewPresentComposition: returns\n")); +} + +#endif /* !IN_VMSVGA3D */ + +void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + COCOA_LOG_FLOW(("cocoaViewMakeCurrentContext: pView=%p pCtx=%p\n", (void *)pView, (void *)pCtx)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + if (pView) + { + [(OverlayView *)pView setGLCtx:pCtx]; + [(OverlayView *)pView makeCurrentFBO]; + } + else + { +#ifdef IN_VMSVGA3D + /* Always flush before flush. glXMakeCurrent and wglMakeCurrent does this + implicitly, seemingly NSOpenGLContext::makeCurrentContext doesn't. */ + if ([NSOpenGLContext currentContext] != nil) + { + DEBUG_CLEAR_GL_ERRORS(); + glFlush(); + DEBUG_CHECK_GL_ERROR("glFlush"); + } +#endif + [NSOpenGLContext clearCurrentContext]; + } + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewMakeCurrentContext: returns\n")); +} + +#ifndef IN_VMSVGA3D + +GLboolean cocoaViewNeedsEmptyPresent(NativeNSViewRef pView) +{ + COCOA_LOG_FLOW(("cocoaViewNeedsEmptyPresent: pView=%p\n", (void *)pView)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + GLboolean fNeedsPresent = [(OverlayView *)pView vboxNeedsEmptyPresent]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewNeedsEmptyPresent: returns %d\n", fNeedsPresent)); + return fNeedsPresent; +} + +void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint *paRects) +{ + COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: pView=%p cRects=%d paRects=%p)\n", (void *)pView, cRects, (void const *)paRects)); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + [(OverlayView *)pView setVisibleRegions:cRects paRects:paRects]; + + [pPool release]; + COCOA_LOG_FLOW(("cocoaViewSetVisibleRegion: returns\n")); +} + +#endif /* IN_VMSVGA3D */ + +#ifdef IN_VMSVGA3D +/* + * VMSVGA3D interface. + */ + +VMSVGA3DCOCOA_DECL(bool) vmsvga3dCocoaCreateViewAndContext(NativeNSViewRef *ppView, NativeNSOpenGLContextRef *ppCtx, + NativeNSViewRef pParentView, uint32_t cx, uint32_t cy, + NativeNSOpenGLContextRef pSharedCtx, bool fOtherProfile) +{ + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + GLbitfield fVisParams = CR_ALPHA_BIT | CR_DEPTH_BIT | CR_DOUBLE_BIT | (fOtherProfile ? VMSVGA3D_NON_DEFAULT_PROFILE_BIT : 0); + bool fRc = false; + + cocoaGLCtxCreate(ppCtx, fVisParams, pSharedCtx); + if (*ppCtx) + { + cocoaViewCreate(ppView, NULL, pParentView, fVisParams); + if (*ppView) + { + cocoaViewSetSize(*ppView, cx, cy); + [(OverlayView *)*ppView setGLCtx: *ppCtx]; + fRc = true; + } + else + [*ppCtx release]; + } + + [pPool release]; + return fRc; +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaDestroyViewAndContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + cocoaGLCtxDestroy(pCtx); + cocoaViewDestroy(pView); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y) +{ + cocoaViewSetPosition(pView, pParentView, x, y); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewSetSize(NativeNSViewRef pView, int w, int h) +{ + cocoaViewSetSize(pView, w, h); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ + Assert(!pView || [(OverlayView *)pView glCtx] == pCtx || [(OverlayView *)pView glCtx] == nil); + cocoaViewMakeCurrentContext(pView, pCtx); +} + +VMSVGA3DCOCOA_DECL(void) vmsvga3dCocoaSwapBuffers(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx) +{ +# if 1 + Assert([(OverlayView *)pView glCtx] == pCtx); + Assert([pCtx view] == pView); + cocoaViewDisplay(pView); +# else + DEBUG_FUNC_ENTER(); + NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init]; + + Assert([(OverlayView *)pView glCtx] == pCtx); + Assert([pCtx view] == pView); + + [pCtx flushBuffer]; + + [pPool release]; + DEBUG_FUNC_LEAVE(); +# endif +} + +#endif /* IN_VMSVGA3D */ |