renderspu_cocoa_helper.m revision ae2a9b93d772d52146af2c010701ead81e4fb348
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/* $Id$ */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @file
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * VirtualBox OpenGL Cocoa Window System Helper Implementation.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/*
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Copyright (C) 2009-2014 Oracle Corporation
c58f1213e628a545081c70e26c6b67a841cff880vboxsync *
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * available from http://www.virtualbox.org. This file is free software;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * you can redistribute it and/or modify it under the terms of the GNU
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * General Public License (GPL) as published by the Free Software
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @page pg_opengl_cocoa OpenGL - Cocoa Window System Helper
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync *
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * How this works:
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * In general it is not so easy like on the other platforms, cause Cocoa
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * doesn't support any clipping of already painted stuff. In Mac OS X there is
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * the concept of translucent canvas's e.g. windows and there it is just
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * painted what should be visible to the user. Unfortunately this isn't the
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * concept of chromium. Therefor I reroute all OpenGL operation from the guest
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * to a frame buffer object (FBO). This is a OpenGL extension, which is
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * supported by all OS X versions we support (AFAIC tell). Of course the guest
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * doesn't know that and we have to make sure that the OpenGL state always is
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * in the right state to paint into the FBO and not to the front/back buffer.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Several functions below (like cocoaBindFramebufferEXT, cocoaGetIntegerv,
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * ...) doing this. When a swap or finish is triggered by the guest, the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * content (which is already bound to an texture) is painted on the screen
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * within a separate OpenGL context. This allows the usage of the same
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * resources (texture ids, buffers ...) but at the same time having an
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * different internal OpenGL state. Another advantage is that we can paint a
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * thumbnail of the current output in a much more smaller (GPU accelerated
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * scale) version on a third context and use glReadPixels to get the actual
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * data. glReadPixels is a very slow operation, but as we just use a much more
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * smaller image, we can handle it (anyway this is only done 5 times per
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * second).
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync *
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Other things to know:
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - If the guest request double buffering, we have to make sure there are two
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * buffers. We use the same FBO with 2 color attachments. Also glDrawBuffer
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * and glReadBuffer is intercepted to make sure it is painted/read to/from
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * the correct buffers. On swap our buffers are swapped and not the
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * front/back buffer.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - If the guest request a depth/stencil buffer, a combined render buffer for
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * this is created.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - If the size of the guest OpenGL window changes, all FBO's, textures, ...
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * need to be recreated.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - We need to track any changes to the parent window
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * (create/destroy/move/resize). The various classes like OverlayHelperView,
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * OverlayWindow, ... are there for.
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * - The HGCM service runs on a other thread than the Main GUI. Keeps this
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * always in mind (see e.g. performSelectorOnMainThread in renderFBOToView)
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * - We make heavy use of late binding. We can not be sure that the GUI (or any
9040f019271f91b98e1320c0a8c38a42636e3979vboxsync * other third party GUI), overwrite our NSOpenGLContext. So we always ask if
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * this is our own one, before use. Really neat concept of Objective-C/Cocoa
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * ;)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/*******************************************************************************
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync* Header Files *
99be02f9e15a3ca61b6a7c207cc7eb68dbd04817vboxsync*******************************************************************************/
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include "renderspu_cocoa_helper.h"
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#import <Cocoa/Cocoa.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#undef PVM /* sys/param.h (included via Cocoa.h) pollutes the namespace with this define. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include "chromium.h" /* For the visual bits of chromium */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include <iprt/thread.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include <iprt/string.h>
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync#include <iprt/mem.h>
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync#include <iprt/time.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include <iprt/assert.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include <VBox/VBoxOGLTest.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include <cr_vreg.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include <cr_error.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include <cr_blitter.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#ifdef VBOX_WITH_CRDUMPER_THUMBNAIL
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# include <cr_pixeldata.h>
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#endif
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#include "renderspu.h"
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/*******************************************************************************
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync* Defined Constants And Macros *
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync*******************************************************************************/
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/* Debug macros */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def FBO
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Disable this to see how the output is without the FBO in the middle of the processing chain. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#define FBO 1
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def CR_RENDER_FORCE_PRESENT_MAIN_THREAD
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Force present schedule to main thread. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def SHOW_WINDOW_BACKGROUND
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Define this to see the window background even if the window is clipped. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync/** @def DEBUG_VERBOSE
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync * Define this to get some debug info about the messages flow. */
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#if 0 || defined(DOXYGEN_RUNNING)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define CR_RENDER_FORCE_PRESENT_MAIN_THREAD
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define SHOW_WINDOW_BACKGROUND 1
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_VERBOSE
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#endif
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#ifdef DEBUG_VERBOSE
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# error "should be disabled!"
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_INFO(text) do { \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync crWarning text ; \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync AssertFailed(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_WARN(text) do { \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync crWarning text ; \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync AssertFailed(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_MSG(text) do { printf text; } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_MSG_1(text) do { printf text; } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#else
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_INFO(text) do { \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync crInfo text ; \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_WARN(text) do { \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync crWarning text ; \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_MSG(text) do {} while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_MSG_1(text) do {} while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#endif
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#define DEBUG_FUNC_ENTER() DEBUG_MSG(("==>%s\n", __PRETTY_FUNCTION__))
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#define DEBUG_FUNC_LEAVE() DEBUG_MSG(("<==%s\n", __PRETTY_FUNCTION__))
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#define DEBUG_GL_SAVE_STATE() \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync do { \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPushAttrib(GL_ALL_ATTRIB_BITS); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glMatrixMode(GL_PROJECTION); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPushMatrix(); \
7eaaa8a4480370b82ef3735994f986f338fb4df2vboxsync glMatrixMode(GL_TEXTURE); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPushMatrix(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glMatrixMode(GL_COLOR); \
b39c3fa81cadaec00ebb2e7170a8db96998b7032vboxsync glPushMatrix(); \
b39c3fa81cadaec00ebb2e7170a8db96998b7032vboxsync glMatrixMode(GL_MODELVIEW); \
b39c3fa81cadaec00ebb2e7170a8db96998b7032vboxsync glPushMatrix(); \
b39c3fa81cadaec00ebb2e7170a8db96998b7032vboxsync } while (0)
b39c3fa81cadaec00ebb2e7170a8db96998b7032vboxsync
479124979f37167f8ce75bf59090903d30e1ced3vboxsync#define DEBUG_GL_RESTORE_STATE() \
d8b66c9f4ecb096e1f2308d74df4d6a1220c3b98vboxsync do { \
d8b66c9f4ecb096e1f2308d74df4d6a1220c3b98vboxsync glMatrixMode(GL_MODELVIEW); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPopMatrix(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glMatrixMode(GL_COLOR); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPopMatrix(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glMatrixMode(GL_TEXTURE); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPopMatrix(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glMatrixMode(GL_PROJECTION); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPopMatrix(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPopClientAttrib(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync glPopAttrib(); \
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync } while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#ifdef DEBUG_poetzsch
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_CHECK_GL_ERROR() do { checkGLError(__FILE__, __LINE__); } while (0);
88e9d91abf9293ecc1c70e0bf40e42f1c3a0bdcevboxsyncstatic void checkGLError(char *pszFile, int iLine)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync{
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync GLenum uGlErr = glGetError();
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync if (uGlErr != GL_NO_ERROR)
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync {
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync const char *errStr;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync switch (uGlErr)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync {
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_INVALID_ENUM: errStr = "GL_INVALID_ENUM"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_INVALID_VALUE: errStr = "GL_INVALID_VALUE"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_INVALID_OPERATION: errStr = "GL_INVALID_OPERATION"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_STACK_OVERFLOW: errStr = "GL_STACK_OVERFLOW"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_STACK_UNDERFLOW: errStr = "GL_STACK_UNDERFLOW"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_OUT_OF_MEMORY: errStr = "GL_OUT_OF_MEMORY"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync case GL_TABLE_TOO_LARGE: errStr = "GL_TABLE_TOO_LARGE"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync default: errStr = "UNKNOWN"; break;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync }
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync DEBUG_MSG(("%s:%d: glError %d (%s)\n", pszFile, iLine, uGlErr, errStr));
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync }
b39c3fa81cadaec00ebb2e7170a8db96998b7032vboxsync}
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#else
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync# define DEBUG_CHECK_GL_ERROR() do {} while (0)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync#endif
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
00019d7a7381b029a3c17229f14c24a3a7b07a94vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsyncstatic NSOpenGLContext *vboxCtxGetCurrent(void)
00019d7a7381b029a3c17229f14c24a3a7b07a94vboxsync{
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync GET_CONTEXT(pCtxInfo);
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync if (pCtxInfo)
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync {
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync Assert(pCtxInfo->context);
6831f283dbc5c27bde8a8f8bea179b84a5741697vboxsync return pCtxInfo->context;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync }
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync return nil;
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync}
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsyncstatic bool vboxCtxSyncCurrentInfo(void)
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync{
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync GET_CONTEXT(pCtxInfo);
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync NSOpenGLContext *pCtx = [NSOpenGLContext currentContext];
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync NSView *pView = pCtx ? [pCtx view] : nil;
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync bool fAdjusted = false;
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync if (pCtxInfo)
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync {
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync WindowInfo *pWinInfo = pCtxInfo->currentWindow;
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync Assert(pWinInfo);
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync if ( pCtxInfo->context != pCtx
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync || pWinInfo->window != pView)
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync {
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync renderspu_SystemMakeCurrent(pWinInfo, 0, pCtxInfo);
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync fAdjusted = true;
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync }
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync }
c1f5ec452b23d55c71e6f07628b84ac5e97cf551vboxsync else if (pCtx)
479124979f37167f8ce75bf59090903d30e1ced3vboxsync {
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync [NSOpenGLContext clearCurrentContext];
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync fAdjusted = true;
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync }
bbf3d430bae83177ab9ce3097f49d89cc873e7c0vboxsync
return fAdjusted;
}
/**
* 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();
[pNewCtx 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)
{
[pOldCtx setView: pOldView];
}
[pOldCtx 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
{
/*
* Call on main thread (?) via renderspuCalloutClient (whatever that is).
*/
if (renderspuCalloutAvailable())
{
Assert(![NSThread isMainThread]);
renderspuCalloutClient(VBoxMainThreadTaskRunner_RcdRunCallback, self);
return true;
}
/*
* 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
@class DockOverlayView;
/**
* 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;
/** 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;
/** @name For clipping
* @{ */
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;
CR_BLITTER *m_pBlitter;
WindowInfo *m_pWinInfo;
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;
- (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;
- (void)vboxReshapePerform;
- (void)vboxReshapeOnResizePerform;
- (void)vboxReshapeOnReparentPerform;
- (void)createDockTile;
- (void)deleteDockTile;
- (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;
- (void)vboxPresentToDockTileCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor;
- (void)vboxPresentToViewCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor;
- (void)presentComposition:(const VBOXVR_SCR_COMPOSITOR_ENTRY *)pChangedEntry;
- (void)vboxBlitterSyncWindow;
- (void)clearVisibleRegions;
- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint *)paRects;
- (GLboolean)vboxNeedsEmptyPresent;
- (NSView *)dockTileScreen;
- (void)reshapeDockTile;
- (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:(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
/**
* 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
/********************************************************************************
*
* 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];
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:(OverlayWindow *)pOverlayWindow
{
DEBUG_FUNC_ENTER();
self = [super initWithFrame:NSZeroRect];
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];
m_pOverlayHelperView = [[OverlayHelperView alloc] initWithOverlayWindow:self];
/* 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
{
DEBUG_FUNC_ENTER();
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;
m_pBlitter = nil;
m_pWinInfo = pWinInfo;
m_fNeedViewportUpdate = true;
m_fNeedCtxUpdate = true;
m_fDataVisible = false;
m_fCleanupNeeded = false;
m_fEverSized = false;
self = [super initWithFrame:frame];
DEBUG_MSG(("OVIW(%p): init OverlayView\n", (void *)self));
DEBUG_FUNC_LEAVE();
return self;
}
- (void)cleanupData
{
DEBUG_FUNC_ENTER();
[self deleteDockTile];
[self setGLCtx:nil];
if (m_pSharedGLCtx)
{
if ([m_pSharedGLCtx view] == self)
[m_pSharedGLCtx clearDrawable];
[m_pSharedGLCtx release];
m_pSharedGLCtx = nil;
CrBltTerm(m_pBlitter);
RTMemFree(m_pBlitter);
m_pBlitter = nil;
}
[self clearVisibleRegions];
DEBUG_FUNC_LEAVE();
}
- (void)dealloc
{
DEBUG_FUNC_ENTER();
DEBUG_MSG(("OVIW(%p): dealloc OverlayView\n", (void *)self));
[self cleanupData];
[super dealloc];
DEBUG_FUNC_LEAVE();
}
- (void)drawRect:(NSRect)aRect
{
[self vboxTryDrawUI];
}
- (void)setGLCtx:(NSOpenGLContext *)pCtx
{
DEBUG_FUNC_ENTER();
DEBUG_MSG(("OVIW(%p): setGLCtx: new ctx: %p\n", (void *)self, (void *)pCtx));
/*
* 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)
{
[m_pGLCtx clearDrawable];
[m_pGLCtx release];
/*[m_pGLCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/
}
m_pGLCtx = pCtx;
if (pCtx)
[pCtx retain];
}
DEBUG_FUNC_LEAVE();
}
- (NSOpenGLContext *)glCtx
{
DEBUG_FUNC_ENTER();
DEBUG_FUNC_LEAVE();
return m_pGLCtx;
}
- (NSView *)parentView
{
DEBUG_FUNC_ENTER();
DEBUG_FUNC_LEAVE();
return m_pParentView;
}
- (void)setParentView:(NSView *)pView
{
DEBUG_FUNC_ENTER();
DEBUG_MSG(("OVIW(%p): setParentView: new view: %p\n", (void *)self, (void *)pView));
m_pParentView = pView;
DEBUG_FUNC_LEAVE();
}
- (void)setOverlayWin:(NSWindow *)pWin
{
DEBUG_FUNC_ENTER();
DEBUG_MSG(("OVIW(%p): setOverlayWin: new win: %p\n", (void *)self, (void *)pWin));
m_pOverlayWin = pWin;
DEBUG_FUNC_LEAVE();
}
- (NSWindow *)overlayWin
{
DEBUG_FUNC_ENTER();
DEBUG_FUNC_LEAVE();
return m_pOverlayWin;
}
- (void)vboxSetPosUI:(NSPoint)pos
{
DEBUG_FUNC_ENTER();
m_Pos = pos;
if (m_fEverSized)
[self vboxReshapePerform];
DEBUG_FUNC_LEAVE();
}
- (void)vboxSetPosUIObj:(NSValue *)pPos
{
DEBUG_FUNC_ENTER();
NSPoint pos = [pPos pointValue];
[self vboxSetPosUI:pos];
DEBUG_FUNC_LEAVE();
}
- (void)vboxSetPos:(NSPoint)pos
{
DEBUG_FUNC_ENTER();
DEBUG_MSG(("OVIW(%p): vboxSetPos: new pos: %d, %d\n", (void *)self, (int)pos.x, (int)pos.y));
VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance];
NSValue *pPos = [NSValue valueWithPoint:pos];
[pRunner addObj:self selector:@selector(vboxSetPosUIObj:) arg:pPos];
DEBUG_FUNC_LEAVE();
}
- (NSPoint)pos
{
DEBUG_FUNC_ENTER();
DEBUG_FUNC_LEAVE();
return m_Pos;
}
- (bool)isEverSized
{
DEBUG_FUNC_ENTER();
DEBUG_FUNC_LEAVE();
return m_fEverSized;
}
- (void)vboxDestroy
{
DEBUG_FUNC_ENTER();
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];
}
renderspuWinRelease(m_pWinInfo);
DEBUG_FUNC_LEAVE();
}
- (void)vboxSetSizeUIObj:(NSValue *)pSize
{
DEBUG_FUNC_ENTER();
NSSize size = [pSize sizeValue];
[self vboxSetSizeUI:size];
DEBUG_FUNC_LEAVE();
}
- (void)vboxSetSizeUI:(NSSize)size
{
DEBUG_FUNC_ENTER();
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];
DEBUG_FUNC_LEAVE();
}
- (void)vboxSetSize:(NSSize)size
{
DEBUG_FUNC_ENTER();
VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance];
NSValue *pSize = [NSValue valueWithSize:size];
[pRunner addObj:self selector:@selector(vboxSetSizeUIObj:) arg:pSize];
DEBUG_FUNC_LEAVE();
}
- (NSSize)size
{
DEBUG_FUNC_ENTER();
DEBUG_FUNC_LEAVE();
return m_Size;
}
- (void)updateViewportCS
{
DEBUG_FUNC_ENTER();
DEBUG_MSG(("OVIW(%p): updateViewport\n", (void *)self));
/* Update the viewport for our OpenGL view. */
[m_pSharedGLCtx update];
[self vboxBlitterSyncWindow];
/* Clear background to transparent. */
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
DEBUG_FUNC_LEAVE();
}
- (void)vboxReshapeOnResizePerform
{
DEBUG_FUNC_ENTER();
[self vboxReshapePerform];
[self createDockTile];
/* 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
DEBUG_FUNC_LEAVE();
}
- (void)vboxReshapeOnReparentPerform
{
DEBUG_FUNC_ENTER();
[self createDockTile];
DEBUG_FUNC_LEAVE();
}
- (void)vboxReshapePerform
{
DEBUG_FUNC_ENTER();
NSRect parentFrame = NSZeroRect;
NSPoint parentPos = NSZeroPoint;
NSPoint childPos = NSZeroPoint;
NSRect childFrame = NSZeroRect;
NSRect newFrame = NSZeroRect;
DEBUG_MSG(("OVIW(%p): vboxReshapePerform\n", (void *)self));
parentFrame = [m_pParentView frame];
DEBUG_MSG(("FIXED parentFrame [%f:%f], [%f:%f]\n", parentFrame.origin.x, parentFrame.origin.y, parentFrame.size.width, parentFrame.size.height));
parentPos = parentFrame.origin;
parentPos.y += parentFrame.size.height;
DEBUG_MSG(("FIXED(view) parentPos [%f:%f]\n", parentPos.x, parentPos.y));
parentPos = [m_pParentView convertPoint:parentPos toView:nil];
DEBUG_MSG(("FIXED parentPos(win) [%f:%f]\n", parentPos.x, parentPos.y));
parentPos = [[m_pParentView window] convertBaseToScreen:parentPos];
DEBUG_MSG(("FIXED parentPos(screen) [%f:%f]\n", parentPos.x, parentPos.y));
parentFrame.origin = parentPos;
childPos = NSMakePoint(m_Pos.x, m_Pos.y + m_Size.height);
DEBUG_MSG(("FIXED(view) childPos [%f:%f]\n", childPos.x, childPos.y));
childPos = [m_pParentView convertPoint:childPos toView:nil];
DEBUG_MSG(("FIXED(win) childPos [%f:%f]\n", childPos.x, childPos.y));
childPos = [[m_pParentView window] convertBaseToScreen:childPos];
DEBUG_MSG(("FIXED childPos(screen) [%f:%f]\n", childPos.x, childPos.y));
childFrame = NSMakeRect(childPos.x, childPos.y, m_Size.width, m_Size.height);
DEBUG_MSG(("FIXED childFrame [%f:%f], [%f:%f]\n", childFrame.origin.x, childFrame.origin.y, childFrame.size.width, childFrame.size.height));
/* We have to make sure that the overlay window will not be displayed out
* of the parent window. So intersect both frames & use the result as the new
* frame for the window. */
newFrame = NSIntersectionRect(parentFrame, childFrame);
DEBUG_MSG(("[%#p]: parentFrame pos[%f : %f] size[%f : %f]\n",
(void *)self, parentFrame.origin.x, parentFrame.origin.y, parentFrame.size.width, parentFrame.size.height));
DEBUG_MSG(("[%#p]: childFrame pos[%f : %f] size[%f : %f]\n",
(void *)self, childFrame.origin.x, childFrame.origin.y, childFrame.size.width, childFrame.size.height));
DEBUG_MSG(("[%#p]: newFrame pos[%f : %f] size[%f : %f]\n",
(void *)self, newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height));
/* 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 = newFrame.origin.x - childFrame.origin.x;
m_RootRect.origin.y = childFrame.size.height + childFrame.origin.y - (newFrame.size.height + newFrame.origin.y);
m_RootRect.size = newFrame.size;
m_yInvRootOffset = newFrame.origin.y - childFrame.origin.y;
DEBUG_MSG(("[%#p]: m_RootRect pos[%f : %f] size[%f : %f]\n",
(void *)self, m_RootRect.origin.x, m_RootRect.origin.y, m_RootRect.size.width, m_RootRect.size.height));
/*
NSScrollView *pScrollView = [[[m_pParentView window] contentView] enclosingScrollView];
if (pScrollView)
{
NSRect scrollRect = [pScrollView documentVisibleRect];
NSRect scrollRect = [m_pParentView visibleRect];
printf ("sc rect: %d %d %d %d\n", (int) scrollRect.origin.x,(int) scrollRect.origin.y,(int) scrollRect.size.width,(int) scrollRect.size.height);
NSRect b = [[m_pParentView superview] bounds];
printf ("bound rect: %d %d %d %d\n", (int) b.origin.x,(int) b.origin.y,(int) b.size.width,(int) b.size.height);
newFrame.origin.x += scrollRect.origin.x;
newFrame.origin.y += scrollRect.origin.y;
}
*/
/* Set the new frame. */
[[self window] setFrame:newFrame display:YES];
/* Inform the dock tile view as well. */
[self reshapeDockTile];
/* 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);
}
DEBUG_FUNC_LEAVE();
}
- (void)createDockTile
{
DEBUG_FUNC_ENTER();
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];
}
DEBUG_FUNC_LEAVE();
}
- (void)deleteDockTile
{
DEBUG_FUNC_ENTER();
if (m_DockTileView != nil)
{
[m_DockTileView removeFromSuperview];
[m_DockTileView release];
m_DockTileView = nil;
}
DEBUG_FUNC_LEAVE();
}
- (void)makeCurrentFBO
{
DEBUG_MSG(("OVIW(%p): makeCurrentFBO\n", (void *)self));
if (m_pGLCtx)
{
if ([m_pGLCtx view] != self)
{
/* We change the active view, so flush first */
if ([NSOpenGLContext currentContext] != 0)
glFlush();
[m_pGLCtx setView: self];
DEBUG_CHECK_GL_ERROR();
}
/*
if ([NSOpenGLContext currentContext] != m_pGLCtx)
*/
{
[m_pGLCtx makeCurrentContext];
DEBUG_CHECK_GL_ERROR();
if (m_fNeedCtxUpdate == true)
{
[m_pGLCtx update];
m_fNeedCtxUpdate = false;
}
}
if (!m_FBOId)
{
glGenFramebuffersEXT(1, &m_FBOId);
Assert(m_FBOId);
}
}
}
- (bool)vboxSharedCtxCreate
{
DEBUG_FUNC_ENTER();
GLint opaque = 0;
NSOpenGLContext *pSharedGLCtx;
NSOpenGLPixelFormat *pPixelFormat;
if (m_pSharedGLCtx)
{
DEBUG_FUNC_LEAVE();
return true;
}
Assert(!m_pBlitter);
m_pBlitter = RTMemAlloc(sizeof (*m_pBlitter));
if (RT_UNLIKELY(!m_pBlitter))
{
DEBUG_WARN(("m_pBlitter allocation failed"));
DEBUG_FUNC_LEAVE();
return false;
}
int rc = CrBltInit(m_pBlitter, NULL, false, false, &render_spu.GlobalShaders, &render_spu.blitterDispatch);
if (RT_FAILURE(rc))
{
DEBUG_WARN(("CrBltInit failed, rc %d", rc));
RTMemFree(m_pBlitter);
m_pBlitter = NULL;
DEBUG_FUNC_LEAVE();
return false;
}
DEBUG_MSG(("blitter created successfully for view 0x%p\n", (void *)self));
/* Create a shared context out of the main context. Use the same pixel format. */
pPixelFormat = [(OverlayOpenGLContext *)m_pGLCtx openGLPixelFormat];
pSharedGLCtx = [[NSOpenGLContext alloc] initWithFormat:pPixelFormat shareContext:m_pGLCtx];
/* Set the new context as non opaque */
[pSharedGLCtx setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
/* Set this view as the drawable for the new context */
[pSharedGLCtx setView:self];
m_fNeedViewportUpdate = true;
m_pSharedGLCtx = pSharedGLCtx;
DEBUG_FUNC_LEAVE();
return true;
}
- (void)vboxTryDraw
{
glFlush();
/* Issue to the gui thread. */
DEBUG_MSG(("My[%p]: Draw\n", self));
[self performSelectorOnMainThread:@selector(vboxTryDrawUI) withObject:nil waitUntilDone:NO];
}
- (void)vboxSetVisible:(GLboolean)fVisible
{
DEBUG_FUNC_ENTER();
VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance];
NSNumber* pVisObj = [NSNumber numberWithBool:fVisible];
[pRunner addObj:self selector:@selector(vboxSetVisibleUIObj:) arg:pVisObj];
DEBUG_FUNC_LEAVE();
}
- (void)vboxSetVisibleUI:(GLboolean)fVisible
{
DEBUG_FUNC_ENTER();
[self setHidden: !fVisible];
DEBUG_FUNC_LEAVE();
}
- (void)vboxSetVisibleUIObj:(NSNumber*)pVisible
{
DEBUG_FUNC_ENTER();
BOOL fVisible = [pVisible boolValue];
[self vboxSetVisibleUI:fVisible];
DEBUG_FUNC_LEAVE();
}
- (void)vboxReparent:(NSView *)pParentView
{
DEBUG_FUNC_ENTER();
VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance];
[pRunner addObj:self selector:@selector(vboxReparentUI:) arg:pParentView];
DEBUG_FUNC_LEAVE();
}
- (void)vboxReparentUI:(NSView *)pParentView
{
DEBUG_FUNC_ENTER();
/* 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];
}
DEBUG_FUNC_LEAVE();
}
- (void)vboxTryDrawUI
{
const VBOXVR_SCR_COMPOSITOR *pCompositor;
DEBUG_MSG(("My[%p]: DrawUI\n", self));
if ([self isHidden])
{
DEBUG_INFO(("request to draw on a hidden view"));
return;
}
if ([[self overlayWin] parentWindow] == nil)
{
DEBUG_INFO(("request to draw a view w/o a parent"));
return;
}
int rc = renderspuVBoxCompositorLock(m_pWinInfo, &pCompositor);
if (RT_FAILURE(rc))
{
DEBUG_WARN(("renderspuVBoxCompositorLock failed\n"));
return;
}
if (!pCompositor && !m_fCleanupNeeded)
{
DEBUG_MSG(("My[%p]: noCompositorUI\n", self));
renderspuVBoxCompositorUnlock(m_pWinInfo);
return;
}
VBOXVR_SCR_COMPOSITOR TmpCompositor;
if (pCompositor)
{
if (!m_pSharedGLCtx)
{
Assert(!m_fDataVisible);
Assert(!m_fCleanupNeeded);
renderspuVBoxCompositorRelease(m_pWinInfo);
if (![self vboxSharedCtxCreate])
{
DEBUG_WARN(("vboxSharedCtxCreate failed\n"));
return;
}
Assert(m_pSharedGLCtx);
pCompositor = renderspuVBoxCompositorAcquire(m_pWinInfo);
Assert(!m_fDataVisible);
Assert(!m_fCleanupNeeded);
if (!pCompositor)
return;
}
}
else
{
DEBUG_MSG(("My[%p]: NeedCleanup\n", self));
Assert(m_fCleanupNeeded);
CrVrScrCompositorInit(&TmpCompositor, NULL);
pCompositor = &TmpCompositor;
}
if ([self lockFocusIfCanDraw])
{
[self vboxPresent:pCompositor];
[self unlockFocus];
}
else if (!m_pWinInfo->visible)
{
DEBUG_MSG(("My[%p]: NotVisible\n", self));
m_fCleanupNeeded = false;
}
else
{
DEBUG_MSG(("My[%p]: Reschedule\n", self));
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(vboxTryDrawUI) userInfo:nil repeats:NO];
}
renderspuVBoxCompositorUnlock(m_pWinInfo);
}
- (void)swapFBO
{
DEBUG_FUNC_ENTER();
[m_pGLCtx flushBuffer];
DEBUG_FUNC_LEAVE();
}
- (void)vboxPresent:(const VBOXVR_SCR_COMPOSITOR*)pCompositor
{
VBOX_CR_RENDER_CTX_INFO CtxInfo;
DEBUG_MSG(("OVIW(%p): renderFBOToView\n", (void *)self));
Assert(pCompositor);
vboxCtxEnter(m_pSharedGLCtx, &CtxInfo);
[self vboxPresentCS:pCompositor];
vboxCtxLeave(&CtxInfo);
}
- (void)vboxPresentCS:(const VBOXVR_SCR_COMPOSITOR *)pCompositor
{
if ([m_pSharedGLCtx view] != self)
{
DEBUG_MSG(("OVIW(%p): not current view of shared ctx! Switching ...\n", (void *)self));
[m_pSharedGLCtx setView: self];
m_fNeedViewportUpdate = true;
}
if (m_fNeedViewportUpdate)
{
[self updateViewportCS];
m_fNeedViewportUpdate = false;
}
m_fCleanupNeeded = false;
/* Render FBO content to the dock tile when necessary. */
[self vboxPresentToDockTileCS:pCompositor];
/* change to #if 0 to see thumbnail image */
#if 1
[self vboxPresentToViewCS:pCompositor];
#else
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
[m_pSharedGLCtx flushBuffer];
#endif
}
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:(const VBOXVR_SCR_COMPOSITOR*)pCompositor
{
NSRect r = [self frame];
float xStretch;
float yStretch;
DEBUG_MSG(("OVIW(%p): rF2V frame: [%i, %i, %i, %i]\n", (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 */
VBOXVR_SCR_COMPOSITOR_CONST_ITERATOR CIter;
const VBOXVR_SCR_COMPOSITOR_ENTRY *pEntry;
CrVrScrCompositorConstIterInit(pCompositor, &CIter);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
glDrawBuffer(GL_BACK);
/* Clear background to transparent */
glClear(GL_COLOR_BUFFER_BIT);
m_fDataVisible = false;
CrVrScrCompositorGetStretching(pCompositor, &xStretch, &yStretch);
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, 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 | 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
/*
glFinish();
*/
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
[m_pSharedGLCtx flushBuffer];
}
- (void)presentComposition:(const VBOXVR_SCR_COMPOSITOR_ENTRY *)pChangedEntry
{
[self vboxTryDraw];
}
- (void)vboxBlitterSyncWindow
{
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);
}
#ifdef VBOX_WITH_CRDUMPER_THUMBNAIL
static int g_cVBoxTgaCtr = 0;
#endif
- (void)vboxPresentToDockTileCS:(const VBOXVR_SCR_COMPOSITOR*)pCompositor
{
NSRect r = [self frame];
NSRect rr = NSZeroRect;
GLint i = 0;
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))
{
int 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];
}
}
}
- (void)clearVisibleRegions
{
DEBUG_FUNC_ENTER();
if (m_paClipRects)
{
RTMemFree(m_paClipRects);
m_paClipRects = NULL;
}
m_cClipRects = 0;
DEBUG_FUNC_LEAVE();
}
- (GLboolean)vboxNeedsEmptyPresent
{
if (m_fDataVisible)
{
m_fCleanupNeeded = true;
return GL_TRUE;
}
return GL_FALSE;
}
- (void)setVisibleRegions:(GLint)cRects paRects:(const GLint*)paRects
{
DEBUG_FUNC_ENTER();
GLint cOldRects = m_cClipRects;
DEBUG_MSG_1(("OVIW(%p): setVisibleRegions: cRects=%d\n", (void *)self, cRects));
[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*)RTMemAlloc(sizeof(GLint) * 4 * cRects);
m_cClipRects = cRects;
memcpy(m_paClipRects, paRects, sizeof(GLint) * 4 * cRects);
}
DEBUG_FUNC_LEAVE();
}
- (NSView *)dockTileScreen
{
DEBUG_FUNC_ENTER();
NSView *contentView = [[[NSApplication sharedApplication] dockTile] contentView];
NSView *screenContent = nil;
/*
* First try the new variant which checks if this window is within the
* screen which is previewed in the dock.
*/
if ([contentView respondsToSelector:@selector(screenContentWithParentView:)])
screenContent = [contentView performSelector:@selector(screenContentWithParentView:) withObject:(id)m_pParentView];
/*
* If it fails, fall back to the old variant (VBox...).
*/
else if ([contentView respondsToSelector:@selector(screenContent)])
screenContent = [contentView performSelector:@selector(screenContent)];
DEBUG_FUNC_LEAVE();
return screenContent;
}
- (void)reshapeDockTile
{
DEBUG_FUNC_ENTER();
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];
}
DEBUG_FUNC_LEAVE();
}
@end /* @implementation OverlayView */
/********************************************************************************
*
* OpenGL context management
*
********************************************************************************/
void cocoaGLCtxCreate(NativeNSOpenGLContextRef *ppCtx, GLbitfield fVisParams, NativeNSOpenGLContextRef pSharedCtx)
{
DEBUG_FUNC_ENTER();
NSOpenGLPixelFormat *pFmt = nil;
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
NSOpenGLPixelFormatAttribute attribs[24] =
{
NSOpenGLPFAAccelerated,
NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)24
};
int i = 3;
if (fVisParams & CR_ALPHA_BIT)
{
DEBUG_MSG(("CR_ALPHA_BIT requested\n"));
attribs[i++] = NSOpenGLPFAAlphaSize;
attribs[i++] = 8;
}
if (fVisParams & CR_DEPTH_BIT)
{
DEBUG_MSG(("CR_DEPTH_BIT requested\n"));
attribs[i++] = NSOpenGLPFADepthSize;
attribs[i++] = 24;
}
if (fVisParams & CR_STENCIL_BIT)
{
DEBUG_MSG(("CR_STENCIL_BIT requested\n"));
attribs[i++] = NSOpenGLPFAStencilSize;
attribs[i++] = 8;
}
if (fVisParams & CR_ACCUM_BIT)
{
DEBUG_MSG(("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)
{
DEBUG_MSG(("CR_MULTISAMPLE_BIT requested\n"));
attribs[i++] = NSOpenGLPFASampleBuffers;
attribs[i++] = 1;
attribs[i++] = NSOpenGLPFASamples;
attribs[i++] = 4;
}
if (fVisParams & CR_DOUBLE_BIT)
{
DEBUG_MSG(("CR_DOUBLE_BIT requested\n"));
attribs[i++] = NSOpenGLPFADoubleBuffer;
}
if (fVisParams & CR_STEREO_BIT)
{
/* We don't support that.
DEBUG_MSG(("CR_STEREO_BIT requested\n"));
attribs[i++] = NSOpenGLPFAStereo;
*/
}
if (VBoxOglIsOfflineRenderingAppropriate())
{
DEBUG_MSG(("Offline rendering is enabled\n"));
attribs[i++] = NSOpenGLPFAAllowOfflineRenderers;
}
/* Mark the end */
attribs[i++] = 0;
/* Choose a pixel format */
pFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
if (pFmt)
{
*ppCtx = [[OverlayOpenGLContext alloc] initWithFormat:pFmt shareContext:pSharedCtx];
/* 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");
*/
DEBUG_MSG(("New context %X\n", (uint)*ppCtx));
}
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaGLCtxDestroy(NativeNSOpenGLContextRef pCtx)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
[pCtx release];
/*[pCtx performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];*/
[pPool release];
DEBUG_FUNC_LEAVE();
}
/********************************************************************************
*
* View management
*
********************************************************************************/
typedef struct CR_RCD_CREATEVIEW
{
WindowInfo *pWinInfo;
NSView *pParentView;
GLbitfield fVisParams;
/* out */
OverlayView *pView;
} CR_RCD_CREATEVIEW;
static OverlayView * vboxViewCreate(WindowInfo *pWinInfo, NativeNSViewRef pParentView)
{
DEBUG_FUNC_ENTER();
/* Create our worker view */
OverlayView* pView = [[OverlayView alloc] initWithFrame:NSZeroRect thread:RTThreadSelf() parentView:pParentView winInfo:pWinInfo];
if (pView)
{
/* We need a real window as container for the view */
[[OverlayWindow alloc] initWithParentView:pParentView overlayView:pView];
/* Return the freshly created overlay view */
DEBUG_FUNC_LEAVE();
return pView;
}
DEBUG_FUNC_LEAVE();
return NULL;
}
static DECLCALLBACK(void) vboxRcdCreateView(void *pvCb)
{
DEBUG_FUNC_ENTER();
CR_RCD_CREATEVIEW * pCreateView = (CR_RCD_CREATEVIEW*)pvCb;
pCreateView->pView = vboxViewCreate(pCreateView->pWinInfo, pCreateView->pParentView);
DEBUG_FUNC_LEAVE();
}
void cocoaViewCreate(NativeNSViewRef *ppView, WindowInfo *pWinInfo, NativeNSViewRef pParentView, GLbitfield fVisParams)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance];
/* make sure all tasks are run, to preserve the order */
[pRunner runTasksSyncIfPossible];
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
{
DEBUG_MSG(("no callout available on createWindow\n"));
#if 0
dispatch_sync(dispatch_get_main_queue(), ^{
#endif
*ppView = vboxViewCreate(pWinInfo, pParentView);
#if 0
});
#endif
}
if (!*ppView)
renderspuWinRelease(pWinInfo);
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaViewReparent(NativeNSViewRef pView, NativeNSViewRef pParentView)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
OverlayView* pOView = (OverlayView*)pView;
if (pOView)
{
[pOView vboxReparent:pParentView];
}
[pPool release];
DEBUG_FUNC_LEAVE();
}
typedef struct CR_RCD_DESTROYVIEW
{
OverlayView *pView;
} CR_RCD_DESTROYVIEW;
static DECLCALLBACK(void) vboxRcdDestroyView(void *pvCb)
{
DEBUG_FUNC_ENTER();
CR_RCD_DESTROYVIEW * pDestroyView = (CR_RCD_DESTROYVIEW*)pvCb;
[pDestroyView->pView vboxDestroy];
DEBUG_FUNC_LEAVE();
}
void cocoaViewDestroy(NativeNSViewRef pView)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance];
[pRunner addObj:pView selector:@selector(vboxDestroy) arg:nil];
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaViewShow(NativeNSViewRef pView, GLboolean fShowIt)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
[(OverlayView*)pView vboxSetVisible:fShowIt];
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaViewDisplay(NativeNSViewRef pView)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
DEBUG_WARN(("cocoaViewDisplay should never happen!\n"));
DEBUG_MSG_1(("cocoaViewDisplay %p\n", (void *)pView));
[(OverlayView*)pView swapFBO];
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaViewSetPosition(NativeNSViewRef pView, NativeNSViewRef pParentView, int x, int y)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
[(OverlayView*)pView vboxSetPos:NSMakePoint(x, y)];
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaViewSetSize(NativeNSViewRef pView, int w, int h)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
[(OverlayView*)pView vboxSetSize:NSMakeSize(w, h)];
[pPool release];
DEBUG_FUNC_LEAVE();
}
typedef struct CR_RCD_GETGEOMETRY
{
OverlayView *pView;
NSRect rect;
} CR_RCD_GETGEOMETRY;
static DECLCALLBACK(void) vboxRcdGetGeomerty(void *pvUser)
{
DEBUG_FUNC_ENTER();
CR_RCD_GETGEOMETRY * pGetGeometry = (CR_RCD_GETGEOMETRY *)pvUser;
pGetGeometry->rect = [[pGetGeometry->pView window] frame];
DEBUG_FUNC_LEAVE();
}
void cocoaViewGetGeometry(NativeNSViewRef pView, int *pX, int *pY, int *pW, int *pH)
{
NSAutoreleasePool *pPool;
DEBUG_FUNC_ENTER();
pPool = [[NSAutoreleasePool alloc] init];
NSRect frame;
VBoxMainThreadTaskRunner *pRunner = [VBoxMainThreadTaskRunner globalInstance];
/* make sure all tasks are run, to preserve the order */
[pRunner runTasksSyncIfPossible];
if (renderspuCalloutAvailable())
{
CR_RCD_GETGEOMETRY GetGeometry;
GetGeometry.pView = (OverlayView *)pView;
renderspuCalloutClient(vboxRcdGetGeomerty, &GetGeometry);
frame = GetGeometry.rect;
}
else
{
DEBUG_MSG(("no callout available on getGeometry\n"));
frame = [[pView window] frame];
}
*pX = frame.origin.x;
*pY = frame.origin.y;
*pW = frame.size.width;
*pH = frame.size.height;
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaViewPresentComposition(NativeNSViewRef pView, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
NSOpenGLContext *pCtx;
/* view should not necesserily have a 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];
}
[(OverlayView*)pView presentComposition:pChangedEntry];
[pPool release];
DEBUG_FUNC_LEAVE();
}
void cocoaViewMakeCurrentContext(NativeNSViewRef pView, NativeNSOpenGLContextRef pCtx)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
DEBUG_MSG(("cocoaViewMakeCurrentContext(%p, %p)\n", (void *)pView, (void *)pCtx));
if (pView)
{
[(OverlayView*)pView setGLCtx:pCtx];
[(OverlayView*)pView makeCurrentFBO];
}
else
{
[NSOpenGLContext clearCurrentContext];
}
[pPool release];
DEBUG_FUNC_LEAVE();
}
GLboolean cocoaViewNeedsEmptyPresent(NativeNSViewRef pView)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
GLboolean fNeedsPresent = [(OverlayView*)pView vboxNeedsEmptyPresent];
[pPool release];
DEBUG_FUNC_LEAVE();
return fNeedsPresent;
}
void cocoaViewSetVisibleRegion(NativeNSViewRef pView, GLint cRects, const GLint* paRects)
{
DEBUG_FUNC_ENTER();
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
[(OverlayView*)pView setVisibleRegions:cRects paRects:paRects];
[pPool release];
DEBUG_FUNC_LEAVE();
}